Procédure pas à pas : création d'un composant Windows Runtime de base en C++ et appel de ce composant depuis JavaScript ou C#
Cette procédure pas à pas montre comment créer une DLL de composant Windows Runtime de base qui peut être appelée à partir de JavaScript, C# ou de Visual Basic. Avant de commencer cette procédure pas à pas, assurez-vous de comprendre les concepts tels que l'interface binaire abstraite (ABI), les classes ref et les extensions de composants Visual C++ qui facilitent l'utilisation des classes ref. Pour plus d'informations, consultez Création de composants Windows Runtime en C++ et Référence du langage Visual C++ (C++/CX).
Création de la DLL du composant C++
Dans cet exemple, nous commençons par créer le projet du composant, mais vous pouvez très bien créer le projet JavaScript en premier. L'ordre n'a pas d'importance.
Notez que la classe principale du composant contient des exemples de propriété et de définitions de méthode, ainsi qu'une déclaration d'événement. Ces éléments sont fournis uniquement pour vous montrer le fonctionnement. ils ne sont pas obligatoires, et dans cet exemple, nous remplacerons le code généré par notre propre code.
Pour créer le projet de composant C++
Dans la barre de menus Visual Studio, choisissez Fichier, Nouveau, Projet.
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 Store.
Dans le volet central, sélectionnez Composant Windows Runtime, puis nommez le projet WinRT_CPP.
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 expression new (New en Visual Basic, ou ref new en C++). Dans votre composant, vous devez la déclarer comme public ref class sealed. En fait, les fichiers Class1.h et .cpp ont déjà une classe de référence. Vous pouvez modifier le nom, mais dans cet exemple nous utiliserons celui par défaut : Class1. Vous pouvez définir des classes ref ou standard supplémentaires dans votre composant si elles sont requises. Pour plus d'informations sur les classes ref, consultez Système de type (C++/CX).
Pour ajouter les directives #include requises
Ajoutez ces directives #include à Class1.h:
#include <collection.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 Platform::Collections::Vector, classe et la Platform::Collections::Map, classe, qui implémentent des interfaces indépendantes du langage définies par Windows Runtime. Les en-têtes amp sont utilisés pour exécuter des calculs sur le GPU. Ils n'ont pas d'équivalent dans Windows Runtime, et cela ne pose pas de problème, car ils sont privés. En général, pour des raisons de performance vous devez utiliser du code C++ ISO et des bibliothèques standard en interne dans le composant ; seule l'interface Windows Runtime doit être exprimée en types Windows Runtime.
Pour ajouter un délégué à la portée 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 event public de ce type ; cela permet au code client de fournir des méthodes qui sont invoquées lorsque l'événement est déclenché.
Ajoutez la déclaration delegate suivante à la portée espace de noms dans Class1.h, juste avant la déclaration Class1.
public delegate void PrimeFoundHandler(int result);
Conseil
Si le code ne s'aligne pas correctement lorsque vous le collez dans Visual Studio, appuyez sur Ctrl+K+D pour corriger la mise en retrait pour le fichier complet.
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 de ne pas bloquer le thread d'interface utilisateur. Ces méthodes retournent IAsyncOperationWithProgress et IAsyncActionWithProgress. Le premier définit une méthode asynchrone qui retourne un résultat, et le second 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
Dans Class1.cpp, ajoutez ces directives #include :
#include <ppltasks.h> #include <concurrent_vector.h>
Ajoutez ensuite ces instructions using pour récupérer 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 façon 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 Présentation de C++ AMP. Les résultats sont ajoutés à un type concret Platform::Collections::Vector<T>, qui est implicitement converti en un Windows::Foundation::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 https://msdn.microsoft.com/en-us/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 Classe concurrent_vector et une boucle parallel_for, fonction pour diviser le travail et pour utiliser les ressources maximales de l'ordinateur sur lequel le programme s'exécute pour produire des résultats. Une fois que les résultats sont calculés, stockés et triés, ils sont ajoutés à Platform::Collections::Vector<T> et retournés en tant que Windows::Foundation::Collections::IVector<T> au code client.
Notez le code du rapporteur de progression, qui permet au client de connecter une barre de progression ou toute autre interface utilisateur permettant à l'utilisateur de voir combien de temps l'opération va encore prendre. Le rapport de progression a un coût. Un événement doit être déclenché du côté du composant et géré sur le thread d'interface utilisateur, et la valeur de progression doit être stockée à chaque itération. Un moyen 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 encore excessif, ou si vous ne pouvez pas estimer la longueur de l'opération, envisagez d'utiliser une boucle de progression ; elle indique qu'une opération est en cours sans afficher le temps restant jusqu'à la fin.
// 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 pour créer le composant C++ consiste à ajouter l'implémentation pour GetPrimesUnordered dans Class1.cpp. Cette méthode retourne chaque résultat dès 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é dans l'interface utilisateur en temps réel. Là encore, notez qu'un rapporteur 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); }); }
Appuyez sur Ctrl+Shift+B pour générer le composant.
Création d'une application cliente JavaScript
Pour créer un projet JavaScript
-
Notes
Si vous souhaitez simplement créer un client C#, vous pouvez ignorer cette section.
Dans l'Explorateur de solutions, ouvrez le menu contextuel du nœud Solution et sélectionnez Ajouter, Nouveau projet.
Développez JavaScript (il peut être imbriqué sous Autres langages) et choisissez Application vide.
Acceptez le nom par défaut, App1, en cliquant sur le bouton OK.
Ouvrez le menu contextuel du nœud de projet App1, puis sélectionnez Définir comme projet de démarrage.
Ajoutez une référence de projet à WinRT_CPP :
Ouvrez le menu contextuel du nœud Références et sélectionnez Ajouter une référence.
Dans le volet gauche de la boîte de dialogue Gestionnaire de références, sélectionnez Solution, puis Projets.
Dans le volet central, sélectionnez WinRT_CPP, puis cliquez sur le bouton OK.
Pour ajouter le code HTML qui appelle les gestionnaires d'événements JavaScript
Collez ce code HTML dans le nœud <body> de la page default.html :
<div id="LogButtonDiv"> <button id="logButton" onclick="LogButton_Click()">Logarithms using AMP</button> </div> <div id="LogResultDiv"> <p id="logResult"></p> </div> <div id="OrderedPrimeButtonDiv"> <button id="orderedPrimeButton" onclick="ButtonOrdered_Click()">Primes using parallel_for with sort</button> </div> <div id="OrderedPrimeProgress"> <progress id="OrderedPrimesProgressBar" style="-ms-grid-column-span:2" value="0" max="100"></progress> </div> <div id="OrderedPrimeResultDiv"> <p id="orderedPrimes"> Primes found (ordered): </p> </div> <div id="UnorderedPrimeButtonDiv"> <button id="ButtonUnordered" onclick="ButtonUnordered_Click()">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" onclick="ButtonClear_Click()">Clear</button> </div>
<div id="LogButtonDiv"> <button id="logButton" onclick="LogButton_Click()">Logarithms using AMP</button> </div> <div id="LogResultDiv"> <p id="logResult"></p> </div> <div id="OrderedPrimeButtonDiv"> <button id="orderedPrimeButton" onclick="ButtonOrdered_Click()">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" onclick="ButtonUnordered_Click()">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" onclick="ButtonClear_Click()">Clear</button> </div>
Pour ajouter des styles
Dans default.css, supprimez le style body, 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 faisant appel à 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 sélectionnés. Notez que JavaScript active la classe C++, appelle ensuite ses méthodes, puis utilise des 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; }
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; }
Appuyez sur F5 pour exécuter l'application.
Création d'une application cliente C#
La DLL du composant Windows Runtime C++ peut tout aussi facilement être appelée à partir d'un client C# que d'un client JavaScript. Les étapes suivantes montrent comment créer un client C# qui soit pratiquement équivalent au client JavaScript de la section précédente.
Pour créer un projet C#
Dans l'Explorateur de solutions, ouvrez le menu contextuel du nœud Solution, puis sélectionnez Ajouter, Nouveau projet.
Développez Visual C# (il peut être imbriqué sous Autres langages), sélectionnez Windows Store dans le volet gauche, puis Application vide dans le volet central.
Nommez cette application CS_Client, puis cliquez sur le bouton OK.
Ouvrez le menu contextuel du nœud de projet CS_Client, puis sélectionnez Définir comme projet de démarrage.
Ajoutez une référence de projet à WinRT_CPP :
Ouvrez le menu contextuel du nœud Références et sélectionnez Ajouter une référence.
Dans le volet gauche de la boîte de dialogue Gestionnaire de références, sélectionnez Solution, puis Projets.
Dans le volet central, sélectionnez WinRT_CPP, puis cliquez sur le bouton OK.
Pour ajouter le code XAML qui définit l'interface utilisateur
Ajoutez le ScrollViewer suivant et son contenu à la grille 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 l'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 de logarithme dans la classe MainPage, juste après OnNavigateTo.
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 exécuter à nouveau 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 en tant que projet de démarrage en ouvrant le menu contextuel du nœud de projet dans l'Explorateur de solutions et en sélectionnant Définir comme projet de démarrage. Appuyez ensuite sur F5 pour une exécution avec débogage, ou sur Ctrl+F5 pour une exécution sans débogage.
Examen de votre composant dans l'Explorateur d'objets (facultatif)
Dans Explorateur d'objets, vous pouvez examiner tous les types Windows Runtime définis dans les fichiers.winmd. Cela inclut les types de l'espace de noms Platform et l'espace de noms par défaut. Toutefois, étant donné que les types dans 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 Explorateur d'objets.
Pour vérifier le composant
Dans la barre de menus, sélectionnez Afficher, Autres fenêtres, Explorateur d'objets.
Dans le volet gauche de l'Explorateur d'objets, développez le nœud WinRT_CPP pour afficher les types et les méthodes qui sont définis dans votre composant.
Conseils de débogage
Pour optimiser le débogage, téléchargez les symboles de débogage à partir des serveurs de symboles publics de Microsoft :
Dans la barre de menus, sélectionnez Outils, Options.
Dans la boîte de dialogue Options, développez Débogage et sélectionnez Symboles.
Sélectionnez Serveurs de symboles Microsoft, puis cliquez sur le bouton OK.
Le téléchargement des symboles peut prendre un certain temps la première fois. Pour accélérer les performances la prochaine fois que vous appuierez sur F5, spécifiez un répertoire local dans lequel mettre en cache les symboles.
Lorsque vous déboguez une solution JavaScript qui contient une DLL de composant, vous pouvez définir le débogueur pour activer la progression dans le script ou dans le code natif du composant, mais pas les deux à la fois. Pour modifier ce paramètre, ouvrez le menu contextuel du nœud de projet JavaScript dans l'Explorateur de solutions, puis sélectionnez Propriétés, Débogage, Type de débogueur.
Veillez à sélectionner les fonctionnalités appropriées dans le concepteur de packages. Par exemple, si vous essayez d'accéder par programmation à des fichiers du dossier Images, veillez à activer la case à cocher Bibliothèque d'images dans le volet Capacités du concepteur de packages.
Si votre code JavaScript ne reconnaît pas les propriétés ou méthodes publiques du composant, assurez-vous que vous utilisez la casse mixte dans JavaScript. Par exemple, la méthode C++ ComputeResult doit être référencée sous la forme computeResult dans JavaScript.
Si vous supprimez un projet de composant Windows Runtime dans une solution, vous devez également supprimer manuellement la référence de projet dans le projet JavaScript. Sinon, les opérations de débogage ou de génération suivantes ne peuvent pas être effectuées. Si nécessaire, ajoutez ensuite une référence d'assembly à la DLL.
Voir aussi
Référence
Roadmap for Windows Store apps using C++