Composants Windows Runtime avec C++/WinRT

Cette rubrique montre comment utiliser C++/WinRT pour créer et consommer un composant Windows Runtime, un composant qui peut être appelé à partir d’une application Windows universelle créée à l’aide de n’importe quel langage Windows Runtime.

Plusieurs raisons justifient la création d’un composant Windows Runtime en C++/WinRT.

  • Pour profiter de l’avantage en matière de performances de C++ dans les opérations complexes ou gourmandes en ressources informatiques.
  • Pour réutiliser le code C++ standard déjà écrit et testé.
  • Pour exposer la fonctionnalité Win32 à une application plateforme Windows universelle (UWP) écrite dans, par exemple, C#.

En général, lorsque vous créez votre composant C++/WinRT, vous pouvez utiliser des types à partir de la bibliothèque C++ standard et des types intégrés, sauf à la limite de l’interface binaire d’application (ABI) où vous transmettez des données vers et à partir du code d’un autre .winmd package. Au niveau de l’ABI, utilisez Windows Runtime types. En outre, dans votre code C++/WinRT, utilisez des types tels que délégué et événement pour implémenter des événements qui peuvent être déclenchés à partir de votre composant et gérés dans un autre langage. Pour plus d’informations sur C++/WinRT, consultez C++ /WinRT.

Le reste de cette rubrique vous explique comment créer un composant Windows Runtime en C++/WinRT, puis comment l’utiliser à partir d’une application.

Le composant Windows Runtime que vous allez générer dans cette rubrique contient une classe runtime représentant un thermomètre. La rubrique présente également une application core qui consomme la classe runtime du thermomètre et appelle une fonction pour ajuster la température.

Notes

Pour plus d’informations sur l’installation et l’utilisation de l’extension VSIX (Visual Studio Extension) C++/WinRT et du package NuGet (qui fournissent ensemble la prise en charge des modèles et des builds de projet), consultez Prise en charge de Visual Studio pour C++/WinRT.

Important

Pour obtenir les principaux concepts et termes facilitant votre compréhension pour utiliser et créer des classes runtime avec C++/WinRT, voir Utiliser des API avec C++/WinRT et Créer des API avec C++/WinRT.

Meilleures pratiques en matière de nommage pour les dll de composants Windows Runtime

Important

Cette section décrit la convention de nommage que nous vous recommandons d’utiliser pour le .dll fichier (DLL) dans lequel vous générez votre composant Windows Runtime. Il s’agit de la séquence d’activation que suit C++/WinRT lorsque vous utilisez une classe runtime à partir d’un composant Windows Runtime.

Lors de l’activation d’une fabrique de classes, C++/WinRT tente d’abord d’appeler RoGetActivationFactory. En cas d’échec, C++/WinRT tente de trouver une DLL à charger directement. Windows Runtime’activation est toujours basée sur un nom de classe complet. La logique consiste à supprimer le nom de la classe (de ce nom de classe complet), puis à rechercher une DLL nommée pour l’espace de noms complet qui reste. Si ce n’est pas trouvé, supprimez le nom de segment le plus spécifique et répétez.

Par conséquent, par exemple, si la classe en cours d’activation a un nom complet de Contoso.Instruments.ThermometerWRC.Thermometer et que RoGetActivationFactory échoue, nous allons d’abord rechercher un Contoso.Instruments.ThermometerWRC.dll. Si ce n’est pas trouvé, nous allons rechercher Contoso.Instruments.dll, puis pour Contoso.dll.

Lorsqu’une DLL est trouvée (dans cette séquence), nous allons utiliser le point d’entrée DllGetActivationFactory de cette DLL pour essayer d’obtenir la fabrique directement (plutôt qu’indirectement via la fonction RoGetActivationFactory que nous avons tentée pour la première fois). Malgré cela, le résultat final est indisctinguishable pour l’appelant et la DLL.

Ce processus est entièrement automatique: aucune inscription ni aucun outil n’est nécessaire. Si vous créez un composant Windows Runtime, il vous suffit d’utiliser une convention de nommage pour vos DLL qui fonctionne avec le processus qui vient d’être décrit. Et si vous consommez un composant Windows Runtime et qu’il n’est pas nommé correctement, vous avez la possibilité de le renommer comme décrit.

