Freigeben über


Exemplarische Vorgehensweise zum Erstellen einer Komponente für Windows-Runtime in C++/CX und Aufrufen der Komponente über JavaScript oder C#

Hinweis

Dieses Thema soll Ihnen dabei helfen, Ihre C++/CX-Anwendung zu pflegen. Wir empfehlen aber, C++/WinRT für neue Anwendungen zu nutzen. C++/WinRT ist eine vollständig standardisierte, moderne C++17-Programmiersprache für Windows-Runtime-APIs (WinRT), die als headerdateibasierte Bibliothek implementiert ist und Ihnen einen erstklassigen Zugriff auf die moderne Windows-API bietet. Informationen zum Erstellen einer Windows-Runtime-Komponente mithilfe von C++/WinRT finden Sie unter Windows-Runtime-Komponenten mit C++/WinRT.

In dieser exemplarischen Vorgehensweise wird veranschaulicht, wie Sie eine einfache Komponenten-DLL-Datei für Windows-Runtime erstellen, die von JavaScript, C# oder Visual Basic aufgerufen werden kann. Bevor Sie mit dieser exemplarischen Vorgehensweise beginnen, stellen Sie sicher, dass Sie Konzepte wie die abstrakte binäre Schnittstelle (ABI), Verweisklassen und Visual C++-Komponentenerweiterungen kennen, die das Verwenden von Verweisklassen vereinfachen. Weitere Informationen finden Sie unter Windows-Runtime Komponenten mit C++/CX und Visual C++-Sprachreferenz (C++/CX).

Erstellen der C++-Komponenten-DLL

In diesem Beispiel erstellen wir zuerst das Komponentenprojekt, aber Sie können zuerst das JavaScript-Projekt erstellen. Die Reihenfolge spielt keine Rolle.

Beachten Sie, dass die Hauptklasse der Komponente Beispiele für Eigenschafts- und Methodendefinitionen und eine Ereignisdeklaration enthält. Diese werden nur bereitgestellt, um Ihnen zu zeigen, wie sie fertig ist. Sie sind nicht erforderlich, und in diesem Beispiel ersetzen wir den gesamten generierten Code durch unseren eigenen Code.

So erstellen Sie das C++-Komponentenprojekt

  1. Wählen Sie auf der Visual Studio-Menüleiste " Datei", "Neu", "Projekt" aus.

  2. Erweitern Sie im Dialogfeld "Neues Projekt" im linken Bereich Visual C++ und wählen Sie dann den Knoten für universelle Windows-Apps aus.

  3. Wählen Sie im mittleren Bereich Windows-Runtime Komponente aus, und benennen Sie dann das Projekt WinRT_CPP.

  4. Klicken Sie auf die Schaltfläche OK .

So fügen Sie der Komponente eine aktivierbare Klasse hinzu

Eine aktivierbare Klasse ist eine Klasse, die Clientcode mithilfe eines neuen Ausdrucks erstellen kann (neu in Visual Basic oder verweis neu in C++). In Ihrer Komponente deklarieren Sie sie als öffentliche Referenzklasse versiegelt. Tatsächlich verfügen die Dateien "Class1.h" und ".cpp" bereits über eine Referenzklasse. Sie können den Namen ändern, aber in diesem Beispiel verwenden wir den Standardnamen "Class1". Sie können zusätzliche Referenzklassen oder reguläre Klassen in Ihrer Komponente definieren, wenn sie erforderlich sind. Weitere Informationen zu Referenzklassen finden Sie unter Type System (C++/CX).For more information about ref classes, see Type System (C++/CX).

Fügen Sie diese #include Direktiven zu Class1.h hinzu:

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

collection.h ist die Headerdatei für C++-konkrete Klassen wie die Platform::Collections::Vector-Klasse und die Platform::Collections::Map-Klasse, die sprachneutrale Schnittstellen implementieren, die durch die Windows-Runtime definiert werden. Die Amp-Header werden verwendet, um Berechnungen auf der GPU auszuführen. Sie haben keine Windows-Runtime Entsprechungen, und das ist in Ordnung, weil sie privat sind. Im Allgemeinen sollten Sie aus Leistungsgründen ISO C++-Code und Standardbibliotheken intern innerhalb der Komponente verwenden; Es ist nur die Windows-Runtime Schnittstelle, die in Windows-Runtime Typen ausgedrückt werden muss.

So fügen Sie einen Delegaten im Namespacebereich hinzu

Ein Delegat ist ein Konstrukt, das die Parameter und den Rückgabetyp für Methoden definiert. Ein Ereignis ist eine Instanz eines bestimmten Delegatentyps, und jede Ereignishandlermethode, die das Ereignis abonniert, muss über die Signatur verfügen, die im Delegaten angegeben ist. Der folgende Code definiert einen Delegattyp, der ein Int akzeptiert und "void" zurückgibt. Als Nächstes deklariert der Code ein öffentliches Ereignis dieses Typs. Dadurch kann Clientcode Methoden bereitstellen, die aufgerufen werden, wenn das Ereignis ausgelöst wird.

