Partager via


Procédure pas à pas pour créer un composant C++/CX/Windows Runtime et l’appeler à partir de JavaScript ou C#

Remarque

Cette rubrique existe pour vous aider à gérer votre application C++/CX. Mais nous recommandons que vous utilisiez C++/WinRT pour de nouvelles applications. C++/WinRT est une projection de langage C++17 moderne entièrement standard pour les API Windows Runtime (WinRT), implémentée en tant que bibliothèque basée sur un fichier d’en-tête et conçue pour vous fournir un accès de première classe à l’API Windows moderne. Pour savoir comment créer un composant Windows Runtime avec C++/WinRT, consultez Composants Windows Runtime avec C++/WinRT.

Cette procédure pas à pas indique comment créer une DLL de composant Windows Runtime de base qui peut être appelée à partir de JavaScript, C# ou Visual Basic. Avant d’entreprendre cette procédure pas à pas, vous devez maîtriser des concepts tels que l’interface binaire abstraite (ABI), les classes ref et les extensions des composants Visual C++ qui facilitent l’utilisation des classes ref. Pour plus d’informations, consultez les composants Windows Runtime avec C++/CX et Visual C++ Language Reference (C++/CX).

Création de la DLL du composant C++

Dans cet exemple, nous créons d’abord le projet de composant, mais vous pouvez d’abord créer le projet JavaScript. L’ordre n’a pas d’importance.

Notez que la classe principale du composant contient des exemples de définitions de propriété et de méthode et une déclaration d’événement. Celles-ci sont fournies simplement pour vous montrer comment elle est effectuée. Ils ne sont pas obligatoires, et dans cet exemple, nous allons remplacer tout le code généré par notre propre code.

Pour créer le projet de composant C++

  1. Dans la barre de menus de Visual Studio, choisissez Fichier, Nouveau, Projet.

  2. Dans la boîte de dialogue Nouveau projet , dans le volet gauche, développez Visual C++ , puis sélectionnez le nœud pour les applications Windows universelles.

  3. Dans le volet central, sélectionnez Composant Windows Runtime, puis nommez le projet WinRT_CPP.

  4. Cliquez sur le bouton OK.

Pour ajouter une classe activable au composant

Une classe activable est une classe que le code client peut créer à l’aide d’une nouvelle expression (New in Visual Basic ou ref new in C++). Dans votre composant, vous le déclarez en tant que classe ref publique scellée. En fait, les fichiers Class1.h et .cpp ont déjà une classe ref. Vous pouvez modifier le nom, mais dans cet exemple, nous allons utiliser le nom par défaut : Class1. Vous pouvez définir des classes ref supplémentaires ou des classes régulières dans votre composant si elles sont requises. Pour plus d’informations sur les classes ref, consultez Type System (C++/CX).

Ajoutez ces directives #include à Class1.h :

#include <collection.h>
#include <ppl.h>
#include <amp.h>
#include <amp_math.h>

collection.h est le fichier d’en-tête pour les classes concrètes C++ telles que la classe Platform ::Collections ::Vector et la classe Platform ::Collections ::Map, qui implémentent des interfaces neutres en langage définies par Windows Runtime. Les en-têtes d’ampli sont utilisés pour exécuter des calculs sur le GPU. Ils n’ont pas d’équivalents Windows Runtime, et c’est correct parce qu’ils sont privés. En général, pour des raisons de performances, vous devez utiliser du code ISO C++ et des bibliothèques standard en interne dans le composant ; il s’agit simplement de l’interface Windows Runtime qui doit être exprimée dans les types Windows Runtime.

Pour ajouter un délégué à l’étendue de l’espace de noms

Un délégué est une construction qui définit les paramètres et le type de retour pour les méthodes. Un événement est une instance d’un type délégué particulier, et toute méthode de gestionnaire d’événements qui s’abonne à l’événement doit avoir la signature spécifiée dans le délégué. Le code suivant définit un type délégué qui accepte un int et retourne void. Ensuite, le code déclare un événement public de ce type ; Cela permet au code client de fournir des méthodes appelées lorsque l’événement est déclenché.

