Auslösen von Ereignissen in Komponenten für Windows-Runtime

Hinweis

Weitere Informationen zum Auslösen von Ereignissen in einer C++/WinRT-Windows-Runtime Komponente finden Sie unter Author-Ereignisse in C++/WinRT.

Wenn Ihre Windows-Runtime-Komponente ein Ereignis eines benutzerdefinierten Delegatentyps in einem Hintergrundthread (Workerthread) auslöst und JavaScript das Ereignis empfangen soll, können Sie es auf eine dieser Arten implementieren und/oder auslösen.

  • (Option 1) Lösen Sie das Ereignis über windows.UI.Core.CoreDispatcher aus, um das Ereignis im JavaScript-Threadkontext zu marshallen. Obwohl dies in der Regel die beste Option ist, bieten sie in einigen Szenarien möglicherweise nicht die schnellste Leistung.
  • (Option 2) Verwenden Sie das Windows.Foundation.EventHandler-Objekt<> (aber verlieren Sie die Ereignistypinformationen). Wenn Option 1 nicht machbar ist oder die Leistung nicht ausreicht, ist dies eine gute zweite Wahl, vorausgesetzt, dass der Verlust von Typinformationen akzeptabel ist. Wenn Sie eine C#-Windows-Runtime Komponente erstellen, ist der Windows.Foundation.EventHandler-Objekttyp<> nicht verfügbar. Stattdessen wird dieser Typ auf "System.EventHandler" projiziert. Verwenden Sie stattdessen diesen.
  • (Option 3) Erstellen Sie Ihren eigenen Proxy und Stub für die Komponente. Diese Option ist die schwierigste Implementierung, behält jedoch Typinformationen bei und bietet möglicherweise eine bessere Leistung im Vergleich zu Option 1 in anspruchsvollen Szenarien.

Wenn Sie nur ein Ereignis in einem Hintergrundthread auslösen, ohne eine dieser Optionen zu verwenden, empfängt ein JavaScript-Client das Ereignis nicht.

Hintergrund

Alle Windows-Runtime Komponenten und Apps sind grundsätzlich COM-Objekte, unabhängig davon, welche Sprache Sie zum Erstellen verwenden. In der Windows-API sind die meisten Komponenten agile COM-Objekte, die gleichermaßen gut mit Objekten im Hintergrundthread und im UI-Thread kommunizieren können. Wenn ein COM-Objekt nicht agil gemacht werden kann, müssen Hilfsobjekte, die als Proxys und Stubs bezeichnet werden, für die Kommunikation mit anderen COM-Objekten über die Thread-Hintergrund-Grenze der UI erforderlich sein. (In COM-Begriffen wird dies als Kommunikation zwischen Threadwohnungen bezeichnet.)

Die meisten Objekte in der Windows-API sind entweder agil oder verfügen über Proxys und Stubs, die integriert sind. Proxys und Stubs können jedoch nicht für generische Typen wie Windows.Foundation erstellt werden.TypedEventHandler<TSender, TResult> , da sie erst abgeschlossene Typen sind, wenn Sie das Typargument angeben. Es liegt nur bei JavaScript-Clients vor, dass das Fehlen von Proxys oder Stubs zu einem Problem wird. Wenn Ihre Komponente jedoch sowohl aus JavaScript als auch aus C++ oder einer .NET-Sprache verwendet werden soll, müssen Sie eine der folgenden drei Optionen verwenden.

(Option 1) Auslösen des Ereignisses über coreDispatcher

Sie können Ereignisse eines beliebigen benutzerdefinierten Delegattyps mithilfe von Windows.UI.Core.CoreDispatcher senden, und JavaScript kann sie empfangen. Wenn Sie nicht sicher sind, welche Option Sie verwenden möchten, versuchen Sie es zuerst. Wenn die Latenz zwischen dem Auslösen des Ereignisses und der Ereignisbehandlung zu einem Problem wird, versuchen Sie es mit einer der anderen Optionen.

Das folgende Beispiel zeigt, wie Sie mithilfe des CoreDispatcher ein stark typiertes Ereignis auslösen. Beachten Sie, dass das Typargument "Popup" und nicht "Object" ist.

public event EventHandler<Toast> ToastCompletedEvent;
private void OnToastCompleted(Toast args)
{
    var completedEvent = ToastCompletedEvent;
    if (completedEvent != null)
    {
        completedEvent(this, args);
    }
}

