Freigeben über


Auslösen von Ereignissen in Windows-Runtime-Komponenten

Wenn die Komponente für Windows-Runtime ein Ereignis eines benutzerdefinierten Delegattyps in einem Hintergrundthread (Arbeitsthread) auslöst und JavaScript in der Lage sein soll, das Ereignis zu empfangen, können Sie es auf eine der folgenden Weisen implementieren und/oder auslösen:

  1. (Option 1) Lösen Sie das Ereignis mit dem CoreDispatcher aus, um das Ereignis zum JavaScript-Threadkontext zu marshallen. Dies in der Regel zwar die beste, in manchen Szenarien aber möglicherweise nicht die schnellste Option.

  2. (Option 2) Verwenden Sie „Windows.Foundation.EventHandler<Objekt>“ (jedoch mit Verlust von Ereignistypinformationen). Ist Option 1 nicht möglich oder die Leistung nicht ausreichend, bietet dies eine gute Alternative, wenn der Verlust von Typinformationen akzeptabel ist.

  3. (Option 3) Erstellen Sie eigene Proxy- und Stub-COM-Objekte für die Komponente. Diese Option ist am schwierigsten zu implementieren, behält aber die Typinformationen bei und gewährleistet im Vergleich zu Option 1 eine bessere Leistung in anspruchsvollen Szenarien.

Sollten Sie lediglich ein Ereignis in einem Hintergrundthread auslösen, ohne eine dieser Optionen zu verwenden, wird das Ereignis vom JavaScript-Client nicht empfangen.

Hintergrund

Bei allen Komponenten und Apps für Windows-Runtime handelt es sich im Grunde um COM-Objekte, unabhängig von der bei ihrer Erstellung verwendeten Sprache. In der Windows-API sind die meisten Komponenten agile COM-Objekte, die mit Objekten im Hintergrundthread und im UI-Thread gleichermaßen gut kommunizieren können. Sollte ein COM-Objekt nicht agil gemacht werden können, sind Hilfsobjekte (auch als Proxys bzw. Stubs bezeichnet) erforderlich, um mit anderen COM-Objekten über die Threadgrenze des UI-Threadhintergrunds hinweg zu kommunizieren. (In Bezug auf COM wird dies als Kommunikation zwischen Threadapartments bezeichnet.)

Die meisten Objekte in der Windows-API sind entweder agil oder weisen integrierte Proxys und Stubs auf. Allerdings können Proxys und Stubs nicht für generische Typen z. B. Windows.Foundation.TypedEventHandler<TSender, TResult> erstellt werden, da es keine vollständigen Typen sind, bis das Typargument bereitgestellt wird. Fehlende Proxys oder Stubs stellen nur bei JavaScript-Clients ein Problem dar. Soll die Komponente aber sowohl in JavaScript als auch in C++ oder einer .NET-Sprache verwendet werden können, müssen Sie eine der drei folgenden Optionen verwenden.

(Option 1) Lösen Sie das Ereignis mit dem CoreDispatcher aus

Sie können Ereignisse eines benutzerdefinierten Delegattyps mithilfe von Windows.UI.Core.CoreDispatcher senden, und JavaScript kann diese empfangen. Wenn Sie sich nicht sicher sind, welche Option Sie verwenden sollen, versuchen Sie es zunächst mit dieser ersten Option. Sollte die Wartezeit zwischen den Auslösen von Ereignissen und der Ereignisbehandlung ein Problem darstellen, versuchen Sie es mit einer der anderen Optionen.

Im folgenden Beispiel wird erläutert, wie der CoreDispatcher verwendet wird, um ein stark typisiertes Ereignis auszulösen. Beachten Sie, dass es sich bei dem Typargument um Toast handelt, und nicht um Object.


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 Sie EventHandler<Object> (jedoch mit Verlust von Typinformationen)