Ajoutez la déclaration de délégué suivante à l’étendue de l’espace de noms dans Class1.h, juste avant la déclaration Class1.

public delegate void PrimeFoundHandler(int result);

Si le code ne s’aligne pas correctement lorsque vous le collez dans Visual Studio, appuyez simplement sur Ctrl+K+D pour corriger la mise en retrait pour l’intégralité du fichier.

Pour ajouter les membres publics

La classe expose trois méthodes publiques et un événement public. La première méthode est synchrone, car elle s’exécute toujours très rapidement. Étant donné que les deux autres méthodes peuvent prendre un certain temps, elles sont asynchrones afin qu’elles ne bloquent pas le thread d’interface utilisateur. Ces méthodes retournent IAsyncOperationWithProgress et IAsyncActionWithProgress. L’ancien définit une méthode asynchrone qui retourne un résultat, et celle-ci définit une méthode asynchrone qui retourne void. Ces interfaces permettent également au code client de recevoir des mises à jour sur la progression de l’opération.

public:

        // Synchronous method.
        Windows::Foundation::Collections::IVector<double>^  ComputeResult(double input);

        // Asynchronous methods
        Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^
            GetPrimesOrdered(int first, int last);
        Windows::Foundation::IAsyncActionWithProgress<double>^ GetPrimesUnordered(int first, int last);

        // Event whose type is a delegate "class"
        event PrimeFoundHandler^ primeFoundEvent;

Pour ajouter les membres privés

La classe contient trois membres privés : deux méthodes d’assistance pour les calculs numériques et un objet CoreDispatcher utilisé pour marshaler les appels d’événements des threads de travail vers le thread d’interface utilisateur.

private:
        bool is_prime(int n);
        Windows::UI::Core::CoreDispatcher^ m_dispatcher;

Pour ajouter les directives d’en-tête et d’espace de noms

  1. Dans Class1.cpp, ajoutez ces directives #include :
#include <ppltasks.h>
#include <concurrent_vector.h>
  1. Ajoutez maintenant ces instructions using pour extraire les espaces de noms requis :
using namespace concurrency;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;

Pour ajouter l’implémentation pour ComputeResult

Dans Class1.cpp, ajoutez l’implémentation de méthode suivante. Cette méthode s’exécute de manière synchrone sur le thread appelant, mais elle est très rapide, car elle utilise C++ AMP pour paralléliser le calcul sur le GPU. Pour plus d’informations, consultez Vue d’ensemble de L’AMP C++. Les résultats sont ajoutés à un type concret Platform ::Collections ::Vector<T> , qui est implicitement converti en windows ::Foundation ::Collections ::Collections ::IVector<T> lorsqu’il est retourné.

//Public API
IVector<double>^ Class1::ComputeResult(double input)
{
    // Implement your function in ISO C++ or
    // call into your C++ lib or DLL here. This example uses AMP.
    float numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 10000.0 };
    array_view<float, 1> logs(6, numbers);

    // See http://msdn.microsoft.com/library/hh305254.aspx
    parallel_for_each(
        logs.extent,
        [=] (index<1> idx) restrict(amp)
    {
        logs[idx] = concurrency::fast_math::log10(logs[idx]);
    }
    );

    // Return a Windows Runtime-compatible type across the ABI
    auto res = ref new Vector<double>();
    int len = safe_cast<int>(logs.extent.size());
    for(int i = 0; i < len; i++)
    {      
        res->Append(logs[i]);
    }

    // res is implicitly cast to IVector<double>
    return res;
}

Pour ajouter l’implémentation pour GetPrimesOrdered et sa méthode d’assistance

Dans Class1.cpp, ajoutez les implémentations pour GetPrimesOrdered et la méthode d’assistance is_prime. GetPrimesOrdered utilise une classe concurrent_vector et une boucle de fonction parallel_for pour diviser le travail et utiliser les ressources maximales de l’ordinateur sur lequel le programme s’exécute pour produire des résultats. Une fois les résultats calculés, stockés et triés, ils sont ajoutés à une plateforme ::Collections ::Vector<T> et retournés en tant que Windows ::Foundation ::Collections ::IVector<T> au code client.