Fügen Sie die folgende Delegatdeklaration im Namespacebereich in Class1.h direkt vor der Class1-Deklaration hinzu.

public delegate void PrimeFoundHandler(int result);

Wenn der Code beim Einfügen in Visual Studio nicht ordnungsgemäß angezeigt wird, drücken Sie einfach STRG+K+D, um den Einzug für die gesamte Datei zu beheben.

So fügen Sie die öffentlichen Mitglieder hinzu

Die Klasse macht drei öffentliche Methoden und ein öffentliches Ereignis verfügbar. Die erste Methode ist synchron, da sie immer sehr schnell ausgeführt wird. Da die anderen beiden Methoden einige Zeit in Anspruch nehmen können, sind sie asynchron, sodass sie den UI-Thread nicht blockieren. Diese Methoden geben IAsyncOperationWithProgress und IAsyncActionWithProgress zurück. Der erste definiert eine asynchrone Methode, die ein Ergebnis zurückgibt, und letztere definiert eine asynchrone Methode, die "void" zurückgibt. Diese Schnittstellen ermöglichen auch clientcode den Empfang von Updates für den Fortschritt des Vorgangs.

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;

So fügen Sie die privaten Mitglieder hinzu

Die Klasse enthält drei private Member: zwei Hilfsmethoden für die numerischen Berechnungen und ein CoreDispatcher-Objekt, das zum Marshallen des Ereignisses von Workerthreads zurück in den UI-Thread verwendet wird.

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

So fügen Sie die Header- und Namespacedirektiven hinzu

  1. Fügen Sie in Class1.cpp die folgenden #include Direktiven hinzu:
#include <ppltasks.h>
#include <concurrent_vector.h>
  1. Fügen Sie nun diese using-Anweisungen hinzu, um die erforderlichen Namespaces abzurufen:
using namespace concurrency;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;

So fügen Sie die Implementierung für ComputeResult hinzu

Fügen Sie in Class1.cpp die folgende Methodenimplementierung hinzu. Diese Methode wird synchron im aufrufenden Thread ausgeführt, ist aber sehr schnell, da sie C++ AMP verwendet, um die Berechnung auf der GPU zu parallelisieren. Weitere Informationen finden Sie unter C++ AMP Overview. Die Ergebnisse werden an einen konkreten Platform::Collections::Vector<T-Typ> angefügt, der implizit in einen Windows::Foundation::Collections::IVector<T> konvertiert wird, wenn er zurückgegeben wird.

//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;
}

So fügen Sie die Implementierung für GetPrimesOrdered und die zugehörige Hilfsmethode hinzu

Fügen Sie in Class1.cpp die Implementierungen für GetPrimesOrdered und die is_prime Hilfsmethode hinzu. GetPrimesOrdered verwendet eine concurrent_vector Klasse und eine parallel_for Funktionsschleife, um die Arbeit aufzuteilen und die maximalen Ressourcen des Computers zu verwenden, auf dem das Programm ausgeführt wird, um Ergebnisse zu erzielen. Nachdem die Ergebnisse berechnet, gespeichert und sortiert wurden, werden sie einem Platform::Collections::Vector<T> hinzugefügt und als Windows::Foundation::Collections::IVector<T> an Clientcode zurückgegeben.

Beachten Sie den Code für den Statusbericht, der es dem Client ermöglicht, eine Statusanzeige oder eine andere Benutzeroberfläche zu verbinden, um dem Benutzer anzuzeigen, wie viel länger der Vorgang dauern wird. Die Fortschrittsberichterstattung hat einen Kostenaufwand. Ein Ereignis muss auf der Komponentenseite ausgelöst und im UI-Thread behandelt werden, und der Statuswert muss für jede Iteration gespeichert werden. Eine Möglichkeit, die Kosten zu minimieren, besteht darin, die Häufigkeit zu begrenzen, mit der ein Statusereignis ausgelöst wird. Wenn die Kosten immer noch unertragbar sind oder Sie die Länge des Vorgangs nicht schätzen können, sollten Sie einen Statusring verwenden, der anzeigt, dass ein Vorgang ausgeführt wird, aber die verbleibende Zeit bis zum Abschluss nicht anzeigt.

// 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());
    });
}

So fügen Sie die Implementierung für GetPrimesUnordered hinzu