Eine weitere Möglichkeit zum Senden eines Ereignisses aus einem Hintergrundthread besteht in der Verwendung von Windows.Foundation.EventHandler<Object> als Typ des Ereignisses. Windows bietet diese konkrete generische Typinstanziierung und stellt hierzu einen Proxy und einen Stub bereit. Der Nachteil daran ist, dass die Typinformationen von Ereignisargumenten und -sender verloren gehen. C++- und .NET-Clients müssen anhand der Dokumentation wissen, welcher Typ in den ursprünglichen Typ umgewandelt werden muss, wenn das Ereignis empfangen wird. Bei JavaScript-Clients sind keine Informationen zu ursprünglichen Typen erforderlich. Sie finden die Argumenteigenschaften auf Grundlage ihrer Namen in den Metadaten.

In diesem Beispiel wird die Verwendung von Windows.Foundation.EventHandler<Object> 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 verwenden 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 Sie einen eigenen Proxy und Stub

Um bei benutzerdefinierten Ereignistypen mit gänzlich beibehaltenen Typinformationen potenzielle Leistungszuwächse zu erreichen, müssen Sie eigene Proxy- und Stub-Objekte erstellen und diese im App-Paket einbetten. In der Regel findet diese Option nur in seltenen Fällen Anwendung, in denen keine der beiden anderen Optionen geeignet ist. Außerdem ist nicht gewährleistet, dass mit dieser Option eine bessere Leistung erzielt wird als mit den 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 der Anwendung zu messen und um zu identifizieren, ob das Ereignis tatsächlich einen Engpass darstellt.

Im weiteren Verlauf dieses Artikels wird die Erstellung einer grundlegenden Komponente für Windows-Runtime in C# veranschaulicht. Anschließend wird gezeigt, wie eine DLL für den Proxy und Stub in C++ erstellt wird, sodass JavaScript ein Windows.Foundation.TypedEventHandler<TSender, TResult>-Ereignis nutzen kann, das von der Komponente in einem asynchronen Vorgang ausgelöst wird. (Sie können die Komponente auch in C++ oder Visual Basic erstellen. Die Schritte, die mit dem Erstellen der Proxys und Stubs verbunden sind, sind identisch.) Diese exemplarische Vorgehensweise basiert auf Erstellen einer prozessinternen Komponente für Windows-Runtime (C++/CX) – Beispiel und hilft bei der Erläuterung der Zielsetzungen.

Diese exemplarische Vorgehensweise besteht auf folgenden Teilen:

  1. Erstellen der Komponente für Windows-Runtime: In diesem Fall erstellen Sie zwei grundlegende Windows-Runtime-Klassen. Die eine Klasse macht ein Ereignis vom Typ Windows.Foundation.TypedEventHandler<TSender, TResult> verfügbar, die andere Klasse ist der Typ, der als Argument für TValue an JavaScript zurückgegeben wird. Diese Klassen können erst mit JavaScript kommunizieren, wenn Sie die nachfolgenden Schritte ausführen.

  2. Programmieren der JavaScript-App: Diese App aktiviert das Hauptklasseobjekt, ruft eine Methode auf und behandelt ein Ereignis, das von der Komponente für Windows-Runtime ausgelöst wird.

  3. Generieren von GUIDs für die Schnittstellen der Komponente: Diese werden von den Tools zum Generieren der Proxy- und Stubklassen benötigt.

  4. Generieren einer IDL-Datei für die Komponente: Anschließend wird die IDL-Datei verwendet, um den C-Quellcode für den Proxy und Stub zu generieren.

  5. Kompilieren von Proxy- und Stubcode in eine DLL

  6. Registrieren und Verwenden der Proxy-Stub-DLL: Registrieren Sie die Proxy-Stub-Objekte, damit die COM-Laufzeit sie finden kann, und verweisen Sie im App-Projekt auf die Proxy-Stub-DLL.