Notez le code du reporter de progression, qui permet au client de raccorder une barre de progression ou une autre interface utilisateur pour montrer à l’utilisateur combien de temps l’opération va prendre. Les rapports de progression ont un coût. Un événement doit être déclenché côté composant et géré sur le thread d’interface utilisateur, et la valeur de progression doit être stockée sur chaque itération. Une façon de réduire le coût consiste à limiter la fréquence à laquelle un événement de progression est déclenché. Si le coût est toujours prohibitif ou si vous ne pouvez pas estimer la longueur de l’opération, envisagez d’utiliser un anneau de progression, ce qui montre qu’une opération est en cours, mais ne montre pas le temps restant jusqu’à l’achèvement.

// Determines whether the input value is prime.
bool Class1::is_prime(int n)
{
    if (n < 2)
        return false;
    for (int i = 2; i < n; ++i)
    {
        if ((n % i) == 0)
            return false;
    }
    return true;
}

// This method computes all primes, orders them, then returns the ordered results.
IAsyncOperationWithProgress<IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last)
{
    return create_async([this, first, last]
    (progress_reporter<double> reporter) -> IVector<int>^ {
        // Ensure that the input values are in range.
        if (first < 0 || last < 0) {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel.
        concurrent_vector<int> primes;
        long operation = 0;
        long range = last - first + 1;
        double lastPercent = 0.0;

        parallel_for(first, last + 1, [this, &primes, &operation,
            range, &lastPercent, reporter](int n) {

                // Increment and store the number of times the parallel
                // loop has been called on all threads combined. There
                // is a performance cost to maintaining a count, and
                // passing the delegate back to the UI thread, but it's
                // necessary if we want to display a determinate progress
                // bar that goes from 0 to 100%. We can avoid the cost by
                // setting the ProgressBar IsDeterminate property to false
                // or by using a ProgressRing.
                if(InterlockedIncrement(&operation) % 100 == 0)
                {
                    reporter.report(100.0 * operation / range);
                }

                // If the value is prime, add it to the local vector.
                if (is_prime(n)) {
                    primes.push_back(n);
                }
        });

        // Sort the results.
        std::sort(begin(primes), end(primes), std::less<int>());		
        reporter.report(100.0);

        // Copy the results to a Vector object, which is
        // implicitly converted to the IVector return type. IVector
        // makes collections of data available to other
        // Windows Runtime components.
        return ref new Vector<int>(primes.begin(), primes.end());
    });
}

Pour ajouter l’implémentation pour GetPrimesUnordered

La dernière étape de création du composant C++ consiste à ajouter l’implémentation de GetPrimesUnordered dans Class1.cpp. Cette méthode retourne chaque résultat tel qu’il est trouvé, sans attendre que tous les résultats soient trouvés. Chaque résultat est retourné dans le gestionnaire d’événements et affiché sur l’interface utilisateur en temps réel. Là encore, notez qu’un journaliste de progression est utilisé. Cette méthode utilise également la méthode d’assistance is_prime.

// This method returns no value. Instead, it fires an event each time a
// prime is found, and passes the prime through the event.
// It also passes progress info.
IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last)
{

    auto window = Windows::UI::Core::CoreWindow::GetForCurrentThread();
    m_dispatcher = window->Dispatcher;


    return create_async([this, first, last](progress_reporter<double> reporter) {

        // Ensure that the input values are in range.
        if (first < 0 || last < 0) {
            throw ref new InvalidArgumentException();
        }

        // In this particular example, we don't actually use this to store
        // results since we pass results one at a time directly back to
        // UI as they are found. However, we have to provide this variable
        // as a parameter to parallel_for.
        concurrent_vector<int> primes;
        long operation = 0;
        long range = last - first + 1;
        double lastPercent = 0.0;

        // Perform the computation in parallel.
        parallel_for(first, last + 1,
            [this, &primes, &operation, range, &lastPercent, reporter](int n)
        {
            // Store the number of times the parallel loop has been called  
            // on all threads combined. See comment in previous method.
            if(InterlockedIncrement(&operation) % 100 == 0)
            {
                reporter.report(100.0 * operation / range);
            }

            // If the value is prime, pass it immediately to the UI thread.
            if (is_prime(n))
            {                
                // Since this code is probably running on a worker
                // thread, and we are passing the data back to the
                // UI thread, we have to use a CoreDispatcher object.
                m_dispatcher->RunAsync( CoreDispatcherPriority::Normal,
                    ref new DispatchedHandler([this, n, operation, range]()
                {
                    this->primeFoundEvent(n);

                }, Platform::CallbackContext::Any));

            }
        });
        reporter.report(100.0);
    });
}