public void MakeToastWithDispatcher(string message)
{
    Toast toast = new Toast(message);
    // Assume you have a CoreDispatcher at class scope.
    // Initialize it here, then use it from the background thread.
    var window = Windows.UI.Core.CoreWindow.GetForCurrentThread();
    m_dispatcher = window.Dispatcher;

    Task.Run( () =>
    {
        if (ToastCompletedEvent != null)
        {
            m_dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            new DispatchedHandler(() =>
            {
                this.OnToastCompleted(toast);
            })); // end m_dispatcher.RunAsync
         }
     }); // end Task.Run
}

(Option 2) Verwenden des EventHandler-Objekts<>, aber Verlieren von Typinformationen

Hinweis

Wenn Sie eine C#-Windows-Runtime Komponente erstellen, ist der Windows.Foundation.EventHandler-Objekttyp<> nicht verfügbar. Stattdessen wird dieser Typ auf "System.EventHandler" projiziert. Verwenden Sie stattdessen diesen.

Eine weitere Möglichkeit zum Senden eines Ereignisses aus einem Hintergrundthread ist die Verwendung des Windows.Foundation.EventHandler-Objekts<> als Ereignistyp. Windows stellt diese konkrete Instanziierung des generischen Typs bereit und stellt dafür einen Proxy und stub bereit. Der Nachteil ist, dass die Typinformationen Ihrer Ereignisargumente und des Absenders verloren gegangen sind. C++- und .NET-Clients müssen durch Dokumentation wissen, zu welchem Typ sie zurückkehren sollen, wenn das Ereignis empfangen wird. JavaScript-Clients benötigen keine ursprünglichen Typinformationen. Sie finden die Argeneigenschaften basierend auf ihren Namen in den Metadaten.

In diesem Beispiel wird die Verwendung des Windows.Foundation.EventHandler-Objekts<> in C# veranschaulicht:

public sealed Class1
{
// Declare the event
public event EventHandler<Object> ToastCompletedEvent;

    // Raise the event
    public async void MakeToast(string message)
    {
        Toast toast = new Toast(message);
        // Fire the event from a background thread to allow this thread to continue
        Task.Run(() =>
        {
            if (ToastCompletedEvent != null)
            {
                OnToastCompleted(toast);
            }
        });
    }

    private void OnToastCompleted(Toast args)
    {
        var completedEvent = ToastCompletedEvent;
        if (completedEvent != null)
        {
            completedEvent(this, args);
        }
    }
}

Sie nutzen dieses Ereignis auf der JavaScript-Seite wie folgt:

toastCompletedEventHandler: function (event) {
   var toastType = event.toast.toastType;
   document.getElementById("toasterOutput").innerHTML = "<p>Made " + toastType + " toast</p>";
}

(Option 3) Erstellen Eines eigenen Proxys und Stubs

Für potenzielle Leistungssteigerungen bei benutzerdefinierten Ereignistypen mit vollständig beibehaltenen Typinformationen müssen Sie eigene Proxy- und Stubobjekte erstellen und in Ihr App-Paket einbetten. In der Regel müssen Sie diese Option nur in seltenen Situationen verwenden, in denen keine der beiden anderen Optionen angemessen ist. Außerdem gibt es keine Garantie dafür, dass diese Option eine bessere Leistung bietet als die anderen beiden Optionen. Die tatsächliche Leistung hängt von vielen Faktoren ab. Verwenden Sie den Visual Studio-Profiler oder andere Profilerstellungstools, um die tatsächliche Leistung in Ihrer Anwendung zu messen und zu bestimmen, ob das Ereignis tatsächlich ein Engpass ist.

Im restlichen Artikel wird gezeigt, wie Sie mit C# eine einfache Windows-Runtime Komponente erstellen und dann mit C++ eine DLL für den Proxy und Stub erstellen, mit der JavaScript einen Windows.Foundation.TypedEventHandler<TSender, TResult-Ereignis> verwenden kann, das von der Komponente in einem asynchronen Vorgang ausgelöst wird. (Sie können auch C++ oder Visual Basic verwenden, um die Komponente zu erstellen. Die Schritte zum Erstellen der Proxys und Stubs sind identisch.) Diese exemplarische Vorgehensweise basiert auf dem Erstellen eines Windows-Runtime In-Process-Komponentenbeispiels (C++/CX) und hilft bei der Erläuterung der Zwecke.