Der letzte Schritt zum Erstellen der C++-Komponente besteht darin, die Implementierung für "GetPrimesUnordered" in Class1.cpp hinzuzufügen. Diese Methode gibt jedes Ergebnis so zurück, wie es gefunden wird, ohne zu warten, bis alle Ergebnisse gefunden werden. Jedes Ergebnis wird im Ereignishandler zurückgegeben und in Echtzeit auf der Benutzeroberfläche angezeigt. Beachten Sie auch hier, dass ein Fortschrittsreporter verwendet wird. Diese Methode verwendet auch die is_prime Hilfsmethode.

// 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);
    });
}

Erstellen einer JavaScript-Client-App (Visual Studio 2017)

Wenn Sie einen C#-Client erstellen möchten, können Sie diesen Abschnitt überspringen.

Hinweis

Universelle Windows-Plattform (UWP)-Projekte mit JavaScript werden in Visual Studio 2019 nicht unterstützt. Siehe JavaScript und TypeScript in Visual Studio 2019. Es wird empfohlen, Visual Studio 2017 zu verwenden, um diesem Abschnitt zu folgen. Siehe JavaScript in Visual Studio 2017.

So erstellen Sie ein JavaScript-Projekt

  1. Öffnen Sie in Projektmappen-Explorer (in Visual Studio 2017; siehe Hinweis oben) das Kontextmenü für den Projektmappenknoten, und wählen Sie "Hinzufügen", "Neues Projekt" aus.

  2. Erweitern Sie JavaScript (möglicherweise unter "Andere Sprachen" geschachtelt), und wählen Sie "Leere App" (Universelle Windows-App) aus.

  3. Übernehmen Sie den Standardnamen "App1", indem Sie die Schaltfläche "OK " auswählen.

  4. Öffnen Sie das Kontextmenü für den Projektknoten "App1", und wählen Sie "Als Startprojekt festlegen" aus.

  5. Hinzufügen eines Projektverweises zu WinRT_CPP:

  6. Öffnen Sie das Kontextmenü für den Knoten "Verweise", und wählen Sie "Verweis hinzufügen" aus.

  7. Wählen Sie im linken Bereich des Dialogfelds "Verweise-Manager" die Option "Projekte" und dann "Projektmappe" aus.

  8. Wählen Sie im mittleren Bereich WinRT_CPP und dann die Schaltfläche "OK " aus.

So fügen Sie den HTML-Code hinzu, der die JavaScript-Ereignishandler aufruft

Fügen Sie diesen HTML-Code in den <Textkörperknoten> der default.html Seite ein:

<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>

So fügen Sie Formatvorlagen hinzu

Entfernen Sie in default.css das Textkörperformat, und fügen Sie dann die folgenden Formatvorlagen hinzu:

#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;
}

So fügen Sie die JavaScript-Ereignishandler hinzu, die die Komponenten-DLL aufrufen

Fügen Sie die folgenden Funktionen am Ende der default.js Datei hinzu. Diese Funktionen werden aufgerufen, wenn die Schaltflächen auf der Hauptseite ausgewählt werden. Beachten Sie, wie JavaScript die C++-Klasse aktiviert und anschließend seine Methoden aufruft und die Rückgabewerte verwendet, um die HTML-Bezeichnungen aufzufüllen.

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;
}

Fügen Sie Code hinzu, um die Ereignislistener hinzuzufügen, indem Sie den vorhandenen Aufruf von WinJS.UI.processAll in "app.onactivated" in default.js durch den folgenden Code ersetzen, der die Ereignisregistrierung in einem anschließenden Block implementiert. Eine ausführliche Erläuterung hierzu finden Sie unter Erstellen einer "Hello, World"-App (JS).For a detailed explanation of this, see Create a "Hello, World" app (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);
}));

Drücken Sie F5 , um die App auszuführen.

Erstellen einer C#-Client-App

So erstellen Sie ein C#-Projekt

  1. Öffnen Sie in Projektmappen-Explorer das Kontextmenü für den Projektmappenknoten, und wählen Sie dann "Hinzufügen", "Neues Projekt" aus.

  2. Erweitern Sie Visual C# (möglicherweise unter "Andere Sprachen" geschachtelt), wählen Sie "Windows" und dann im linken Bereich "Universal" aus, und wählen Sie dann im mittleren Bereich "Leere App" aus.

  3. Benennen Sie diese App CS_Client, und wählen Sie dann die Schaltfläche "OK " aus.

  4. Öffnen Sie das Kontextmenü für den CS_Client Projektknoten, und wählen Sie "Als Startprojekt festlegen" aus.

  5. Hinzufügen eines Projektverweises zu WinRT_CPP:

    • Öffnen Sie das Kontextmenü für den Knoten "Verweise", und wählen Sie "Verweis hinzufügen" aus.

    • Wählen Sie im linken Bereich des Dialogfelds "Verweise-Manager" die Option "Projekte" und dann "Projektmappe" aus.

    • Wählen Sie im mittleren Bereich WinRT_CPP und dann die Schaltfläche "OK " aus.