Création d’une application cliente JavaScript (Visual Studio 2017)

Si vous souhaitez créer un client C#, vous pouvez ignorer cette section.

Remarque

les projets plateforme Windows universelle (UWP) utilisant JavaScript ne sont pas pris en charge dans Visual Studio 2019. Consultez JavaScript et TypeScript dans Visual Studio 2019. Pour suivre cette section, nous vous recommandons d’utiliser Visual Studio 2017. Consultez JavaScript dans Visual Studio 2017.

Pour créer un projet JavaScript

  1. Dans Explorateur de solutions (dans Visual Studio 2017 ; voir Remarque ci-dessus), ouvrez le menu contextuel du nœud Solution et choisissez Ajouter, Nouveau projet.

  2. Développez JavaScript (il peut être imbriqué sous d’autres langues) et choisissez Application vide (Windows universel).

  3. Acceptez le nom par défaut (App1) en choisissant le bouton OK .

  4. Ouvrez le menu contextuel du nœud du projet App1 et choisissez Définir comme projet de démarrage.

  5. Ajoutez une référence de projet à WinRT_CPP :

  6. Ouvrez le menu contextuel du nœud Références et choisissez Ajouter une référence.

  7. Dans le volet gauche de la boîte de dialogue Gestionnaire de références, sélectionnez Projets , puis sélectionnez Solution.

  8. Dans le volet central, sélectionnez WinRT_CPP, puis choisissez le bouton OK.

Pour ajouter le code HTML qui appelle les gestionnaires d’événements JavaScript

Collez ce code HTML dans le <nœud du corps> de la page default.html :

<div id="LogButtonDiv">
     <button id="logButton">Logarithms using AMP</button>
 </div>
 <div id="LogResultDiv">
     <p id="logResult"></p>
 </div>
 <div id="OrderedPrimeButtonDiv">
     <button id="orderedPrimeButton">Primes using parallel_for with sort</button>
 </div>
 <div id="OrderedPrimeProgress">
     <progress id="OrderedPrimesProgressBar" value="0" max="100"></progress>
 </div>
 <div id="OrderedPrimeResultDiv">
     <p id="orderedPrimes">
         Primes found (ordered):
     </p>
 </div>
 <div id="UnorderedPrimeButtonDiv">
     <button id="ButtonUnordered">Primes returned as they are produced.</button>
 </div>
 <div id="UnorderedPrimeDiv">
     <progress id="UnorderedPrimesProgressBar" value="0" max="100"></progress>
 </div>
 <div id="UnorderedPrime">
     <p id="unorderedPrimes">
         Primes found (unordered):
     </p>
 </div>
 <div id="ClearDiv">
     <button id="Button_Clear">Clear</button>
 </div>

Pour ajouter des styles

Dans default.css, supprimez le style de corps, puis ajoutez ces styles :

#LogButtonDiv {
border: orange solid 1px;
-ms-grid-row: 1; /* default is 1 */
-ms-grid-column: 1; /* default is 1 */
}
#LogResultDiv {
background: black;
border: red solid 1px;
-ms-grid-row: 1;
-ms-grid-column: 2;
}
#UnorderedPrimeButtonDiv, #OrderedPrimeButtonDiv {
border: orange solid 1px;
-ms-grid-row: 2;   
-ms-grid-column:1;
}
#UnorderedPrimeProgress, #OrderedPrimeProgress {
border: red solid 1px;
-ms-grid-column-span: 2;
height: 40px;
}
#UnorderedPrimeResult, #OrderedPrimeResult {
border: red solid 1px;
font-size:smaller;
-ms-grid-row: 2;
-ms-grid-column: 3;
-ms-overflow-style:scrollbar;
}