Diese exemplarische Vorgehensweise enthält diese Teile.

  • Hier erstellen Sie zwei grundlegende Windows-Runtime Klassen. Eine Klasse macht ein Ereignis vom Typ "Windows.Foundation.TypedEventHandler<TSender", "TResult> " und die andere Klasse als Argument für TValue an JavaScript zurück. Diese Klassen können erst mit JavaScript kommunizieren, wenn Sie die späteren Schritte ausführen.
  • Diese App aktiviert das Hauptklassenobjekt, ruft eine Methode auf und behandelt ein Ereignis, das von der Windows-Runtime Komponente ausgelöst wird.
  • Diese werden von den Tools benötigt, die die Proxy- und Stubklassen generieren.
  • Anschließend verwenden Sie die IDL-Datei, um den C-Quellcode für den Proxy und den Stub zu generieren.
  • Registrieren Sie die Proxy-Stubobjekte, damit sie von der COM-Laufzeit gefunden werden können, und verweisen Sie im App-Projekt auf die Proxy-Stub-DLL.

So erstellen Sie die komponente Windows-Runtime

Wählen Sie in Visual Studio auf der Menüleiste "Neues Projekt" > aus. Erweitern Sie im Dialogfeld "Neues Projekt" javaScript > Universal Windows, und wählen Sie dann "Leere App" aus. Benennen Sie das Projekt ToasterApplication, und wählen Sie dann die Schaltfläche "OK " aus.

Fügen Sie der Projektmappe eine C#-Windows-Runtime Komponente hinzu: Öffnen Sie in Projektmappen-Explorer das Kontextmenü für die Projektmappe, und wählen Sie dann "Neues Projekt hinzufügen" > aus. Erweitern Sie visual C# > Microsoft Store, und wählen Sie dann Windows-Runtime Komponente aus. Benennen Sie das Projekt ToasterComponent, und wählen Sie dann die Schaltfläche "OK " aus. ToasterComponent ist der Stammnamespace für die Komponenten, die Sie in späteren Schritten erstellen.

Öffnen Sie in Projektmappen-Explorer das Kontextmenü für die Lösung, und wählen Sie dann "Eigenschaften" aus. Wählen Sie im Dialogfeld "Eigenschaftenseiten" im linken Bereich die Option "Konfigurationseigenschaften" und dann oben im Dialogfeld "Konfiguration auf Debuggen und Plattform" auf "x86", "x64" oder "ARM" aus. Klicken Sie auf die Schaltfläche OK .

Wichtige Plattform = Jede CPU funktioniert nicht, da sie für die win32-DLL mit systemeigenem Code, die Sie später zur Lösung hinzufügen, ungültig ist.

Benennen Sie in Projektmappen-Explorer class1.cs in ToasterComponent.cs um, sodass sie dem Namen des Projekts entspricht. Visual Studio benennt die Klasse in der Datei automatisch so um, dass sie mit dem neuen Dateinamen übereinstimmt.

Fügen Sie in der datei .cs eine using-Direktive für den Windows.Foundation-Namespace hinzu, um TypedEventHandler in den Bereich zu bringen.

Wenn Sie Proxys und Stubs benötigen, muss Ihre Komponente Schnittstellen verwenden, um die öffentlichen Member verfügbar zu machen. Definieren Sie in ToasterComponent.cs eine Schnittstelle für den Toaster und ein weiteres für das Popup, das der Toaster erzeugt.

Hinweis In C# können Sie diesen Schritt überspringen. Erstellen Sie stattdessen zuerst eine Klasse, und öffnen Sie dann das Kontextmenü, und wählen Sie "Benutzeroberfläche umgestalten>" aus. Geben Sie im generierten Code den Schnittstellen die öffentliche Barrierefreiheit manuell.

	public interface IToaster
        {
            void MakeToast(String message);
            event TypedEventHandler<Toaster, Toast> ToastCompletedEvent;

        }
        public interface IToast
        {
            String ToastType { get; }
        }

Die IToast-Schnittstelle verfügt über eine Zeichenfolge, die abgerufen werden kann, um den Typ des Popups zu beschreiben. Die IToaster-Schnittstelle verfügt über eine Methode zum Erstellen eines Popups und ein Ereignis, das angibt, dass das Popup erstellt wird. Da dieses Ereignis den bestimmten Teil (d. h. Typ) des Popups zurückgibt, wird es als typiertes Ereignis bezeichnet.

