Déclenchement d’événements dans les composants Windows Runtime

Notes

Pour plus d’informations sur la génération d’événements dans un composant Windows Runtime C++/WinRT, consultez Créer des événements en C++/WinRT.

Si votre composant Windows Runtime déclenche un événement d’un type délégué défini par l’utilisateur sur un thread d’arrière-plan (thread de travail) et que vous souhaitez que JavaScript puisse recevoir l’événement, vous pouvez l’implémenter et/ou le déclencher de l’une de ces manières.

  • (Option 1) Déclenchez l’événement via Windows.UI.Core.CoreDispatcher pour marshaler l’événement dans le contexte de thread JavaScript. Bien qu’il s’agisse en général de la meilleure option, elle peut dans certains cas ne pas fournir des performances optimales.
  • (Option 2) Utilisez l’objet>Windows.Foundation.EventHandler< (mais perdez les informations de type d’événement). Si l’option 1 n’est pas possible ou si son rendement n’est pas suffisant, il s’agit d’un bon deuxième choix, à condition que la perte d’informations de type soit acceptable. Si vous créez un composant C# Windows Runtime, le type d’objet> Windows.Foundation.EventHandler< n’est pas disponible; au lieu de cela, ce type est projeté sur System.EventHandler. Vous devez donc l’utiliser à la place.
  • (Option nº 3) Créer vos propres proxy/stub pour le composant. Cette option est la plus difficile à implémenter, mais elle conserve les informations sur le type et peut offrir de meilleures performances que l’option nº 1 dans les scénarios exigeants.

Si vous déclenchez un événement sur un thread d’arrière-plan sans utiliser l’une de ces options, un client JavaScript ne recevra pas l’événement.

Arrière-plan

Tous les composants et applications Windows Runtime sont essentiellement des objets COM, quel que soit le langage utilisé pour les créer. Dans l’API Windows, la plupart des composants sont des objets COM agiles qui peuvent communiquer aussi bien avec les objets sur le thread d’arrière-plan qu’avec les objets sur le thread d’interface utilisateur. Si un objet COM ne peut pas être rendu agile, il requiert des objets d’assistance appelés proxys et stubs pour communiquer avec d’autres objets COM à travers la limite thread d’interface utilisateur-thread d’arrière-plan. (En termes COM, on parle de communication entre cloisonnements de threads.)

La plupart des objets de l’API Windows sont agiles ou ont des proxys et des stubs incorporés. Toutefois, les proxys et les stubs ne peuvent pas être créés pour les types génériques comme Windows.Foundation.TypedEventHandler<TSender, TResult>, car ces types ne sont pas complets tant que vous n’avez pas fourni l’argument de type. L’absence de proxys ou de stubs pose problème uniquement avec les clients JavaScript, mais si vous souhaitez que votre composant soit utilisable à partir de JavaScript, mais aussi de C++ ou d’un langage .NET, vous devez utiliser l’une des trois options suivantes.

(Option nº 1) Déclencher l’événement via CoreDispatcher

Vous pouvez envoyer des événements de n’importe quel type délégué défini par l’utilisateur à l’aide de Windows.UI.Core.CoreDispatcher, et JavaScript sera en mesure de les recevoir. Si vous n’êtes pas certain de l’option à utiliser, essayez celle-ci en premier. Si la latence entre le déclenchement des événements et la gestion des événements devient un problème, essayez l’une des autres options.

L’exemple suivant montre comment utiliser CoreDispatcher pour déclencher un événement fortement typé. Notez que l’argument de type est Toast, et non 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 nº 2) Utiliser EventHandler<Object> mais perdre les informations sur le type

Notes

Si vous créez un composant C# Windows Runtime, le type d’objet> Windows.Foundation.EventHandler< n’est pas disponible; au lieu de cela, ce type est projeté sur System.EventHandler. Vous devez donc l’utiliser à la place.