So erstellen Sie die Komponente für Windows-Runtime

  1. Wählen Sie in der Visual Studio-Menüleiste den Menüpunkt Datei > Neues Projekt aus. Erweitern Sie im Dialogfeld Neues Projekt die Option JavaScript > Windows Store, und wählen Sie dann Leere App aus. Geben Sie ToasterApplication als Projektnamen ein, und wählen Sie dann die Schaltfläche OK aus.

  2. Fügen Sie eine Komponente für Windows-Runtime in C# zur Projektmappe hinzu: Öffnen Sie in Projektmappen-Explorer das Kontextmenü für die Projektmappe, und wählen Sie dann Hinzufügen > Neues Projekt aus. Erweitern Sie Visual C# > Windows Store, und wählen Sie dann Komponente für Windows-Runtime aus. Geben Sie ToasterComponent als Projektnamen ein, 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 im Projektmappen-Explorer das Kontextmenü für die Projektmappe, und wählen Sie dann Eigenschaften aus. Wählen Sie im Dialogfeld Eigenschaftenseiten links Konfigurationseigenschaften aus, und legen Sie dann oben im Dialogfeld die Konfiguration auf Debuggen und die Plattform auf x86, x64 oder ARM fest. Klicken Sie auf OK.

    Wichtig

    Plattform = Any CPU funktioniert nicht, da dies für die Win32-DLL in systemeigenem Code, die Sie später der Projektmappe hinzufügen, ungültig ist.

  3. In Projektmappen-Explorer benennen Sie „class1.cs“ in „ToasterComponent.cs“ um, sodass es dem Namen des Projekts entspricht. Entsprechend dem neuen Dateinamen benennt Visual Studio die Klasse in der Datei automatisch um.

  4. In der CS-Datei fügen Sie eine using-Direktive hinzu, damit der Windows.Foundation-Namespace TypedEventHandler in den Gültigkeitsbereich einbindet.

  5. Wenn Sie Proxys und Stubs benötigen, muss die Komponente Schnittstellen verwenden, um die öffentlichen Member verfügbar zu machen. In „ToasterComponent.cs“ definieren Sie eine Schnittstelle für den Toaster und eine andere für den Toast, den der Toaster erzeugt.

    Hinweis

    In C# können Sie diesen Schritt überspringen.Erstellen Sie stattdessen zuerst eine Klasse, öffnen Sie das Kontextmenü, und wählen Sie Umgestalten > Schnittstelle extrahieren aus.Ordnen Sie den Schnittstellen im generierten Code manuell öffentliche Barrierefreiheit zu.

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

    Die IToast-Schnittstelle hat eine Zeichenfolge, die abgerufen werden kann, um den Typ des Toasts zu beschreiben. Die IToaster-Schnittstelle verfügt über eine Methode zum Erstellen von Toasts und über ein Ereignis, das angibt, dass der Toast erstellt wurde. Da dieses Ereignis das bestimmte Element (d. h. Typ) des Toasts zurückgibt, wird es als typisiertes Ereignis bezeichnet.

  6. Im nächsten Schritt sind Klassen erforderlich, die diese Schnittstellen implementieren und die öffentlich und versiegelt sind, damit auf diese mit der JavaScript-App, die Sie später programmieren, zugegriffen werden kann.

    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 vorangehenden Code wird der Toast erstellt und anschließend eine Arbeitsaufgabe im Threadpool ausgeführt, um die Benachrichtigung auszulösen. Selbst wenn die IDE vorschlägt, das await-Schlüsselwort auf den asynchronen Aufruf anzuwenden, ist dies in diesem Fall nicht erforderlich, da die Methode keine Aktionen ausführt, die von den Ergebnissen des Vorgangs abhängig sind.

    Wichtig

    Der asynchrone Aufruf im vorangehenden Code verwendet ThreadPool.RunAsync lediglich, um das Auslösen des Ereignisses in einem Hintergrundthread auf einfache Weise zu veranschaulichen. Diese bestimmte Methode kann wie im folgenden Beispiel gezeigt geschrieben werden. Dies sollte problemlos sein, da der .NET-Taskplaner Async/Await-Aufrufe automatisch zum UI-Thread zurückmarshallt.

    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 ordnungsgemäß erstellt werden.