Als Nächstes benötigen wir Klassen, die diese Schnittstellen implementieren, und sind öffentlich und versiegelt, damit sie über die JavaScript-App zugänglich sind, die Sie später programmieren werden.

	public sealed class Toast : IToast
        {
            private string _toastType;

            public string ToastType
            {
                get
                {
                    return _toastType;
                }
            }
            internal Toast(String toastType)
            {
                _toastType = toastType;
            }

        }
        public sealed class Toaster : IToaster
        {
            public event TypedEventHandler<Toaster, Toast> ToastCompletedEvent;

            private void OnToastCompleted(Toast args)
            {
                var completedEvent = ToastCompletedEvent;
                if (completedEvent != null)
                {
                    completedEvent(this, args);
                }
            }

            public void MakeToast(string message)
            {
                Toast toast = new Toast(message);
                // Fire the event from a thread-pool thread to enable this thread to continue
                Windows.System.Threading.ThreadPool.RunAsync(
                (IAsyncAction action) =>
                {
                    if (ToastCompletedEvent != null)
                    {
                        OnToastCompleted(toast);
                    }
                });
           }
        }

Im vorherigen Code erstellen wir das Popup und drehen dann eine Arbeitsaufgabe im Threadpool, um die Benachrichtigung auszulöschen. Obwohl die IDE vorschlagen könnte, dass Sie das Await-Schlüsselwort auf den asynchronen Aufruf anwenden, ist es in diesem Fall nicht erforderlich, da die Methode keine Arbeit macht, die von den Ergebnissen des Vorgangs abhängt.

Beachten Sie: Der asynchrone Aufruf im vorherigen Code verwendet ThreadPool.RunAsync ausschließlich, um eine einfache Möglichkeit zum Auslösen des Ereignisses in einem Hintergrundthread zu veranschaulichen. Sie könnten diese bestimmte Methode schreiben, wie im folgenden Beispiel gezeigt, und es würde einwandfrei funktionieren, da der .NET Task scheduler automatisch async/await-Aufrufe zurück an den UI-Thread marshallt.  

	public async void MakeToast(string message)
    {
        Toast toast = new Toast(message)
        await Task.Delay(new Random().Next(1000));
        OnToastCompleted(toast);
    }

Wenn Sie das Projekt jetzt erstellen, sollte es sauber erstellt werden.

So programmieren Sie die JavaScript-App

Jetzt können wir der JavaScript-App eine Schaltfläche hinzufügen, damit sie die klasse verwendet, die wir soeben definiert haben, um Popups zu erstellen. Bevor wir dies tun, müssen wir einen Verweis auf das soeben erstellte ToasterComponent-Projekt hinzufügen. Öffnen Sie in Projektmappen-Explorer das Kontextmenü für das ToasterApplication-Projekt, wählen Sie "Verweise hinzufügen>" und dann die Schaltfläche "Neuen Verweis hinzufügen" aus. Wählen Sie im Dialogfeld "Verweis hinzufügen" im linken Bereich unter "Projektmappe" das Komponentenprojekt und dann im mittleren Bereich "ToasterComponent" aus. Klicken Sie auf die Schaltfläche OK .

Öffnen Sie in Projektmappen-Explorer das Kontextmenü für das ToasterApplication-Projekt, und wählen Sie dann "Als Startprojekt festlegen" aus.

Fügen Sie am Ende der default.js-Datei einen Namespace hinzu, der die Funktionen enthält, um die Komponente aufzurufen und von ihr wieder aufgerufen zu werden. Der Namespace verfügt über zwei Funktionen, eine zum Erstellen eines Popups und eine zum Behandeln des Popupereignisses. Die Implementierung von makeToast erstellt ein Toaster-Objekt, registriert den Ereignishandler und macht das Popup. Bisher tut der Ereignishandler nicht viel, wie hier gezeigt:

	WinJS.Namespace.define("ToasterApplication"), {
       makeToast: function () {

          var toaster = new ToasterComponent.Toaster();
          //toaster.addEventListener("ontoastcompletedevent", ToasterApplication.toastCompletedEventHandler);
          toaster.ontoastcompletedevent = ToasterApplication.toastCompletedEventHandler;
          toaster.makeToast("Peanut Butter");
       },

       toastCompletedEventHandler: function(event) {
           // The sender of the event (the delegate's first type parameter)
           // is mapped to event.target. The second argument of the delegate
           // is contained in event, which means in this case event is a
           // Toast class, with a toastType string.
           var toastType = event.toastType;

           document.getElementById('toastOutput').innerHTML = "<p>Made " + toastType + " toast</p>";
        },
    });

Die MakeToast-Funktion muss mit einer Schaltfläche eingebunden werden. Aktualisieren Sie default.html so, dass sie eine Schaltfläche und etwas Platz zum Ausgeben des Ergebnisses des Popups enthalten:

    <body>
        <h1>Click the button to make toast</h1>
        <button onclick="ToasterApplication.makeToast()">Make Toast!</button>
        <div id="toasterOutput">
            <p>No Toast Yet...</p>
        </div>
    </body>