Pour ajouter les gestionnaires d’événements JavaScript qui appellent la DLL du composant

Ajoutez les fonctions suivantes à la fin du fichier default.js. Ces fonctions sont appelées lorsque les boutons de la page principale sont choisis. Notez comment JavaScript active la classe C++, puis appelle ses méthodes et utilise les valeurs de retour pour remplir les étiquettes HTML.

var nativeObject = new WinRT_CPP.Class1();

function LogButton_Click() {

    var val = nativeObject.computeResult(0);
    var result = "";

    for (i = 0; i < val.length; i++) {
        result += val[i] + "<br/>";
    }

    document.getElementById('logResult').innerHTML = result;
}

function ButtonOrdered_Click() {
    document.getElementById('orderedPrimes').innerHTML = "Primes found (ordered): ";

    nativeObject.getPrimesOrdered(2, 10000).then(
        function (v) {
            for (var i = 0; i < v.length; i++)
                document.getElementById('orderedPrimes').innerHTML += v[i] + " ";
        },
        function (error) {
            document.getElementById('orderedPrimes').innerHTML += " " + error.description;
        },
        function (p) {
            var progressBar = document.getElementById("OrderedPrimesProgressBar");
            progressBar.value = p;
        });
}

function ButtonUnordered_Click() {
    document.getElementById('unorderedPrimes').innerHTML = "Primes found (unordered): ";
    nativeObject.onprimefoundevent = handler_unordered;

    nativeObject.getPrimesUnordered(2, 10000).then(
        function () { },
        function (error) {
            document.getElementById("unorderedPrimes").innerHTML += " " + error.description;
        },
        function (p) {
            var progressBar = document.getElementById("UnorderedPrimesProgressBar");
            progressBar.value = p;
        });
}

var handler_unordered = function (n) {
    document.getElementById('unorderedPrimes').innerHTML += n.target.toString() + " ";
};

function ButtonClear_Click() {

    document.getElementById('logResult').innerHTML = "";
    document.getElementById("unorderedPrimes").innerHTML = "";
    document.getElementById('orderedPrimes').innerHTML = "";
    document.getElementById("UnorderedPrimesProgressBar").value = 0;
    document.getElementById("OrderedPrimesProgressBar").value = 0;
}

Ajoutez du code pour ajouter les écouteurs d’événements en remplaçant l’appel existant à WinJS.UI.processAll dans app.onactivated dans default.js par le code suivant qui implémente l’inscription d’événements dans un bloc. Pour obtenir une explication détaillée de ce problème, consultez Créer une application « Hello, World » (JS).

args.setPromise(WinJS.UI.processAll().then( function completed() {
    var logButton = document.getElementById("logButton");
    logButton.addEventListener("click", LogButton_Click, false);
    var orderedPrimeButton = document.getElementById("orderedPrimeButton");
    orderedPrimeButton.addEventListener("click", ButtonOrdered_Click, false);
    var buttonUnordered = document.getElementById("ButtonUnordered");
    buttonUnordered.addEventListener("click", ButtonUnordered_Click, false);
    var buttonClear = document.getElementById("Button_Clear");
    buttonClear.addEventListener("click", ButtonClear_Click, false);
}));

Appuyez sur F5 pour exécuter l'application.

Création d’une application cliente C#

Pour créer un projet C#

  1. Dans Explorateur de solutions, ouvrez le menu contextuel du nœud Solution, puis choisissez Ajouter, Nouveau projet.

  2. Développez Visual C# (il peut être imbriqué sous d’autres langues), sélectionnez Windows , puis Universel dans le volet gauche, puis sélectionnez Application vide dans le volet central.

  3. Nommez cette application CS_Client, puis choisissez le bouton OK .

  4. Ouvrez le menu contextuel du nœud de projet CS_Client, puis choisissez Définir comme projet de démarrage.

  5. Ajoutez une référence de projet à WinRT_CPP :

    • Ouvrez le menu contextuel du nœud Références et choisissez Ajouter une référence.

    • Dans le volet gauche de la boîte de dialogue Gestionnaire de références, sélectionnez Projets , puis sélectionnez Solution.

    • Dans le volet central, sélectionnez WinRT_CPP, puis choisissez le bouton OK .