Créer un composant Windows Runtime (ThermomètreWRC)

Commencez par créer un nouveau projet dans Microsoft Visual Studio. Créez un projet de composant Windows Runtime (C++/WinRT) et nommez-le ThermometerWRC (pour « thermomètre Windows Runtime composant »). Vérifiez que l’option Placer la solution et le projet dans le même répertoire n’est pas cochée. Ciblez la dernière version en disponibilité générale (autrement dit, pas la préversion) du SDK Windows. Le nommage du projet ThermomètreWRC vous offre l’expérience la plus simple avec le reste des étapes de cette rubrique.

Ne générez pas encore le projet.

Le projet nouvellement créé contient un fichier nommé Class.idl. Dans l’Explorateur de solutions, renommez ce fichier Thermometer.idl (le fait de renommer le fichier .idl a aussi pour effet de renommer automatiquement les fichiers dépendants .h et .cpp). Remplacez le contenu de l’élément Thermometer.idl par le listing ci-dessous.

// Thermometer.idl
namespace ThermometerWRC
{
    runtimeclass Thermometer
    {
        Thermometer();
        void AdjustTemperature(Single deltaFahrenheit);
    };
}

Enregistrez le fichier. Le projet ne sera pas généré jusqu’à l’achèvement pour le moment, mais la génération maintenant est une chose utile, car elle génère les fichiers de code source dans lesquels vous allez implémenter la classe runtime Thermometer . Continuons et générons le projet maintenant (les erreurs de génération auxquelles vous pouvez vous attendre à ce stade sont liées à des éléments Class.h et Class.g.h introuvables).

Pendant le processus de génération, l’outil midl.exe est exécuté pour créer le fichier de métadonnées Windows Runtime de votre composant (à savoir \ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd). Puis, l’outil cppwinrt.exe est exécuté (avec l’option -component) pour générer les fichiers de code source vous aidant à créer votre composant. Ces fichiers incluent des stubs pour commencer à implémenter la classe runtime Thermomètre que vous avez déclarée dans votre IDL. Ces stubs sont \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.h et Thermometer.cpp.

Cliquez avec le bouton droit de la souris sur le nœud du projet, puis cliquez sur Ouvrir le dossier dans l'Explorateur de fichiers. Le dossier du projet s’ouvre dans l'Explorateur de fichiers. De là, copiez les fichiers stub Thermometer.h et Thermometer.cpp du dossier \ThermometerWRC\ThermometerWRC\Generated Files\sources\ vers le dossier contenant vos fichiers projet, c’est-à-dire \ThermometerWRC\ThermometerWRC\, puis remplacez les fichiers dans la destination. Maintenant, nous allons ouvrir Thermometer.h et Thermometer.cpp, et implémenter notre classe runtime. Dans Thermometer.h, ajoutez un nouveau membre privé à l’implémentation (et non à l’implémentation d’usine) de Thermomètre.

// Thermometer.h
...
namespace winrt::ThermometerWRC::implementation
{
    struct Thermometer : ThermometerT<Thermometer>
    {
        ...

    private:
        float m_temperatureFahrenheit { 0.f };
    };
}
...

Dans Thermometer.cpp, implémentez la méthode AdjustTemperature comme indiqué dans la liste ci-dessous.

// Thermometer.cpp
...
namespace winrt::ThermometerWRC::implementation
{
    void Thermometer::AdjustTemperature(float deltaFahrenheit)
    {
        m_temperatureFahrenheit += deltaFahrenheit;
    }
}

Vous verrez un static_assert en haut de Thermometer.h et de Thermometer.cpp, que vous devrez supprimer. Le projet peut à présent être généré.

Si un avertissement vous empêche de procéder à la génération, corrigez les erreurs ou définissez la propriété de projet C/C++>Général>Considérer les avertissements comme des erreurs sur Non (/WX-) et générez de nouveau le projet.

Créer une application principale (ThermomètreCoreApp) pour tester le composant Windows Runtime