Wenn wir keinen TypedEventHandler verwenden, können wir die App jetzt auf dem lokalen Computer ausführen und auf die Schaltfläche klicken, um Popups zu erstellen. Aber in unserer App passiert nichts. Um herauszufinden, warum, debuggen wir den verwalteten Code, der das ToastCompletedEvent ausgelöst. Beenden Sie das Projekt, und wählen Sie dann auf der Menüleiste "Debug > Toaster Application"-Eigenschaften aus. Ändern Sie den Debuggertyp in "Nur verwaltet". Wählen Sie erneut auf der Menüleiste "Debug > Exceptions" und dann "Common Language Runtime Exceptions" aus.

Führen Sie nun die App aus, und klicken Sie auf die Schaltfläche "Popup". Der Debugger fängt eine ungültige Cast-Ausnahme ab. Obwohl die Meldung nicht offensichtlich ist, tritt diese Ausnahme auf, da Proxys für diese Schnittstelle fehlen.

Fehlender Proxy

Der erste Schritt beim Erstellen eines Proxys und Stubs für eine Komponente ist das Hinzufügen einer eindeutigen ID oder GUID zu den Schnittstellen. Das zu verwendende GUID-Format unterscheidet sich jedoch je nach Codierung in C#, Visual Basic oder einer anderen .NET-Sprache oder in C++.

