Exemplarische Vorgehensweise: Erstellen einer grundlegenden Windows-Runtime-Komponente in C++ und Aufrufen dieser Komponente über JavaScript
In dieser exemplarischen Vorgehensweise wird die Erstellung einer grundlegenden Windows-Runtime-Komponenten-DLL erläutert, die aus JavaScript, C# oder Visual Basic aufgerufen werden kann. Bevor Sie die einzelnen Schritte durcharbeiten, machen Sie sich mit Konzepten wie der abstrakten binären Schnittstelle (ABI), Verweisklassen und der Komponentenerweiterungen von Visual C++ vertraut, die das Arbeiten mit Verweisklassen vereinfachen. Weitere Informationen finden Sie unter Erstellen von Windows-Runtime-Komponenten in C++ und Sprachreferenz zu Visual C++ (C++/CX).
Erstellen des C++-Komponentenprojekts
In diesem Beispiel wird zuerst das Komponentenprojekt erstellt, Sie können aber auch erst das JavaScript-Projekt erstellen. Die Reihenfolge spielt keine Rolle.
Beachten Sie, dass die Hauptklasse der Komponente Beispiele für Definitionen von Eigenschaften und Methoden sowie eine Ereignisdeklaration enthält. Dies dient nur zur Veranschaulichung der Vorgehensweise. Die Beispiele sind nicht erforderlich, und in diesem Beispiel wird der gesamte generierte Code durch eigenen Code ersetzt.
So erstellen Sie das C++-Komponentenprojekt
Klicken Sie in Visual Studio in der Menüleiste auf Datei, auf Neu und dann auf Projekt.
Erweitern Sie im Dialogfeld Neues Projekt links den Eintrag Visual C++, und wählen Sie den Knoten für Windows Store aus.
Wählen Sie in der Mitte Windows-Runtime-Komponente und dann das Projekt CppLib aus.
Klicken Sie auf OK.
Hinzufügen einer aktivierbaren Klasse zur Komponente
Eine aktivierbare Klasse ist eine Klasse, die JavaScript mithilfe eines new-Ausdrucks erstellen kann. In der Komponente wird sie als public ref class sealed deklariert. Tatsächlich haben die Dateien "Class1.h" und "Class1.cpp" bereits eine Verweisklasse. Der Name kann geändert werden. In diesem Beispiel wird aber der Standardname Class1 verwendet. Bei Bedarf können Sie in der Komponente zusätzliche Verweisklassen definieren. Weitere Informationen über ref-Klassen finden Sie unter Typsystem (C++/CX).
Hinzufügen der erforderlichen #include- und using-Anweisungen
So fügen Sie die erforderlichen #include- und using-Anweisungen hinzu
Fügen Sie der Datei "Class1.h" diese #include-Direktiven hinzu:
#include <collection.h> #include <amp.h> #include <amp_math.h>
Fügen Sie der Datei "Class1.cpp" diese #include-Direktiven hinzu:
#include <ppltasks.h> #include <concurrent_vector.h>
Fügen Sie der Datei "Class1.cpp" diese using-Anweisungen hinzu:
using namespace concurrency; using namespace Platform::Collections; using namespace Windows::Foundation::Collections; using namespace Windows::Foundation; using namespace Windows::UI::Core;
Hinzufügen einer synchronen öffentlichen Methode zur Klasse
Schnell ausführbare Methoden lassen sich als synchrone Methoden implementieren. Sie werden auf demselben Thread wie der JavaScript-Aufrufer ausgeführt. Alle Parametertypen und Rückgabewerte öffentlicher Methoden müssen mit Windows-Runtime kompatibel sein.
So fügen Sie einer Klasse eine synchrone öffentliche Methode hinzu
Fügen Sie der Klasse in der Datei "Class1.h" die folgenden Deklarationen hinzu:
public: Windows::Foundation::Collections::IVector<double>^ ComputeResult(double input); Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last); Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last); event PrimeFoundHandler^ primeFoundEvent; private: bool is_prime(int n); Windows::UI::Core::CoreDispatcher^ m_dispatcher; private: void ComputeResultImpl(concurrency::array_view<float, 1>&);
Fügen Sie der Datei "Class1.cpp" die folgenden Implementierungen hinzu:
void Class1::ComputeResultImpl(array_view<float, 1>& logs) { parallel_for_each( logs.extent, [=] (index<1> idx) restrict(amp) { logs[idx] = concurrency::fast_math::log10(logs[idx]); } ); } //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); ComputeResultImpl(logs); // 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]); } return res; }
Hinzufügen einer asynchronen öffentlichen Methode zur Klasse
Methoden, deren Ausführung etwas länger dauert, sollten als asynchrone Methoden implementiert werden. Eine asynchrone Methode wird in einem Hintergrundthread ausgeführt und blockiert so nicht den JavaScript-Thread. Erstellen Sie die öffentliche async-Methode, indem Sie die create_async-Methode intern verwenden. JavaScript ruft die Methode wie jede andere Zusicherung auf. Wie auch bei einer synchronen Methode müssen alle Parametertypen mit Windows-Runtime kompatibel sein. Bei einer asynchronen Methode muss der Rückgabetyp einer der folgenden sein:
IAsyncAction bei asynchronen Methoden, die keinen Wert zurückgeben und keine Statusinformationen liefern.
IAsyncActionWithProgress bei asynchronen Methoden, die keinen Wert zurückgeben aber Statusinformationen liefern.
IAsyncOperation<T> bei asynchronen Methoden, die einen Wert T zurückgeben aber keine Statusinformationen liefern.
IAsyncOperationWithProgress<T> bei asynchronen Methoden, die einen Wert T zurückgeben und Statusinformationen liefern.
Weitere Informationen finden Sie unter Erstellen von asynchronen Vorgängen in C++ für Windows Store-Apps.
So fügen Sie eine asynchrone Methode hinzu, die ein Ergebnis zurückgibt und Statusinformationen liefert
Fügen Sie der Klasse in der Datei "Class1.h" die folgenden Deklarationen hinzu:
Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last); Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last); event PrimeFoundHandler^ primeFoundEvent; private: bool is_prime(int n);
Fügen Sie der Datei "Class1.cpp" die folgenden Implementierungen hinzu:
// 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) { // Report progress message. double progress = 100.0 * InterlockedIncrement(&operation) / range; if (progress >= lastPercent) { reporter.report(progress); lastPercent += 1.0; } // 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>()); // Copy the results to an IVector object. The IVector // interface makes collections of data available to other // Windows Runtime components. IVector<int>^ results = ref new Vector<int>(); std::for_each(std::begin(primes), std::end(primes), [&results](int prime) { results->Append(prime); }); reporter.report(100.0); return results; }); }
So fügen Sie eine asynchrone Methode hinzu, die Ereignisse auslöst und Statusinformationen liefert
Fügen Sie der Klasse in der Datei "Class1.h" die folgenden öffentlichen Deklarationen hinzu:
Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last); event PrimeFoundHandler^ primeFoundEvent;
Fügen Sie die folgende private Deklaration hinzu:
Windows::UI::Core::CoreDispatcher^ m_dispatcher;
Fügen Sie im Namespace-Gültigkeitsbereich in der Datei "Class1.h" den folgenden Delegaten hinzu:
public delegate void PrimeFoundHandler(int i);
Mit diesem Objekt werden die Ereignisse von der parallelen Funktion zurück an den UI-Thread geleitet.
Fügen Sie der Datei "Class1.cpp" die folgenden Implementierungen hinzu:
// This method returns no value. Instead, it fires an event each time a prime is found, and transfers the prime through the event. 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(); } // 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) { // Report progress message. double progress = 100.0 * InterlockedIncrement(&operation) / range; if (progress >= lastPercent) { reporter.report(progress); lastPercent += 1.0; } // If the value is prime, add it to the local vector. if (is_prime(n)) { primes.push_back(n); m_dispatcher->RunAsync( CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, n]() { this->primeFoundEvent(n); }, Platform::CallbackContext::Any)); } }); reporter.report(100.0); }); }
Erstellen eines JavaScript-Projekts
So erstellen Sie ein JavaScript-Projekt
Wählen Sie in Projektmappen-Explorer im Kontextmenü des Knotens Projektmappe die Option Hinzufügen und dann Neues Projekt aus.
Erweitern Sie JavaScript, und wählen Sie Leere App aus.
Übernehmen Sie den Standardnamen "App1" durch Klicken auf OK.
Klicken Sie mit der rechten Maustaste auf den App1-Projektknoten, und wählen Sie Als Startprojekt festlegen aus.
Ergänzen Sie CppLib mit einem Projektverweis:
Klicken Sie im Kontextmenü des Knotens Verweise auf Verweis hinzufügen.
Wählen Sie links im Dialogfeld Verweis-Manager die Option Projektmappe und dann Projekte aus.
Wählen Sie in der Mitte CppLib aus, und klicken Sie dann auf OK.
Hinzufügen des HTML-Codes, mit dem die JavaScript-Ereignishandler aufgerufen werden
Fügen Sie auf der Seite "default.html" den folgenden HTML-Code in den <body>-Knoten ein:
<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>
Hinzufügen der Stile
Entfernen Sie den body-Stil, und fügen Sie der Datei "default.css" die folgenden Stile hinzu:
#LogButtonDiv { background: maroon; 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 { background: green; border: orange solid 1px; -ms-grid-row: 2; -ms-grid-column:1; } #UnorderedPrimeDiv, #OrderedPrimeDiv { background: maroon; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column:1; } #UnorderedPrimeProgress, #OrderedPrimeProgress { background: lightgray; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column: 2; -ms-grid-column-span: 2; } #UnorderedPrimeResult, #OrderedPrimeResult { background: black; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column: 3; }
Hinzufügen der JavaScript-Ereignishandler, welche die Komponenten-DLL aufrufen
Fügen Sie am Ende der Datei "default.js" die folgenden Funktionen hinzu: Diese Funktionen werden bei Auswahl der Schaltflächen auf der Hauptseite aufgerufen. Beachten Sie, wie JavaScript die C++-Klasse aktiviert und dann die zugehörigen Methoden aufruft und mithilfe der Rückgabewerte die HTML-Bezeichnungen ausfüllt.
var nativeObject = new cpplib.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): "; var asyncResult = nativeObject.getPrimesOrdered(2, 1000).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; var asyncResult = nativeObject.getPrimesUnordered(2, 1000).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() + " "; };
Ausführen der App
Drücken Sie F5.
Überprüfen der Komponente im Objektkatalog (optional)
Im Objektkatalog können Sie alle Windows-Runtime-Typen überprüfen, die in WINMD-Dateien definiert werden. Dazu gehören auch die Typen im Platform-Namespace und im Standard-Namespace. Allerdings werden die Typen im Platform::Collections-Namespace in der Headerdatei "collections.h" und nicht in einer WINMD-Datei definiert. Daher sind diese Typen nicht im Objektkatalog verzeichnet.
So überprüfen Sie die Komponente im Objektkatalog
Klicken Sie in der Visual Studio-Menüleiste auf Ansicht, Weitere Fenster, Objektkatalog.
Erweitern Sie links im Objektkatalog den Knoten CppLib, um die in der Komponente definierten Typen und Methoden einzusehen.
Tipps zum Debuggen
Eine bessere Debugleistung erzielen Sie, wenn Sie die Debugsymbole von den öffentlichen Microsoft-Symbolservern herunterladen. Wählen Sie im Hauptmenü Extras und dann Optionen aus. Erweitern Sie unter Optionen den Eintrag Debugging, und wählen Sie Symbole aus. Aktivieren Sie das Kontrollkästchen neben Microsoft-Symbolserver, und klicken Sie auf OK. Das erstmalige Herunterladen kann etwas dauern. Wenn Sie das nächste Mal F5 drücken, können Sie den Vorgang etwas beschleunigen, indem Sie in dem angezeigten Feld ein lokales Verzeichnis angeben, in dem die Symbole zwischengespeichert werden sollen.
Wenn Sie eine JavaScript-Projektmappe debuggen, die über eine Komponenten-DLL verfügt, können Sie den Debugger so festlegen, dass entweder das schrittweise Ausführen des Skripts oder das schrittweise Ausführen des systemeigenen Codes in der Komponente, nicht jedoch beides aktiviert wird. Um die Einstellung zu ändern, klicken Sie in Projektmappen-Explorer im Kontextmenü des JavaScript-Projektknotens auf Eigenschaften, Debugging, Debuggertyp.
Im Paket-Designer müssen die entsprechenden Funktionen ausgewählt sein. Wenn Sie beispielsweise versuchen, eine Datei mithilfe der Windows-Runtime-APIs zu öffnen, aktivieren Sie im Paket-Designer im Fenster Funktionen auf jeden Fall das Kontrollkästchen Dokumentbibliothek.
Werden die öffentlichen Eigenschaften oder Methoden der Komponente mithilfe des JavaScript-Codes nicht erkannt, überprüfen Sie, dass in JavaScript die Kamel-Schreibweise verwendet wird. Beispielsweise muss in JavaScript auf die ComputeResult-C++-Methode mit computeResult verwiesen werden.
Wenn Sie ein Windows-Runtime-Komponentenprojekt in C++ aus einer Projektmappe löschen, muss auch der Projektverweis manuell aus dem JavaScript-Projekt entfernt werden. Andernfalls können anschließend keine Debugging- oder Erstellungsvorgänge ausgeführt werden. Bei Bedarf können Sie dann einen Assemblyverweis auf die DLL hinzufügen.
Siehe auch
Referenz
Roadmap for Windows Store apps using C++
Weitere Ressourcen
Entwickeln des Reise-Optimierers von Bing Maps, einer Windows Store-App in JavaScript und C++