So programmieren Sie die JavaScript-App

  1. Jetzt können Sie eine Schaltfläche zur JavaScript-App hinzufügen, sodass diese die Klasse verwendet, die soeben zum Erstellen des Toasts definiert wurde. Vorher muss ein Verweis zum ToasterComponent-Projekt hinzugefügt werden, das soeben erstellt wurde. Öffnen Sie in Projektmappen-Explorer das Kontextmenü für das ToasterApplication-Projekt, wählen Sie Hinzufügen > Verweise aus, und wählen Sie dann die Schaltfläche Neuen Verweis hinzufügen aus. Wählen Sie im Dialogfeld Verweis hinzufügen links unter Projektmappe das Komponentenprojekt aus, und wählen Sie dann in der Mitte "ToasterComponent" aus. Klicken Sie auf OK.

  2. Öffnen Sie im Projektmappen-Explorer das Kontextmenü des ToasterApplication-Projekts, und wählen Sie dann Als Startprojekt festlegen aus.

  3. Fügen Sie am Ende der default.js-Datei einen Namespace hinzu, der die Funktionen zum Aufrufen der Komponente und zum Zurückrufen durch diese Komponente enthält. Der Namespace hat zwei Funktionen: eine zum Erstellen des Toasts und eine zum Behandeln des Ereignisses zum Abschließen des Toasts. Die Implementierung von makeToast erstellt ein Toasterobjekt, registriert den Ereignishandler und erstellt den Toast. Bisher hatte der Ereignishandler nicht viele Funktionen, 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 event in this case is a 
                // Toast class, with a toastType string.
                var toastType = event.toastType;
    
                document.getElementById("toasterOutput").innerHTML = "<p>Made " + toastType + " toast</p>";
            },
    
        }); 
    
  4. Die makeToast-Funktion muss mit einer Schaltfläche verknüpft werden. Aktualisieren Sie „default.html“, sodass eine Schaltfläche und Leerzeichen eingefügt werden, um das Ergebnis der Toasterstellung auszugeben:

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

    Ohne TypedEventHandler wäre es jetzt möglich, die App auf dem lokalen Computer auszuführen und auf die Schaltfläche zu klicken, um den Toast zu erstellen. In dieser App passiert jedoch nichts. Um herauszufinden, warum dies so ist, wird der verwaltete Code debuggt, der ToastCompletedEvent auslöst. Beenden Sie das Projekt, und wählen Sie dann in der Menüleiste Debuggen > Toasteranwendungseigenschaften aus. Ändern Sie den Debuggertyp in Nur verwaltet. Wählen Sie in nun der Menüleiste Debuggen > Ausnahmen und anschließend Common Language Runtime-Ausnahmen aus.

    Führen Sie nun die App aus, und klicken Sie auf die Schaltfläche zum Erstellen des Toasts. Der Debugger fängt eine ungültige Umwandlungsausnahme ab. Auch wenn dies nicht aus der Meldung ersichtlich ist, wird diese Ausnahme ausgelöst, weil Proxys für diese Schnittstelle fehlen.

    Debuggerfehlerfenster mit Hinweis zu fehlendem Proxy

Der erste Schritt zum Erstellen eines Proxys und Stubs für eine Komponente besteht darin, eine eindeutige ID oder GUID zu den Schnittstellen hinzuzufügen. Das zu verwendende GUID-Format hängt jedoch davon ab, ob Sie in C#, Visual Basic, einer anderen .NET-Sprache oder in C++ codieren.