So generieren Sie GUIDs für die Schnittstellen der Komponente (C# und andere .NET-Sprachen)

Wählen Sie auf der Menüleiste "Tools > create GUID" aus. Wählen Sie im Dialogfeld "5" aus. [Guid("xxxxxxxx-xxxx... xxxx")]. Wählen Sie die Schaltfläche "Neue GUID" und dann die Schaltfläche "Kopieren" aus.

GUID-Generatortool

Wechseln Sie zurück zur Schnittstellendefinition, und fügen Sie dann die neue GUID direkt vor der IToaster-Schnittstelle ein, wie im folgenden Beispiel gezeigt. (Verwenden Sie die GUID im Beispiel nicht. Jede eindeutige Schnittstelle sollte über eine eigene GUID verfügen.)

[Guid("FC198F74-A808-4E2A-9255-264746965B9F")]
        public interface IToaster...

Fügen Sie eine using-Direktive für den System.Runtime.InteropServices-Namespace hinzu.

Wiederholen Sie diese Schritte für die IToast-Schnittstelle.

So generieren Sie GUIDs für die Schnittstellen der Komponente (C++)

Wählen Sie auf der Menüleiste "Tools > create GUID" aus. Wählen Sie im Dialogfeld "3" aus. Statische Struktur-GUID = {...}. Wählen Sie die Schaltfläche "Neue GUID" und dann die Schaltfläche "Kopieren" aus.

Fügen Sie die GUID direkt vor der IToaster-Schnittstellendefinition ein. Nach dem Einfügen sollte die GUID dem folgenden Beispiel ähneln. (Verwenden Sie die GUID im Beispiel nicht. Jede eindeutige Schnittstelle sollte über eine eigene GUID verfügen.)

// {F8D30778-9EAF-409C-BCCD-C8B24442B09B}
    static const GUID <<name>> = { 0xf8d30778, 0x9eaf, 0x409c, { 0xbc, 0xcd, 0xc8, 0xb2, 0x44, 0x42, 0xb0, 0x9b } };

Fügen Sie eine using-Direktive für Windows.Foundation.Metadata hinzu, um GuidAttribute in den Bereich zu bringen.

Konvertieren Sie nun die Const-GUID manuell in ein GuidAttribute, sodass sie wie im folgenden Beispiel dargestellt formatiert ist. Beachten Sie, dass die geschweiften Klammern durch Klammern und Klammern ersetzt werden, und das nachfolgende Semikolon wird entfernt.

// {E976784C-AADE-4EA4-A4C0-B0C2FD1307C3}
    [GuidAttribute(0xe976784c, 0xaade, 0x4ea4, 0xa4, 0xc0, 0xb0, 0xc2, 0xfd, 0x13, 0x7, 0xc3)]
    public interface IToaster
    {...

Wiederholen Sie diese Schritte für die IToast-Schnittstelle.

Nachdem die Schnittstellen über eindeutige IDs verfügen, können wir eine IDL-Datei erstellen, indem sie die WINMD-Datei in das Befehlszeilentool winmdidl einspeist und dann den C-Quellcode für den Proxy und stub generiert, indem sie diese IDL-Datei in das BEFEHLSZEILENtool MIDL einspeist. Visual Studio tut dies für uns, wenn wir Postbuildereignisse erstellen, wie in den folgenden Schritten gezeigt.

So generieren Sie den Proxy- und Stub-Quellcode

Um ein benutzerdefiniertes Postbuildereignis hinzuzufügen, öffnen Sie in Projektmappen-Explorer das Kontextmenü für das ToasterComponent-Projekt, und wählen Sie dann "Eigenschaften" aus. Wählen Sie im linken Bereich der Eigenschaftenseiten "Buildereignisse" und dann die Schaltfläche "Postbuild bearbeiten" aus. Fügen Sie der Befehlszeile nach dem Build die folgenden Befehle hinzu. (Die Batchdatei muss zuerst aufgerufen werden, um die Umgebungsvariablen so festzulegen, dass das winmdidl-Tool gefunden wird.)

call "$(DevEnvDir)..\..\vc\vcvarsall.bat" $(PlatformName)
winmdidl /outdir:output "$(TargetPath)"
midl /metadata_dir "%WindowsSdkDir%References\CommonConfiguration\Neutral" /iid "$(ProjectDir)$(TargetName)_i.c" /env win32 /h "$(ProjectDir)$(TargetName).h" /winmd "Output\$(TargetName).winmd" /W1 /char signed /nologo /winrt /dlldata "$(ProjectDir)dlldata.c" /proxy "$(ProjectDir)$(TargetName)_p.c" "Output\$(TargetName).idl"

Wichtig für eine ARM- oder x64-Projektkonfiguration, ändern Sie den MIDL /env-Parameter in x64 oder arm32.

Um sicherzustellen, dass die IDL-Datei jedes Mal neu generiert wird, wenn die WINMD-Datei geändert wird, ändern Sie das Ereignis "Ausführen" in "Ausführen", wenn der Build die Projektausgabe aktualisiert. Die Eigenschaftenseite "Buildereignisse" sollte wie folgt aussehen:Buildereignisse

Erstellen Sie die Lösung neu, um die IDL zu generieren und zu kompilieren.

Sie können überprüfen, ob MIDL die Lösung ordnungsgemäß kompiliert hat, indem Sie im Projektverzeichnis ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c und dlldata.c suchen.

So kompilieren Sie den Proxy- und Stubcode in eine DLL

Nachdem Sie nun über die erforderlichen Dateien verfügen, können Sie sie kompilieren, um eine DLL zu erstellen, bei der es sich um eine C++-Datei handelt. Um dies so einfach wie möglich zu machen, fügen Sie ein neues Projekt hinzu, um die Erstellung der Proxys zu unterstützen. Öffnen Sie das Kontextmenü für die ToasterApplication-Lösung, und wählen Sie dann "Neues Projekt hinzufügen" > aus. Erweitern Sie im linken Bereich des Dialogfelds "Neues Projekt" visual C++ > Windows Universal Windows, > und wählen Sie dann im mittleren Bereich DLL (UWP-Apps) aus. (Beachten Sie, dass dies KEIN C++-Windows-Runtime Component-Projekt ist.) Benennen Sie die Projektproxys, und wählen Sie dann die Schaltfläche "OK" aus. Diese Dateien werden von den Postbuildereignissen aktualisiert, wenn sich etwas in der C#-Klasse ändert.

Standardmäßig generiert das Proxies-Projekt Header-H-Dateien und C++-.cpp Dateien. Da die DLL aus den dateien erstellt wird, die aus MIDL erstellt werden, sind die H- und .cpp Dateien nicht erforderlich. Öffnen Sie in Projektmappen-Explorer das Kontextmenü für sie, wählen Sie "Entfernen" aus, und bestätigen Sie dann den Löschvorgang.

Nachdem das Projekt leer ist, können Sie die von MIDL generierten Dateien wieder hinzufügen. Öffnen Sie das Kontextmenü für das Proxies-Projekt, und wählen Sie dann "Vorhandenes Element hinzufügen" > aus. Navigieren Sie im Dialogfeld zum ToasterComponent-Projektverzeichnis, und wählen Sie die folgenden Dateien aus: ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c- und dlldata.c-Dateien. Wählen Sie die Schaltfläche Hinzufügen aus.

Erstellen Sie im Proxies-Projekt eine DEF-Datei, um die in dlldata.c beschriebenen DLL-Exporte zu definieren. Öffnen Sie das Kontextmenü für das Projekt, und wählen Sie dann "Neues Element hinzufügen" > aus. Wählen Sie im linken Bereich des Dialogfelds "Code" und dann im mittleren Bereich "Moduldefinitionsdatei" aus. Benennen Sie die Datei "proxies.def", und wählen Sie dann die Schaltfläche "Hinzufügen " aus. Öffnen Sie diese DEF-Datei, und ändern Sie sie so, dass sie die EXPORTe enthält, die in dlldata.c definiert sind:

EXPORTS
    DllCanUnloadNow         PRIVATE
    DllGetClassObject       PRIVATE

Wenn Sie das Projekt jetzt erstellen, schlägt es fehl. Um dieses Projekt ordnungsgemäß zu kompilieren, müssen Sie ändern, wie das Projekt kompiliert und verknüpft wird. Öffnen Sie in Projektmappen-Explorer das Kontextmenü für das Proxies-Projekt, und wählen Sie dann "Eigenschaften" aus. Ändern Sie die Eigenschaftenseiten wie folgt.

Wählen Sie im linken Bereich C/C++>-Präprozessor aus, und wählen Sie dann im rechten Bereich Präprozessordefinitionen aus, wählen Sie die Nach-unten-Taste und dann "Bearbeiten" aus. Fügen Sie diese Definitionen in das Feld ein:

WIN32;_WINDOWS

Ändern Sie unter "Vorkompilierte Kopfzeilen" unter "Vorkompilierte > Kopfzeilen" in "Nicht vorkompilierte Kopfzeilen verwenden", und wählen Sie dann die Schaltfläche "Übernehmen" aus.

Ändern Sie unter "Linker > Allgemein" die Option "Importbibliothek ignorieren" in Yes, und klicken Sie dann auf die Schaltfläche "Übernehmen".

Wählen Sie unter "Linker-Eingabe>" die Option "Weitere Abhängigkeiten" aus, klicken Sie auf die Schaltfläche "Nach unten", und wählen Sie dann "Bearbeiten" aus. Fügen Sie diesen Text in das Feld ein:

rpcrt4.lib;runtimeobject.lib

Fügen Sie diese Libs nicht direkt in die Listenzeile ein. Verwenden Sie das Feld "Bearbeiten ", um sicherzustellen, dass MSBuild in Visual Studio die richtigen zusätzlichen Abhängigkeiten verwaltet.

Wenn Sie diese Änderungen vorgenommen haben, wählen Sie im Dialogfeld "Eigenschaftenseiten" die Schaltfläche "OK" aus.

Nehmen Sie als Nächstes eine Abhängigkeit vom ToasterComponent-Projekt. Dadurch wird sichergestellt, dass der Toaster vor dem Proxyprojekt erstellt wird. Dies ist erforderlich, da das Toaster-Projekt dafür verantwortlich ist, die Dateien zum Erstellen des Proxys zu generieren.

Öffnen Sie das Kontextmenü für das Proxies-Projekt, und wählen Sie dann "Projektabhängigkeiten" aus. Aktivieren Sie die Kontrollkästchen, um anzugeben, dass das Proxies-Projekt vom ToasterComponent-Projekt abhängt, um sicherzustellen, dass Visual Studio sie in der richtigen Reihenfolge erstellt.

Vergewissern Sie sich, dass die Projektmappe ordnungsgemäß erstellt wird, indem Sie in der Menüleiste von Visual Studio die Option "Projektmappe > neu erstellen" auswählen.

So registrieren Sie den Proxy und den Stub

Öffnen Sie im ToasterApplication-Projekt das Kontextmenü für "package.appxmanifest", und wählen Sie dann "Öffnen mit" aus. Wählen Sie im Dialogfeld "Öffnen mit" den XML-Text-Editor und dann die Schaltfläche "OK " aus. Wir werden in einigen XML-Code einfügen, der eine Windows.activatableClass.proxyStub-Erweiterungsregistrierung bereitstellt und die auf den GUIDs im Proxy basieren. Öffnen Sie ToasterComponent_i.c, um die GUIDs zu finden, die in der APPXMANIFEST-Datei verwendet werden sollen. Suchen Sie Einträge, die denen im folgenden Beispiel ähneln. Beachten Sie außerdem die Definitionen für IToast, IToaster und eine dritte Schnittstelle – einen typierten Ereignishandler mit zwei Parametern: einen Toaster und ein Popup. Dies entspricht dem Ereignis, das in der Toaster-Klasse definiert ist. Beachten Sie, dass die GUIDs für IToast und IToaster mit den GUIDs übereinstimmen, die für die Schnittstellen in der C#-Datei definiert sind. Da die typierte Ereignishandlerschnittstelle automatisch generiert wird, wird auch die GUID für diese Schnittstelle automatisch generiert.

MIDL_DEFINE_GUID(IID, IID___FITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast,0x1ecafeff,0x1ee1,0x504a,0x9a,0xf5,0xa6,0x8c,0x6f,0xb2,0xb4,0x7d);

MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToast,0xF8D30778,0x9EAF,0x409C,0xBC,0xCD,0xC8,0xB2,0x44,0x42,0xB0,0x9B);

MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToaster,0xE976784C,0xAADE,0x4EA4,0xA4,0xC0,0xB0,0xC2,0xFD,0x13,0x07,0xC3);

