Composants Windows Runtime avec C++/WinRT

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

Il existe plusieurs raisons de créer un composant Windows Runtime en C++/WinRT.

  • Pour profiter de l’avantage des performances de C++ dans des opérations complexes ou nécessitant beaucoup de ressources informatiques.
  • Pour réutiliser le code C++ standard déjà écrit et testé.
  • Pour exposer des fonctionnalités Win32 à une application plateforme Windows universelle (UWP) écrite, par exemple, C#.

En règle générale, 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, à l’exception de la limite de l’interface binaire d’application (ABI) où vous transmettez des données vers et depuis du code dans un autre .winmd package. À l’adresse ABI, utilisez les types Windows Runtime. En outre, dans votre code C++/WinRT, utilisez des types tels que délégué et événement pour implémenter des événements pouvant ê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 guide tout au long de la création d’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 illustre également une application Core qui consomme la classe runtime du thermomètre et appelle une fonction pour ajuster la température.

Remarque

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.

Bonne pratique d’affectation de noms pour les dll de composants Windows Runtime

Important

Cette section décrit la convention d’affectation de noms dans laquelle nous vous recommandons d’utiliser le .dll fichier (DLL) dans lequel vous générez votre composant Windows Runtime. Il s’agit de la séquence d’activation que C++/WinRT suit 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 un appel à RoGetActivationFactory. En cas d’échec, C++/WinRT tente de trouver une DLL à charger directement. L’activation de Windows Runtime est toujours basée sur un nom de classe complet. La logique consiste à supprimer le nom de 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, puis répétez.

Par exemple, si la classe activée a un nom complet de Contoso.Instruments.ThermometerWRC.Thermometer, et 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 directement la fabrique (plutôt que indirectement via la fonction RoGetActivationFactory que nous avons tentée pour la première fois). Même si le résultat final est indistinguishable à l’appelant et à la DLL.

Ce processus est entièrement automatique : aucune inscription ou outil n’est nécessaire. Si vous créez un composant Windows Runtime, vous devez simplement utiliser une convention d’affectation de noms pour vos DLL qui fonctionnent avec le processus 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 (ThermometerWRC)

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 le « composant Windows Runtime de thermomètre »). 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 ThermometerWRC vous donnera 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é à l’achèvement pour le moment, mais la génération est maintenant 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 vous aider à implémenter la classe d’exécution 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 de fabrique) 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 Core (ThermometerCoreApp) pour tester le composant Windows Runtime

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

Remarque

Comme mentionné précédemment, le fichier de métadonnées Windows Runtime pour votre composant Windows Runtime (dont vous avez nommé ThermometerWRC) 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 ThermometerWRC, votre fichier de métadonnées se trouve dans le dossier \<YourProjectName>\Debug\<YourProjectName>\.

À présent, dans votre projet Core App (ThermometerCoreApp), 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 ThermometerCoreApp. 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 regénérer ThermometerCoreApp.

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 runtime de votre composant, nommées 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>
...

App.cppAjoutez également le code suivant pour instancier un objet Thermometer (à 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 confirmer que l’application appelle réellement le composant Windows Runtime.

Étapes suivantes

Pour ajouter encore plus de fonctionnalités ou de nouveaux types Windows Runtime à votre composant Windows Runtime C++/WinRT, 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. Générez ensuite le projet dans Visual Studio pour générer une implémentation stub. Terminez ensuite l’implémentation en fonction des besoins. 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 de l’interface Microsoft 3.0.

Pour obtenir un exemple d’ajout d’un é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.