Pour ajouter le code XAML qui définit l’interface utilisateur

Copiez le code suivant dans l’élément Grid dans MainPage.xaml.

<ScrollViewer>
            <StackPanel Width="1400">

                <Button x:Name="Button1" Width="340" Height="50"  Margin="0,20,20,20" Content="Synchronous Logarithm Calculation" FontSize="16" Click="Button1_Click_1"/>
                <TextBlock x:Name="Result1" Height="100" FontSize="14"></TextBlock>
            <Button x:Name="PrimesOrderedButton" Content="Prime Numbers Ordered" FontSize="16" Width="340" Height="50" Margin="0,20,20,20" Click="PrimesOrderedButton_Click_1"></Button>
            <ProgressBar x:Name="PrimesOrderedProgress" IsIndeterminate="false" Height="40"></ProgressBar>
                <TextBlock x:Name="PrimesOrderedResult" MinHeight="100" FontSize="10" TextWrapping="Wrap"></TextBlock>
            <Button x:Name="PrimesUnOrderedButton" Width="340" Height="50" Margin="0,20,20,20" Click="PrimesUnOrderedButton_Click_1" Content="Prime Numbers Unordered" FontSize="16"></Button>
            <ProgressBar x:Name="PrimesUnOrderedProgress" IsIndeterminate="false" Height="40" ></ProgressBar>
            <TextBlock x:Name="PrimesUnOrderedResult" MinHeight="100" FontSize="10" TextWrapping="Wrap"></TextBlock>

            <Button x:Name="Clear_Button" Content="Clear" HorizontalAlignment="Left" Margin="0,20,20,20" VerticalAlignment="Top" Width="341" Click="Clear_Button_Click" FontSize="16"/>
        </StackPanel>
</ScrollViewer>

Pour ajouter les gestionnaires d’événements pour les boutons

Dans Explorateur de solutions, ouvrez MainPage.xaml.cs. (Le fichier peut être imbriqué sous MainPage.xaml.) Ajoutez une directive using pour System.Text, puis ajoutez le gestionnaire d’événements pour le calcul Logarithm dans la classe MainPage.

private void Button1_Click_1(object sender, RoutedEventArgs e)
{
    // Create the object
    var nativeObject = new WinRT_CPP.Class1();

    // Call the synchronous method. val is an IList that
    // contains the results.
    var val = nativeObject.ComputeResult(0);
    StringBuilder result = new StringBuilder();
    foreach (var v in val)
    {
        result.Append(v).Append(System.Environment.NewLine);
    }
    this.Result1.Text = result.ToString();
}

Ajoutez le gestionnaire d’événements pour le résultat ordonné :

async private void PrimesOrderedButton_Click_1(object sender, RoutedEventArgs e)
{
    var nativeObject = new WinRT_CPP.Class1();

    StringBuilder sb = new StringBuilder();
    sb.Append("Primes found (ordered): ");

    PrimesOrderedResult.Text = sb.ToString();

    // Call the asynchronous method
    var asyncOp = nativeObject.GetPrimesOrdered(2, 100000);

    // Before awaiting, provide a lambda or named method
    // to handle the Progress event that is fired at regular
    // intervals by the asyncOp object. This handler updates
    // the progress bar in the UI.
    asyncOp.Progress = (asyncInfo, progress) =>
        {
            PrimesOrderedProgress.Value = progress;
        };

    // Wait for the operation to complete
    var asyncResult = await asyncOp;

    // Convert the results to strings
    foreach (var result in asyncResult)
    {
        sb.Append(result).Append(" ");
    }

    // Display the results
    PrimesOrderedResult.Text = sb.ToString();
}

Ajoutez le gestionnaire d’événements pour le résultat non ordonné et pour le bouton qui efface les résultats afin de pouvoir réexécuter le code.