Nun kopieren wir die GUIDs, fügen sie in "package.appxmanifest" in einen Knoten ein, den wir hinzufügen und Erweiterungen benennen, und formatieren sie dann neu. Der Manifesteintrag ähnelt dem folgenden Beispiel. Denken Sie jedoch erneut daran, ihre eigenen GUIDs zu verwenden. Beachten Sie, dass die ClassId-GUID im XML-Code mit ITypedEventHandler2 identisch ist. Dies liegt daran, dass diese GUID der erste ist, der in ToasterComponent_i.c aufgeführt ist. Bei den hier aufgeführten GUIDs wird die Groß-/Kleinschreibung nicht beachtet. Anstatt die GUIDs für IToast und IToaster manuell neu zu formatieren, können Sie wieder zu den Schnittstellendefinitionen zurückkehren und den GuidAttribute-Wert abrufen, der das richtige Format aufweist. In C++ gibt es eine korrekt formatierte GUID im Kommentar. In jedem Fall müssen Sie die GUID manuell neu formatieren, die sowohl für die ClassId als auch für den Ereignishandler verwendet wird.

	  <Extensions> <!--Use your own GUIDs!!!-->
        <Extension Category="windows.activatableClass.proxyStub">
          <ProxyStub ClassId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d">
            <Path>Proxies.dll</Path>
            <Interface Name="IToast" InterfaceId="F8D30778-9EAF-409C-BCCD-C8B24442B09B"/>
            <Interface Name="IToaster"  InterfaceId="E976784C-AADE-4EA4-A4C0-B0C2FD1307C3"/>  
            <Interface Name="ITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast" InterfaceId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d"/>
          </ProxyStub>      
        </Extension>
      </Extensions>