So generieren Sie GUIDs für die Schnittstellen der Komponente

  • Für C#, Visual Basic oder andere .NET-Sprachen:

    1. Wählen Sie in der Menüleiste Extras > GUID erstellen aus. Wählen Sie im Dialogfeld 5. [Guid ("Xxxxxxxx... xxxx)] aus. Wählen Sie die Schaltfläche Neue GUID aus, und wählen Sie dann die Schaltfläche Kopieren aus.

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

      [Guid("FC198F74-A808-4E2A-9255-264746965B9F")]
          public interface IToaster...
      
    3. Fügen Sie für den System.Runtime.InteropServices-Namespace eine using-Direktive hinzu.

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

  • Für C++:

    1. Wählen Sie in der Menüleiste Extras > GUID erstellen aus. Wählen Sie im Dialogfeld 3. Struktur-GUID mit statischem Konstruktor = {...} aus. Wählen Sie die Schaltfläche Neue GUID aus, und wählen Sie dann die Schaltfläche Kopieren aus.

      GUID-Generator in Visual Studio

    2. Fügen Sie die GUID direkt vor der IToaster-Schnittstellendefinition ein. Nach dem Einfügen sollte die GUID dem folgenden Beispiel entsprechen. (Verwenden Sie nicht die im Beispiel genannte GUID. Jede eindeutige Schnittstelle sollte eine eigene GUID haben.)

      // {F8D30778-9EAF-409C-BCCD-C8B24442B09B}
          static const GUID <<name>> = { 0xf8d30778, 0x9eaf, 0x409c, { 0xbc, 0xcd, 0xc8, 0xb2, 0x44, 0x42, 0xb0, 0x9b } };
      
    3. Fügen Sie eine using-Direktive hinzu, sodass Windows.Foundation.MetadataGuidAttribute in den Gültigkeitsbereich einbindet.

    4. Konvertieren Sie nun die GUID mit Konstruktor manuell in ein GuidAttribute, sodass diese wie im folgenden Beispiel gezeigt formatiert wird. Beachten Sie, dass die geschweiften Klammern durch runde und eckige Klammern ersetzt werden und das nachfolgende Semikolon entfernt wird.

      // {E976784C-AADE-4EA4-A4C0-B0C2FD1307C3}
          [GuidAttribute(0xe976784c, 0xaade, 0x4ea4, 0xa4, 0xc0, 0xb0, 0xc2, 0xfd, 0x13, 0x7, 0xc3)]
          public interface IToaster
          {...
      
    5. Wiederholen Sie diese Schritte für die IToast-Schnittstelle.

Da die Schnittstellen nun über eindeutige IDs verfügen, kann eine IDL-Datei erstellt werden, indem die WINMD-Datei in das winmdidl-Befehlszeilentool gespeist wird. Anschließend wird der C-Quellcode für den Proxy und Stub generiert, indem diese IDL-Datei in das MIDL-Befehlszeilentool gespeist wird. Visual Studio führt diese Aktion aus, wenn wie in den nachfolgenden Schritten Postbuildereignisse erstellt werden.

So generieren Sie den Proxy- und Stubquellcode

  1. Um ein benutzerdefiniertes Postbuildereignis hinzuzufügen, öffnen Sie im Projektmappen-Explorer das Kontextmenü des ToasterComponent-Projekts, und wählen Sie dann Eigenschaften aus. Wählen Sie links auf den Eigenschaftenseiten Buildereignisse aus, und wählen Sie dann die Schaltfläche Postbuild bearbeiten aus. Fügen Sie die folgenden Befehle zur Postbuildbefehlszeile hinzu. (Die Batchdatei muss zuerst aufgerufen werden, um die Umgebungsvariablen zum Suchen des winmdidl-Tools festzulegen).

    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

    Ändern Sie das MIDL-/env-Paramater für eine ARM- oder eine x64-Projektkonfiguration in x64 oder in arm32.

  2. Um sicherzustellen, dass die IDL-Datei bei jeder Änderung der WINMD-Datei geändert wird, ändern Sie Postbuildereignis ausführen in Wenn der Build die Projektausgabe aktualisiert.

    Die Eigenschaftenseite Buildereignisse sollte diesem ähneln:

    Postbuildschritte auf der Visual Studio-Eigenschaftenseite

  3. Erstellen Sie die Projektmappe neu, um die IDL zu generieren und zu kompilieren.

    Sie können überprüfen, ob die Projektmappe ordnungsgemäß von der MIDL kompiliert wurde, indem Sie im ToasterComponent-Projektverzeichnis nach ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c und dlldata.c suchen.

So kompilieren Sie Proxy- und Stubcode in eine DLL

  1. Nachdem Sie nun über die erforderlichen Dateien verfügen, können Sie diese kompilieren, um eine DLL in Form einer C++-Datei zu erstellen. Um dies so einfach wie möglich zu machen, fügen Sie ein neues Projekt hinzu, um das Erstellen der Proxys zu unterstützen. Öffnen Sie das Kontextmenü für die ToasterApplication-Projektmappe, und wählen Sie dann Hinzufügen > Neues Projekt aus. Erweitern Sie links im Dialogfeld Neues Projekt die Option Visual C++ > Windows Store, und wählen Sie dann in der Mitte DLL (Windows Store-Apps) aus. (Beachten Sie, dass es sich hierbei NICHT um ein Komponentenprojekt für Windows-Runtime handelt.) Geben Sie Proxys als Projektnamen ein, und wählen Sie dann die Schaltfläche OK aus. Diese Dateien werden bei Änderungen der C#-Klasse von den Postbuildereignissen aktualisiert.

  2. Standardmäßig generiert das Proxyprojekt Headerdateien (.h) und CPP-Dateien in C++. Da die DLL aus den Dateien erstellt wird, die von der MIDL erstellt werden, sind die H- und CPP-Dateien nicht erforderlich. Öffnen Sie im Projektmappen-Explorer das Kontextmenü für diese Dateien, wählen Sie Entfernen aus, und bestätigen Sie dann die Löschung.

  3. Da das Projekt nun leer ist, können Sie die MIDL-generierten Dateien wieder hinzufügen. Öffnen Sie das Kontextmenü für das Proxyprojekt, und wählen Sie dann Hinzufügen > Vorhandenes Element 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.

  4. Erstellen Sie im Proxyprojekt eine DEF-Datei, um die DLL-Exporte zu definieren, die in „dlldata.c“ beschrieben werden. Öffnen Sie das Kontextmenü für das Projekt, und wählen Sie dann Hinzufügen > Neues Element aus. Wählen Sie links im Dialogfeld Code aus, und wählen Sie dann in der Mitte Moduldefinitionsdatei aus. Benennen Sie die Datei mit „proxies.def“, und wählen Sie dann die Schaltfläche Hinzufügen aus. Öffnen Sie diese DEF-Datei und ändern Sie sie, um die EXPORTS einzuschließen, die in „dlldata.c“ definiert werden:

    EXPORTS
        DllCanUnloadNow         PRIVATE
        DllGetClassObject       PRIVATE
    
  5. Wenn Sie das Projekt jetzt erstellen, tritt ein Fehler auf. Um dieses Projekt ordnungsgemäß zu kompilieren, muss die Projektkompilierung und -verknüpfung geändert werden. Öffnen Sie im Projektmappen-Explorer das Kontextmenü für das Proxyprojekt, und wählen Sie dann Eigenschaften aus. Ändern Sie die Eigenschaftenseiten wie folgt:

    • Wählen Sie links C/C++ > Präprozessor aus, wählen Sie dann rechts Präprozessordefinitionen aus, wählen Sie die Schaltfläche mit dem Pfeil nach unten und anschließend Bearbeiten aus. Fügen Sie diese Definitionen im Feld hinzu:

      WIN32;_WINDOWS
      
    • Ändern Sie unter C/C++ > Vorkompilierte Header die Option Vorkompilierter Header in Vorkompilierte Header nicht verwenden, und wählen Sie dann die Schaltfläche Übernehmen aus.

    • Ändern Sie unter Linker > Allgemein die Option Importbibliothek ignorieren in Ja, und wählen Sie dann die Schaltfläche Übernehmen aus.

    • Wählen Sie unter Linker > Eingabe die Option Zusätzliche Abhängigkeiten aus, wählen Sie die Schaltfläche mit dem Pfeil nach unten aus, und wählen Sie dann Bearbeiten aus. Fügen Sie diesen Text im Feld hinzu:

      rpcrt4.lib;runtimeobject.lib
      

      Fügen Sie diese Bibliotheken nicht direkt in die Listenzeile ein. Verwenden Sie das Eingabefeld, um sicherzustellen, dass MSBuild in Visual Studio die richtigen zusätzlichen Abhängigkeiten beibehält.

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

  6. Übernehmen Sie danach eine Abhängigkeit vom ToasterComponent-Projekt. Dadurch wird sichergestellt, dass der Toaster vor Erstellen des Proxyprojektbuilds erstellt wird. Dies ist erforderlich, da das Toasterprojekt für das Generieren der Dateien zum Erstellen des Proxys zuständig ist.

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

  7. Überprüfen Sie, ob die Projektmappe ordnungsgemäß erstellt wird, indem Sie in Visual Studio in der Menüleiste Erstellen > Projektmappe neu erstellen auswählen.

So registrieren Sie den Proxy und Stub

  1. Ö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 aus, wählen Sie XML-Text-Editor aus, und wählen Sie dann die Schaltfläche OK aus. Zum Bereitstellen der windows.activatableClass.proxyStub-Erweiterungsregistrierung wird XML eingefügt, das auf den GUIDs im Proxy basiert. Öffnen Sie „ToasterComponent_i.c.“, um die GUIDs für die „.appxmanifest“-Datei zu suchen. Suchen Sie Einträge, die den im folgenden Beispiel genannten Einträgen ähneln. Beachten Sie außerdem die Definitionen für IToast, IToaster sowie eine dritte Schnittstelle – einen typisierten Ereignishandler, der über zwei Parameter verfügt: einen Toaster und einen Toast. 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 der C#-Datei definiert sind. Da die typisierte Ereignishandlerschnittstelle automatisch generiert wird, wird die GUID für diese Schnittstelle ebenfalls 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);
    

    Die GUIDs werden jetzt kopiert, in einem hinzugefügten Knoten namens Extensions in „package.appxmanifest“ eingefügt und anschließend umformatiert. Der Manifesteintrag ähnelt dem folgenden Beispiel – denken Sie aber auch hier daran, eigene GUIDs zu verwenden. Beachten Sie, dass die ClassId-GUID in XML mit „ITypedEventHandler2“ identisch ist. Dies liegt daran, dass diese GUID als erstes in „ToasterComponent_i.c“ aufgeführt wird. Bei den hier genannten GUIDs wird zwischen Groß-/Kleinschreibung unterschieden. Anstatt die GUIDs für IToast und IToaster manuell umzuformatieren, können Sie zurück in die Schnittstellendefinitionen wechseln und den GuidAttribute-Wert abrufen, der das richtige Format aufweist. In C++ ist eine korrekt formatierte GUID im Kommentar enthalten. Auf jeden Fall müssen Sie die GUID, die sowohl für die ClassId als auch für den Ereignishandler verwendet wird, manuell umformatieren.

    <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 Extensions-XML-Knoten als direkt untergeordnetes Element des Package-Knotens, sowie einen Peer, z. B. vom Resources-Knoten, ein.

  2. Bevor Sie fortfahren, muss folgendes sichergestellt sein:

    • Die ProxyStub-ClassId ist auf die erste GUID der ToasterComponent_i.c-Datei festgelegt. Verwenden Sie die erste GUID, die in dieser Datei für die classId definiert ist. (Diese ist mit der GUID für „ITypedEventHandler2“ ggf. identisch.)

    • Path ist der relative Pfad des Pakets der Proxybinärdatei. (In dieser exemplarischen Vorgehensweise befindet sich „proxies.dll“ im gleichen Ordner wie „ToasterApplication.winmd“.)

    • Die GUIDs haben das richtige Format. (Hier können leicht Fehler passieren.)

    • Die Schnittstellen-IDs im Manifest entsprechen den IIDs in der ToasterComponent_i.c-Datei.

    • Die Schnittstellennamen sind im Manifest eindeutig. Da diese nicht vom System verwendet werden, können Sie die Werte auswählen. Es empfiehlt sich, Schnittstellennamen auszuwählen, die eindeutig mit den Schnittstellen übereinstimmen, die Sie definiert haben. Für generierte Schnittstellen sollten die Namen auf die generierten Schnittstellen schließen lassen. Sie können die ToasterComponent_i.c-Datei zum Generieren von Schnittstellennamen verwenden.

  3. Wenn Sie die Projektmappe jetzt ausführen, erhalten Sie eine Fehlermeldung, die angibt, 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 Proxyprojekt. Stellen Sie außerdem sicher, dass das Kontrollkästchen neben „ToasterComponent“ ebenfalls aktiviert ist. Klicken Sie auf OK.

    Das Projekt sollte nun erstellt werden. Führen Sie das Projekt aus und überprüfen Sie, ob Sie einen Toast erstellen können.