Créez maintenant un projet (dans votre solution ThermomètreWRC ou dans un nouveau). Créez un projet Core App (C++/WinRT) et nommez-le ThermometerCoreApp. Définissez ThermomètreCoreApp comme projet de démarrage si les deux projets se trouvent dans la même solution.

Notes

Comme mentionné précédemment, le fichier de métadonnées Windows Runtime pour votre composant Windows Runtime (dont vous avez nommé le projet ThermomètreWRC) est créé dans le dossier \ThermometerWRC\Debug\ThermometerWRC\. Le premier segment de ce chemin correspond au nom du dossier qui contient votre fichier de solution, le segment suivant est le sous-répertoire du projet nommé Debug et le dernier segment est le sous-répertoire du projet nommé de votre composant Windows Runtime. Si vous n’avez pas nommé votre projet ThermomètreWRC, votre fichier de métadonnées se trouve dans le dossier \<YourProjectName>\Debug\<YourProjectName>\.

Maintenant, dans votre projet d’application principale (ThermomètreCoreApp), ajoutez une référence et accédez à \ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd (ou ajoutez une référence de projet à projet, si les deux projets se trouvent dans la même solution). Cliquez sur Ajouter, puis sur OK. Maintenant, générez ThermomètreCoreApp. Dans le cas peu probable où vous voyez une erreur indiquant que le fichier readme.txt de charge utile n’existe pas, excluez ce fichier du projet de composant Windows Runtime, régénérez-le, puis régénérez ThermomètreCoreApp.

Pendant le processus de génération, l’outil cppwinrt.exe est exécuté pour traiter le fichier .winmd référencé dans les fichiers de code source contenant les types projetés afin de vous aider à utiliser votre composant. L’en-tête des types projetés pour les classes d’exécution de votre composant ( nommé ThermometerWRC.h) est généré dans le dossier \ThermometerCoreApp\ThermometerCoreApp\Generated Files\winrt\.

Incluez cet en-tête dans App.cpp.

// App.cpp
...
#include <winrt/ThermometerWRC.h>
...

Également dans App.cpp, ajoutez le code suivant pour instancier un objet Thermomètre (à l’aide du constructeur par défaut du type projeté) et appelez une méthode sur l’objet thermomètre.

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer m_thermometer;
    ...
    
    void OnPointerPressed(IInspectable const &, PointerEventArgs const & args)
    {
        m_thermometer.AdjustTemperature(1.f);
        ...
    }
    ...
};

Chaque fois que vous cliquez sur la fenêtre, vous incrémentez la température de l’objet thermomètre. Vous pouvez définir des points d’arrêt si vous souhaitez parcourir le code pour vérifier que l’application appelle vraiment le composant Windows Runtime.

Étapes suivantes

Pour ajouter encore plus de fonctionnalités ou de nouveaux types de Windows Runtime à votre composant C++/WinRT Windows Runtime, vous pouvez suivre les mêmes modèles ci-dessus. Tout d’abord, utilisez IDL pour définir les fonctionnalités que vous souhaitez exposer. Ensuite, générez le projet dans Visual Studio pour générer une implémentation stub. Puis terminez l’implémentation le cas échéant. Toutes les méthodes, propriétés et événements que vous définissez dans IDL sont visibles par l’application qui consomme votre composant Windows Runtime. Pour plus d’informations sur IDL, consultez Présentation du langage de définition d’interface Microsoft 3.0.

Pour obtenir un exemple d’ajout d’événement à votre composant Windows Runtime, consultez Créer des événements en C++/WinRT.

Dépannage

Symptôme Solution
Dans une application C++/WinRT, lors de l’utilisation d’un composant Windows Runtime C# qui utilise XAML, le compilateur génère une erreur au format « 'MyNamespace_XamlTypeInfo' : n’est pas un membre de 'winrt::MyNamespace' », où MyNamespace est le nom de l’espace de noms du composant Windows Runtime. Dans pch.h, dans l’application qui utilise C++/WinRT, ajoutez #include <winrt/MyNamespace.MyNamespace_XamlTypeInfo.h> pour remplacer MyNamespace de manière appropriée.