Fügen Sie den Erweiterungs-XML-Knoten als direktes untergeordnetes Element des Paketknotens und einen Peer des Knotens "Ressourcen" ein.

Bevor Sie fortfahren, ist es wichtig, folgendes zu gewährleisten:

  • Die ProxyStub ClassId wird auf die erste GUID in der Datei ToasterComponent_i.c festgelegt. Verwenden Sie die erste GUID, die in dieser Datei für die classId definiert ist. (Dies kann mit der GUID für ITypedEventHandler2 identisch sein.)
  • Der Pfad ist der relative Paketpfad der Proxy-Binärdatei. (In dieser exemplarischen Vorgehensweise befindet sich proxies.dll im selben Ordner wie ToasterApplication.winmd.)
  • Die GUIDs befinden sich im richtigen Format. (Dies ist leicht falsch.)
  • Die Schnittstellen-IDs im Manifest entsprechen den IIDs in ToasterComponent_i.c-Datei.
  • Die Schnittstellennamen sind im Manifest eindeutig. Da diese vom System nicht verwendet werden, können Sie die Werte auswählen. Es empfiehlt sich, Schnittstellennamen auszuwählen, die eindeutig mit Schnittstellen übereinstimmen, die Sie definiert haben. Bei generierten Schnittstellen sollten die Namen indikativ für die generierten Schnittstellen sein. Sie können die Datei ToasterComponent_i.c verwenden, um Schnittstellennamen zu generieren.

Wenn Sie versuchen, die Lösung jetzt auszuführen, wird eine Fehlermeldung angezeigt, dass proxies.dll nicht Teil der Nutzlast ist. Öffnen Sie das Kontextmenü für den Ordner "Verweise " im ToasterApplication-Projekt, und wählen Sie dann "Verweis hinzufügen" aus. Aktivieren Sie das Kontrollkästchen neben dem Proxies-Projekt. Stellen Sie außerdem sicher, dass das Kontrollkästchen neben ToasterComponent ebenfalls aktiviert ist. Klicken Sie auf die Schaltfläche OK .

Das Projekt sollte jetzt erstellt werden. Führen Sie das Projekt aus, und stellen Sie sicher, dass Sie Popups erstellen können.