Une autre façon d’envoyer un événement à partir d’un thread d’arrière-plan consiste à utiliser l’objet>Windows.Foundation.EventHandler<comme type de l’événement. Windows fournit cette instanciation concrète du type générique, ainsi qu’un proxy et un stub pour celui-ci. L’inconvénient est que les informations sur le type de vos arguments et de votre émetteur d’événement sont perdues. Les clients C++ et .NET doivent connaître via la documentation le type vers lequel effectuer un cast lorsque l’événement est reçu. Les clients JavaScript n’ont pas besoin des informations sur le type d’origine. Ils recherchent les propriétés des arguments, selon leurs noms dans les métadonnées.

Cet exemple montre comment utiliser Windows.Foundation.EventHandler<Object> en C# :

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

Cet événement est utilisé du côté JavaScript comme suit :

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

(Option nº 3) Créer vos propres proxy/stub

Pour améliorer les performances potentielles sur les types d’événements définis par l’utilisateur qui présentent des informations entièrement préservées sur le type, vous devez créer vos propres objets proxy et stub et les inclure dans le package de votre application. En règle générale, vous devez utiliser cette option uniquement dans les rares cas où aucune des deux autres options n’est adaptée. En outre, il n’y a aucune garantie que cette option offrira de meilleures performances que les deux autres options. Les performances réelles dépendent de nombreux facteurs. Pour mesurer les performances réelles dans votre application et déterminer si l’événement est en fait un goulot d’étranglement, utilisez le profileur Visual Studio ou d’autres outils de profilage.

Le reste de cet article explique comment utiliser C# pour créer un composant Windows Runtime de base, puis comment utiliser C++ pour créer une DLL pour le proxy et le stub qui permettra à JavaScript d’utiliser un événement Windows.Foundation.TypedEventHandler<TSender, TResult> déclenché par le composant dans une opération asynchrone. (Vous pouvez également utiliser C++ ou Visual Basic pour créer le composant. Les étapes liées à la création des proxys et des stubs sont les mêmes.) Cette procédure pas à pas est basée sur la création d’un exemple de composant in-process Windows Runtime (C++/CX) et permet d’expliquer ses objectifs.

Cette procédure pas à pas comporte ces parties.

  • Ici, vous allez créer deux classes Windows Runtime de base. Une classe expose un événement de type Windows.Foundation.TypedEventHandler<TSender, TResult> et l’autre classe est le type retourné à JavaScript comme argument pour TValue. Ces classes ne peuvent pas communiquer avec JavaScript tant que vous n’avez pas effectué les étapes suivantes.
  • Cette application active l’objet de classe principal, appelle une méthode et gère un événement déclenché par le composant Windows Runtime.
  • Ces éléments sont requis par les outils qui génèrent les classes proxy et stub.
  • Vous devez ensuite utiliser le fichier IDL pour générer le code source C pour le proxy et le stub.
  • Inscrivez les objets proxy-stub afin que le runtime COM puisse les trouver, puis référencez la DLL de proxy-stub dans le projet d’application.

Pour créer le composant Windows Runtime

Dans Visual Studio, dans la barre de menus, choisissez Fichier > Nouveau projet. Dans la boîte de dialogue Nouveau projet, développez JavaScript > Windows universel, puis sélectionnez Application vide. Nommez le projet ToasterApplication, puis cliquez sur le bouton OK.

Ajoutez un composant Windows Runtime C# à la solution : dans l’Explorateur de solutions, ouvrez le menu contextuel de la solution, puis choisissez Ajouter > Nouveau projet. Développez Microsoft Store Visual C#>, puis sélectionnez Windows Runtime Composant. Nommez le projet ToasterComponent, puis cliquez sur le bouton OK. ToasterComponent sera l’espace de noms racine pour les composants que vous créerez lors des étapes ultérieures.

Dans l’Explorateur de solutions, ouvrez le menu contextuel de la solution, puis choisissez Propriétés. Dans la boîte de dialogue Pages de propriétés, sélectionnez Propriétés de configuration dans le volet gauche, puis en haut de la boîte de dialogue, définissez Configuration sur Déboguer et Plateforme sur x86, x64 ou ARM. Choisissez le bouton OK.

Important Plateforme = Toute CPU ne fonctionnera pas, car cette option n’est pas valide pour la DLL Win32 de code natif que vous ajouterez à la solution ultérieurement.