So fügen Sie den XAML-Code hinzu, der die Benutzeroberfläche definiert

Kopieren Sie den folgenden Code in das Grid-Element in "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>

So fügen Sie die Ereignishandler für die Schaltflächen hinzu

Öffnen Sie in Projektmappen-Explorer MainPage.xaml.cs. (Die Datei ist möglicherweise unter "MainPage.xaml" geschachtelt.) Fügen Sie eine using-Direktive für System.Text hinzu, und fügen Sie dann den Ereignishandler für die Logarithmusberechnung in der MainPage-Klasse hinzu.

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();
}

Fügen Sie den Ereignishandler für das sortierte Ergebnis hinzu:

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();
}

Fügen Sie den Ereignishandler für das ungeordnete Ergebnis hinzu, und für die Schaltfläche, die die Ergebnisse löscht, damit Sie den Code erneut ausführen können.

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 = "";
}

Ausführen der App

Wählen Sie entweder das C#-Projekt oder das JavaScript-Projekt als Startprojekt aus, indem Sie das Kontextmenü für den Projektknoten in Projektmappen-Explorer öffnen und "Als Startprojekt festlegen" auswählen. Drücken Sie dann F5, um mit dem Debuggen auszuführen, oder STRG+F5, um ohne Debugging auszuführen.

Überprüfen der Komponente im Objektbrowser (optional)

Im Objektkatalog können Sie alle Windows-Runtime Typen prüfen, die in WINMD-Dateien definiert sind. Dazu gehören die Typen im Platform-Namespace und der Standardnamespace. Da die Typen im Namespace "Platform::Collections" jedoch in den Headerdatei "collections.h" und nicht in einer winmd-Datei definiert sind, werden sie nicht im Objektbrowser angezeigt.

So prüfen Sie eine Komponente

  1. Wählen Sie auf der Menüleiste "Ansicht", "Objektkatalog " (STRG+ALT+J) aus.

  2. Erweitern Sie im linken Bereich des Objektkatalogs den knoten WinRT_CPP, um die Typen und Methoden anzuzeigen, die für Ihre Komponente definiert sind.

Tipps zum Debuggen

Laden Sie die Debugsymbole aus den öffentlichen Microsoft-Symbolservern herunter, um eine bessere Debuggingerfahrung zu erzielen:

So laden Sie Debugsymbole herunter

  1. Wählen Sie auf der Menüleiste " Extras", "Optionen" aus.

  2. Erweitern Sie im Dialogfeld "Optionen" das Debuggen, und wählen Sie "Symbole" aus.

  3. Wählen Sie Microsoft-Symbolserver und die Schaltfläche "OK " aus.

Es kann einige Zeit dauern, bis die Symbole zum ersten Mal heruntergeladen werden. Wenn Sie das nächste Mal F5 drücken, geben Sie ein lokales Verzeichnis an, in dem die Symbole zwischengespeichert werden sollen.

Wenn Sie eine JavaScript-Lösung debuggen, die über eine Komponenten-DLL verfügt, können Sie den Debugger so festlegen, dass der Debugger entweder das Schrittweise ausführen oder den systemeigenen Code in der Komponente durchläuft, aber nicht beide gleichzeitig. Um die Einstellung zu ändern, öffnen Sie das Kontextmenü für den JavaScript-Projektknoten in Projektmappen-Explorer, und wählen Sie "Eigenschaften", "Debuggen", "Debuggertyp" aus.

Achten Sie darauf, die entsprechenden Funktionen im Paket-Designer auszuwählen. Sie können den Paket-Designer öffnen, indem Sie die Datei "Package.appxmanifest" öffnen. Wenn Sie beispielsweise versuchen, programmgesteuert auf Dateien im Ordner "Bilder" zuzugreifen, müssen Sie das Kontrollkästchen "Bildbibliothek " im Bereich "Funktionen " des Paket-Designers aktivieren.

Wenn Ihr JavaScript-Code die öffentlichen Eigenschaften oder Methoden in der Komponente nicht erkennt, stellen Sie sicher, dass Sie in JavaScript kamelweise verwenden. Beispielsweise muss auf die ComputeResult C++-Methode wie computeResult in JavaScript verwiesen werden.

Wenn Sie ein C++-Windows-Runtime Komponentenprojekt aus einer Projektmappe entfernen, müssen Sie den Projektverweis auch manuell aus dem JavaScript-Projekt entfernen. Andernfalls wird verhindert, dass nachfolgende Debug- oder Buildvorgänge durchgeführt werden. Bei Bedarf können Sie der DLL einen Assemblyverweis hinzufügen.