private void PrimesUnOrderedButton_Click_1(object sender, RoutedEventArgs e)
{
    var nativeObject = new WinRT_CPP.Class1();

    StringBuilder sb = new StringBuilder();
    sb.Append("Primes found (unordered): ");
    PrimesUnOrderedResult.Text = sb.ToString();

    // primeFoundEvent is a user-defined event in nativeObject
    // It passes the results back to this thread as they are produced
    // and the event handler that we define here immediately displays them.
    nativeObject.primeFoundEvent += (n) =>
    {
        sb.Append(n.ToString()).Append(" ");
        PrimesUnOrderedResult.Text = sb.ToString();
    };

    // Call the async method.
    var asyncResult = nativeObject.GetPrimesUnordered(2, 100000);

    // Provide a handler for the Progress event that the asyncResult
    // object fires at regular intervals. This handler updates the progress bar.
    asyncResult.Progress += (asyncInfo, progress) =>
        {
            PrimesUnOrderedProgress.Value = progress;
        };
}

private void Clear_Button_Click(object sender, RoutedEventArgs e)
{
    PrimesOrderedProgress.Value = 0;
    PrimesUnOrderedProgress.Value = 0;
    PrimesUnOrderedResult.Text = "";
    PrimesOrderedResult.Text = "";
    Result1.Text = "";
}

Exécution de l’application

Sélectionnez le projet C# ou le projet JavaScript comme projet de démarrage en ouvrant le menu contextuel du nœud du projet dans Explorateur de solutions et en choisissant Définir comme projet de démarrage. Appuyez ensuite sur F5 pour s’exécuter avec débogage, ou Ctrl+F5 pour s’exécuter sans débogage.

Inspection de votre composant dans l’Explorateur d’objets (facultatif)

Dans l’Explorateur d’objets, vous pouvez inspecter tous les types Windows Runtime définis dans les fichiers .winmd. Cela inclut les types dans l’espace de noms Platform et l’espace de noms par défaut. Toutefois, étant donné que les types de l’espace de noms Platform ::Collections sont définis dans le fichier d’en-tête collections.h, et non dans un fichier winmd, ils n’apparaissent pas dans l’Explorateur d’objets.

Pour inspecter un composant

  1. Dans la barre de menus, choisissez Affichage, Explorateur d’objets (Ctrl+Alt+J).

  2. Dans le volet gauche de l’Explorateur d’objets, développez le nœud WinRT_CPP pour afficher les types et méthodes définis sur votre composant.

Conseils de débogage

Pour une meilleure expérience de débogage, téléchargez les symboles de débogage à partir des serveurs de symboles Microsoft publics :

Pour télécharger les symboles de débogage

  1. Dans la barre de menus, choisissez Outils, Options.

  2. Dans la boîte de dialogue Options , développez Débogage et sélectionnez Symboles.

  3. Sélectionnez Serveurs de symboles Microsoft, puis cliquez sur le bouton OK .

Il peut prendre un certain temps pour télécharger les symboles la première fois. Pour accélérer les performances la prochaine fois que vous appuyez sur F5, spécifiez un répertoire local dans lequel mettre en cache les symboles.

Lorsque vous déboguez une solution JavaScript qui a une DLL de composant, vous pouvez définir le débogueur pour activer l’exécution pas à pas de script ou pas à pas dans le code natif dans le composant, mais pas les deux en même temps. Pour modifier le paramètre, ouvrez le menu contextuel du nœud de projet JavaScript dans Explorateur de solutions et choisissez Propriétés, Débogage, Type de débogueur.

Veillez à sélectionner les fonctionnalités appropriées dans le concepteur de packages. Vous pouvez ouvrir le concepteur de package en ouvrant le fichier Package.appxmanifest. Par exemple, si vous tentez d’accéder par programme aux fichiers dans le dossier Images, veillez à cocher la case à cocher Bibliothèque d’images dans le volet Fonctionnalités du concepteur de packages.

Si votre code JavaScript ne reconnaît pas les propriétés publiques ou les méthodes du composant, assurez-vous que, dans JavaScript, vous utilisez la casse chameau. Par exemple, la ComputeResult méthode C++ doit être référencée comme computeResult dans JavaScript.

Si vous supprimez un projet de composant Windows Runtime C++ d’une solution, vous devez également supprimer manuellement la référence du projet JavaScript. L’échec de cette opération empêche les opérations de débogage ou de génération suivantes. Si nécessaire, vous pouvez ensuite ajouter une référence d’assembly à la DLL.