Dans l’Explorateur de solutions, remplacez le nom class1.cs par ToasterComponent.cs afin qu’il corresponde au nom du projet. Visual Studio renomme automatiquement la classe dans le fichier pour qu’elle corresponde au nouveau nom de fichier.

Dans le fichier .cs, ajoutez une directive using pour l’espace de noms Windows.Foundation afin d’inclure TypedEventHandler dans la portée.

Lorsque vous avez besoin de proxys et de stubs, votre composant doit utiliser des interfaces pour exposer ses membres publics. Dans ToasterComponent.cs, définissez une interface pour le générateur de toasts et une autre pour le toast que le générateur produit.

Remarque En C#, vous pouvez ignorer cette étape. À la place, créez d’abord une classe, puis ouvrez son menu contextuel et choisissez Refactoriser > Extraire l’interface. Dans le code généré, donnez manuellement aux interfaces une accessibilité publique.

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

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

L’interface IToast comporte qui peut être récupérée pour décrire le type de toast. L’interface IToaster dispose d’une méthode pour créer un toast et d’un événement pour indiquer que le toast est créé. Comme cet événement retourne le type particulier du toast, il est appelé événement typé.

Ensuite, nous avons besoin de classes qui implémentent ces interfaces, et qui soient publiques et verrouillées afin qu’elles soient accessibles depuis l’application JavaScript que vous programmerez ultérieurement.

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

Dans le code précédent, nous créons le toast, puis accélérons un élément de travail de pool de threads pour déclencher la notification. Bien que l’IDE puisse suggérer que vous appliquez le mot clé await à l’appel asynchrone, cela n’est pas nécessaire dans ce cas, parce que la méthode n’effectue aucun travail dépendant des résultats de l’opération.

Remarque L’appel asynchrone dans le code précédent utilise ThreadPool.RunAsync uniquement pour illustrer un moyen simple de déclencher l’événement sur un thread d’arrière-plan. Vous pouvez écrire cette méthode particulière comme indiqué dans l’exemple suivant, et elle s’exécutera correctement, car le planificateur de tâches .NET marshale automatiquement les appels asynchrones/d’attente vers le thread d’interface utilisateur.  

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

Si vous générez le projet à présent, il doit être généré correctement.

Pour programmer l'application JavaScript

Maintenant nous pouvons ajouter un bouton à l'application JavaScript pour qu'elle utilise la classe que nous venons de définir pour créer un toast. Avant de le faire, nous devons ajouter une référence au projet ToasterComponent que nous venons de créer. Dans Explorateur de solutions, ouvrez le menu contextuel du projet Grille-painApplication, choisissez Ajouter des > références, puis le bouton Ajouter une nouvelle référence. Dans la boîte de dialogue Ajouter une référence, dans le volet gauche, sous Solution, sélectionnez le projet de composant, puis, dans le volet central, sélectionnez ToasterComponent. Choisissez le bouton OK.

Dans Explorateur de solutions, ouvrez le menu contextuel du projet Grille-painApplication, puis choisissez Définir comme projet de démarrage.

À la fin du fichier default.js, ajoutez un espace de noms pour contenir les fonctions permettant d'appeler le composant et d'être rappelé par celui-ci. L'espace de noms possède deux fonctions, une pour créer un toast et une pour gérer l'événement de fin de toast. L’implémentation de makeToast crée un objet Grille-pain, inscrit le gestionnaire d’événements et effectue le toast. Jusqu'à présent, le gestionnaire d'événements ne fait pas grand-chose, comme illustré ci-après :

	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>";
        },
    });

La fonction makeToast doit être branchée à un bouton. Mettez à jour le fichier default.html pour inclure un bouton et un espace pour sortir le résultat de la création du toast :

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

Si nous n’utilisions pas de TypedEventHandler, nous pourrions maintenant exécuter l’application sur l’ordinateur local et cliquer sur le bouton pour faire un toast. Mais dans notre application, rien ne se produit. Pour savoir pourquoi, déboguer le code managé qui déclenche ToastCompletedEvent. Arrêtez le projet, puis, dans la barre de menus, choisissez Déboguer > les propriétés de l’application grille-pain. Remplacez Type de débogueurpar Managé uniquement. Dans la barre de menus, choisissez Exceptions de débogage>, puis sélectionnez Exceptions common language runtime.

Maintenant, exécutez l'application et cliquez sur le bouton de création de toast. Le débogueur intercepte une exception de cast non valide. Bien que cela ne soit pas évident au vu de son message, cette exception se produit parce que les proxies sont manquants pour cette interface.

proxy manquant

La première étape de la création d'un proxy et d'un stub pour un composant consiste à ajouter un identificateur unique ou GUID aux interfaces. Toutefois, le format de GUID à utiliser diffère selon que vous codez en C#, en Visual Basic ou autre langage .NET, ou en C++.

Pour générer des GUID pour les interfaces du composant (C# et autres langages .NET)

Dans la barre de menus, choisissez Outils > Créer un GUID. Dans la boîte de dialogue, sélectionnez 5. [Guid(« xxxxxxxx-xxxx... xxxx »)]. Sélectionnez le bouton Nouveau GUID, puis cliquez sur le bouton Copier.

outil générateur de guid

Retour à la définition de l’interface, puis collez le nouveau GUID juste avant l’interface IToaster, comme illustré dans l’exemple suivant. (N’utilisez pas le GUID dans l’exemple. Chaque interface unique doit avoir son propre GUID.)

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

Ajoutez une directive using pour l’espace de noms System.Runtime.InteropServices.

Répétez ces étapes pour l’interface IToast.

Pour générer des GUID pour les interfaces du composant (C++)

Dans la barre de menus, choisissez Outils > Créer un GUID. Dans la boîte de dialogue, sélectionnez 3. GUID de struct const statique = {...}. Sélectionnez le bouton Nouveau GUID, puis cliquez sur le bouton Copier.

Collez le GUID juste avant la définition de l’interface IToaster. Une fois collé, le GUID doit ressembler à l'exemple suivant. (N’utilisez pas le GUID dans l’exemple. Chaque interface unique doit avoir son propre GUID.)

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

Ajoutez une directive using pour Windows.Foundation.Metadata afin d’intégrer GuidAttribute dans l’étendue.

Convertissez ensuite manuellement le GUID const en un GuidAttribute afin qu'il soit mis en forme comme illustré dans l'exemple suivant. Notez que les accolades sont remplacées par des crochets et des parenthèses, et que le point-virgule de fin est supprimé.

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

Répétez ces étapes pour l’interface IToast.

Maintenant que les interfaces ont des ID uniques, nous pouvons créer un fichier IDL en alimentant le fichier .winmd dans l’outil en ligne de commande winmdidl, puis générer le code source C pour le proxy et le stub en alimentant ce fichier IDL dans l’outil en ligne de commande MIDL. Visual Studio le fait pour nous si nous créons des événements post-build comme indiqué dans les étapes suivantes.

Pour générer le code source du proxy et du stub

Pour ajouter un événement post-build personnalisé, dans l'Explorateur de solutions, ouvrez le menu contextuel du projet ToasterComponent, puis sélectionnez Propriétés. Dans le volet gauche des pages de propriétés, sélectionnez Événements de build, puis sélectionnez le bouton Modifier post-build. Ajoutez les commandes suivantes à la ligne de commande post-build. (Le fichier batch doit être appelé en premier pour définir les variables d’environnement afin de rechercher l’outil winmdidl.)

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"

Important Pour une configuration de projet ARM ou x64, remplacez le paramètre MIDL /env par x64 ou arm32.

Pour vous assurer que le fichier IDL est régénéré chaque fois que le fichier .winmd est modifié, remplacez Exécuter l’événement post-build par Quand la build met à jour la sortie du projet. La page de propriété Événements de build doit ressembler à ceci : événements de build

Régénérez la solution pour générer et compiler le fichier IDL.

Vous pouvez vérifier que MIDL a correctement compilé la solution en recherchant ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c et dlldata.c dans le répertoire du projet ToasterComponent.

Pour compiler le code de proxy et de stub dans une DLL

Maintenant que vous avez les fichiers requis, vous pouvez les compiler pour produire une DLL, qui est un fichier C++. Pour rendre cette opération aussi simple que possible, ajoutez un nouveau projet pour prendre en charge la génération des proxies. Ouvrez le menu contextuel de la solution Grille-painApplication, puis choisissez Ajouter un > nouveau projet. Dans le volet gauche de la boîte de dialogue Nouveau projet, développez Windows Univeral Windows > Visual C++>, puis dans le volet central, sélectionnez DLL (applications UWP). (Notez qu’il ne s’agit PAS d’un projet de composant Windows Runtime C++.) Nommez les proxys de projet, puis choisissez le bouton OK. Ces fichiers sont mis à jour par les événements post-build lorsque quelque chose change dans la classe C#.

Par défaut, le projet Proxies génère des fichiers d'en-tête .h et des fichiers C++ .cpp. Étant donné que la DLL est générée à partir des fichiers produits avec MIDL, les fichiers .h et .cpp ne sont pas requis. Dans Explorateur de solutions, ouvrez le menu contextuel correspondant, choisissez Supprimer, puis confirmez la suppression.

Maintenant que le projet est vide, vous pouvez ajouter à nouveau les fichiers générés par MIDL. Ouvrez le menu contextuel du projet Proxys, puis choisissez Ajouter un > élément existant. Dans la boîte de dialogue, accédez au répertoire du projet Grille-painComponent et sélectionnez ces fichiers : Grille-painComponent.h, ToasterComponent_i.c, ToasterComponent_p.c et dlldata.c. Choisissez le bouton Ajouter.

Dans le projet Proxies, créez un fichier .def pour définir les exportations de DLL décrites dans dlldata.c. Ouvrez le menu contextuel du projet, puis choisissez Ajouter un > nouvel élément. Dans le volet gauche de la boîte de dialogue, sélectionnez Code, puis, dans le volet central, sélectionnez Fichier de définition de module. Nommez le fichier proxies.def, puis choisissez le bouton Ajouter . Ouvrez ce fichier .def et modifiez-le pour inclure les EXPORTATIONS définies dans dlldata.c :

EXPORTS
    DllCanUnloadNow         PRIVATE
    DllGetClassObject       PRIVATE

Si vous générez le projet maintenant, il échouera. Pour compiler correctement ce projet, vous devez modifier la façon dont le projet est compilé et lié. Dans Explorateur de solutions, ouvrez le menu contextuel du projet Proxys, puis choisissez Propriétés. Modifiez les pages de propriétés comme suit.

Dans le volet gauche, sélectionnez Préprocesseur C/C++>, puis dans le volet droit, sélectionnez Définitions du préprocesseur, cliquez sur le bouton flèche bas, puis sélectionnez Modifier. Ajoutez ces définitions dans la zone :

WIN32;_WINDOWS

Sous En-têtes précompilés C/C++>, remplacezEn-tête précompilé par Ne pas utiliser d’en-têtes précompilés, puis choisissez le bouton Appliquer.

Sous Éditeur de liens > général, remplacez Ignorer la bibliothèque d’importation par Yes, puis choisissez le bouton Appliquer .

Sous Entrée de l’éditeur >de liens, sélectionnez Dépendances supplémentaires, cliquez sur le bouton flèche vers le bas, puis sélectionnez Modifier. Ajoutez ce texte dans la zone :

rpcrt4.lib;runtimeobject.lib

Ne collez pas ces bibliothèques directement dans la ligne de liste. Utilisez la zone Modifier pour vous assurer que MSBuild dans Visual Studio conserve les dépendances supplémentaires appropriées.

Une fois ces modifications effectuées, choisissez le bouton OK dans la boîte de dialogue Pages de propriétés .

Ensuite, prenez une dépendance sur le projet ToasterComponent. Cela garantit que le générateur de toasts sera généré avant le projet de proxy. Ceci est obligatoire, parce que le projet de générateur de toasts est chargé de générer les fichiers permettant de générer le proxy.

Ouvrez le menu contextuel du projet Proxies, puis sélectionnez Dépendances du projet. Cochez les cases pour indiquer que le projet Proxies dépend du projet ToasterComponent, afin de vous assurer que Visual Studio les génère dans l'ordre adéquat.

Vérifiez que la solution est générée correctement en choisissant Build > Rebuild Solution dans la barre de menus de Visual Studio.

Pour enregistrer le proxy et le stub

Dans le projet Grille-painApplication, ouvrez le menu contextuel pour package.appxmanifest, puis choisissez Ouvrir avec. Dans la boîte de dialogue Ouvrir avec, sélectionnez Éditeur de texte XML , puis cliquez sur le bouton OK . Nous allons coller dans un code XML qui fournit une inscription d’extension windows.activatableClass.proxyStub et qui sont basés sur les GUID dans le proxy. Pour rechercher les GUID à utiliser dans le fichier .appxmanifest, ouvrez ToasterComponent_i.c. Recherchez les entrées qui ressemblent à celles de l'exemple suivant. Notez également les définitions pour IToast, IToaster et une troisième interface, un gestionnaire d’événements typé qui a deux paramètres : un grille-pain et un toast. Cela correspond à l’événement défini dans la classe Grille-pain. Notez que les GUID pour IToast et IToaster correspondent aux GUID définis sur les interfaces dans le fichier C#. Étant donné que l'interface de gestionnaire d'événements typés est créée automatiquement, le GUID de cette interface l'est également.

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

Maintenant, nous copieons les GUID, les collons dans package.appxmanifest dans un nœud que nous ajoutons et nommons Extensions, puis les reformater. L'entrée de manifeste ressemble à l'exemple suivant, mais, encore une fois, n'oubliez pas d'utiliser vos propres GUID. Notez que le GUID ClassId dans le fichier XML est identique à ITypedEventHandler2. C'est parce que ce GUID est le premier répertorié dans ToasterComponent_i.c. Les GUID ici ne respectent pas la casse. Au lieu de reformater manuellement les GUID pour IToast et IToaster, vous pouvez revenir dans les définitions d’interface et obtenir la valeur GuidAttribute, qui a le format correct. En C++, il existe un GUID correctement mis en forme dans le commentaire. Dans tous les cas, vous devez reformater manuellement le GUID utilisé pour le ClassId et le gestionnaire d'événements.

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

Collez le nœud XML Extensions en tant qu’enfant direct du nœud Package et un homologue de, par exemple, le nœud Resources.

Avant de continuer, il est important de vérifier que :

  • Le ClassId ProxyStub a pour valeur le premier GUID dans le fichier ToasterComponent_i.c. Utilisez le premier GUID défini dans ce fichier pour le classId. (Cela peut être le même que le GUID pour ITypedEventHandler2.)
  • Le chemin d’accès est le chemin relatif du package du binaire proxy. (Dans cette procédure pas à pas, proxies.dll se trouve dans le même dossier que ToasterApplication.winmd.)
  • Les GUID ont le format correct. (Il est facile de se tromper.)
  • Les ID d'interface dans le manifeste correspondent aux IID dans le fichier ToasterComponent_i.c.
  • Les noms d'interface sont uniques dans le manifeste. Étant donné que ceux-ci ne sont pas utilisés par le système, vous pouvez choisir les valeurs. Il est conseillé de choisir des noms d'interface qui correspondent clairement aux interfaces que vous avez définies. Pour des interfaces générées, les noms doivent évoquer les interfaces générées. Vous pouvez utiliser le fichier ToasterComponent_i.c pour vous aider à générer des noms d'interface.

Si vous essayez à présent d'exécuter la solution, vous obtiendrez une erreur indiquant que proxies.dll ne fait pas partie de la charge utile. Ouvrez le menu contextuel du dossier Références dans le projet Grille-painApplication, puis choisissez Ajouter une référence. Activez la case à cocher en regard du projet Proxies. En outre, assurez-vous que la case à cocher en regard de ToasterComponent est également cochée. Choisissez le bouton OK.

À présent, le projet doit se générer. Exécutez le projet et vérifiez que vous pouvez créer un toast.