Forum aux questions sur C++/WinRT

Réponses aux questions que vous avez probablement sur la création et la consommation d'API Windows Runtime avec C++/WinRT.

Important

Pour obtenir des notes de publication sur C++/WinRT, consultez Actualités et modifications, dans C++/WinRT 2.0.

Note

Si votre question concerne un message d’erreur que vous avez vu, consultez également la rubrique Résolution des problèmes C++/WinRT .

Où puis-je trouver des exemples d’applications C++/WinRT ?

Comment recibler mon projet C++/WinRT vers une version ultérieure du SDK Windows ?

Pourquoi mon nouveau projet ne sera-t-il pas compilé maintenant que je suis passé à C++/WinRT 2.0 ?

Pour connaître l’ensemble de modifications (y compris les changements cassants), consultez Nouveautés et changements dans C++WinRT 2.0. Par exemple, si vous utilisez un for basé sur une plage sur une collection Windows Runtime, vous devez maintenant #include <winrt/Windows.Foundation.Collections.h>.

Pourquoi mon nouveau projet ne compile-t-il pas ? J'utilise Visual Studio 2017 (version 15.8.0 ou ultérieure) et sdk version 17134

Si vous utilisez Visual Studio 2017 (version 15.8.0 ou ultérieure) et ciblez le sdk Windows version 10.0.17134.0 (Windows 10, version 1803), puis un projet C++/WinRT nouvellement créé peut échouer à compiler avec l'erreur « erreur C3861 : « from_abi » : identificateur introuvable » et avec d'autres erreurs provenant de base.h. La solution consiste à cibler une version ultérieure (plus conforme) du KIT de développement logiciel (SDK) Windows, ou définir le mode de conformité du langage C/C++>Language :>Non (également, si /permissive- apparaît dans la propriété de projet C/C++>Ligne de commande sous Options supplémentaires, puis supprimez-la).

Comment faire pour résoudre l’erreur de build « Le VSIX C++WinRT ne fournit plus de prise en charge de build de projet. Ajoutez une référence de projet au package Nuget Microsoft.Windows.CppWinRT » ?

Installez le package NuGet Microsoft.Windows.CppWinRT dans votre projet. Pour plus d’informations, consultez les versions antérieures de l’extension VSIX.

Comment personnaliser la prise en charge des builds dans le package NuGet ?

La prise en charge des builds C++/WinRT (propriétés/cibles) est documentée dans le fichier Lisez-moi du package NuGet Microsoft.Windows.CppWinRT.

Quelles sont les conditions requises pour l’extension de Visual Studio C++/WinRT (VSIX) ?

Pour la version 1.0.190128.4 de l’extension VSIX et les versions ultérieures, consultez prise en charge de C++/WinRT dans Visual Studio. Pour les autres versions, consultez versions antérieures de l’extension VSIX.

Qu’est-ce qu’une classe runtime ?

Une classe runtime est un type qui peut être activé et consommé via des interfaces COM modernes, généralement au-delà des limites exécutables. Toutefois, une classe runtime peut également être utilisée dans l’unité de compilation qui l’implémente. Vous déclarez une classe runtime dans IDL (Interface Definition Language) et vous pouvez l’implémenter en C++ standard à l’aide de C++/WinRT.

Que signifient le type projeté et le type d’implémentation ?

Si vous consommez uniquement une classe Windows Runtime (classe runtime), vous traiterez exclusivement avec des types projetés. C++/WinRT est une projection de langage. Les types projetés font donc partie de la surface du Windows Runtime projeté en C++ avec C++/WinRT. Pour plus d’informations, consultez Utiliser des API avec C++/WinRT.

Le type d’implémentation contient l’implémentation d’une classe runtime. Il est donc disponible uniquement dans le projet qui implémente la classe runtime. Lorsque vous travaillez dans un projet qui implémente des classes runtime (un projet de composant Windows Runtime ou un projet qui utilise l'interface utilisateur XAML), il est important d'être à l'aise avec la distinction entre votre type d'implémentation pour une classe runtime et le type projeté qui représente la classe runtime projetée en C++/WinRT. Pour plus d’informations, consultez Créer des API avec C++/WinRT.

Dois-je déclarer un constructeur dans l’IDL de ma classe runtime ?

Uniquement si la classe runtime est conçue pour être consommée à partir de l'extérieur de son unité de compilation d'implémentation (il s'agit d'un composant Windows Runtime destiné à une consommation générale par Windows Runtime applications clientes). Pour plus d’informations sur l’objectif et les conséquences de la déclaration de constructeurs dans IDL, consultez les constructeurs de classe Runtime.

Pourquoi le compilateur me donne-t-il une erreur « C3779 : consume_Something : la fonction qui retourne « auto » ne peut pas être utilisée avant qu’elle soit définie ?

Vous utilisez un objet Windows Runtime sans avoir d'abord inclus le fichier d'en-tête d'espace de noms correspondant. Inclure l’en-tête correspondant à l’espace de noms de l’API, puis recompilez. Pour plus d’informations, consultez les en-têtes de projection C++/WinRT.

Pourquoi l’éditeur de liens me donne-t-il une erreur « LNK2019 : Symbole externe non résolu » ?

Si le symbole non résolu est une fonction libre Windows Runtime, telle que RoInitialize, vous devez lier explicitement la bibliothèque de parapluies WindowsApp.lib dans votre projet. La projection C++/WinRT dépend de certaines de ces fonctions libres (non membres) et de points d’entrée. Si vous utilisez pour votre application l’un des modèles de projet C++/WinRT Visual Studio Extension (VSIX), alors WindowsApp.lib est automatiquement lié. Sinon, vous pouvez utiliser les paramètres de lien de projet pour l’inclure ou le faire dans le code source.

#pragma comment(lib, "windowsapp")

Il est important de résoudre les erreurs de l'éditeur de liens que vous pouvez en liant WindowsApp.lib au lieu d'une autre bibliothèque de liens statiques, sinon votre application ne réussira pas les tests du Kit de certification application Windows utilisés par Visual Studio et par le Microsoft Store pour valider les soumissions (ce qui signifie qu'il n'est donc pas possible que votre application soit ingérée correctement dans le Microsoft Store).

Si le symbole non résolu est un constructeur, vous avez peut-être oublié d’inclure le fichier d’en-tête du namespace de la classe en cours de construction. Ajoutez l’en-tête correspondant à l’espace de noms de la classe, puis recompilez. Pour plus d’informations, consultez les en-têtes de projection C++/WinRT.

Pourquoi est-ce que je reçois une exception « classe non inscrite » ?

Quand vous construisez une classe runtime ou accédez à un membre statique, une exception est levée au moment du runtime avec une valeur HRESULT égale à REGDB_E_CLASSNOTREGISTERED.

Une cause peut être que votre composant Windows Runtime ne peut pas être chargé. Assurez-vous que le fichier de métadonnées Windows Runtime du composant (.winmd) porte le même nom que le fichier binaire du composant (le .dll), qui est également le nom du projet et le nom de l'espace de noms racine. Vérifiez également que les métadonnées Windows Runtime et le fichier binaire ont bien été copiés par le processus de génération dans le dossier Appx de l’application cliente. Et vérifiez que l’application AppxManifest.xml consommatrice (également dans le Appx dossier) contient un <élément InProcessServer> qui déclare correctement la classe activable et le nom binaire.

Construction uniforme Cette erreur peut également se produire si vous essayez d’instancier une classe runtime implémentée localement via l’un des constructeurs du type projeté (autre que son constructeur std ::nullptr_t ). Pour ce faire, vous aurez besoin de la fonctionnalité C++/WinRT 2.0 souvent appelée construction uniforme. Si vous souhaitez activer cette fonctionnalité, pour en savoir plus et consulter des exemples de code, voir Activer la construction uniforme et l’accès direct à l’implémentation.

Pour une façon d’instancier vos classes runtime implémentées localement qui ne nécessitent pas de construction uniforme, consultez les contrôles XAML ; liez-vous à une propriété C++/WinRT.

Dois-je implémenter Windows ::Foundation ::IClosable et, le cas échéant, comment ?

Si vous disposez d'une classe runtime qui libère des ressources dans son destructeur et que cette classe runtime est conçue pour être consommée en dehors de son unité de compilation d'implémentation (il s'agit d'un composant Windows Runtime destiné à une consommation générale par Windows Runtime applications clientes), nous vous recommandons d'implémenter également IClosable afin de prendre en charge la consommation de votre classe runtime par langages qui manquent de finalisation déterministe. Vérifiez que vos ressources sont libérées si le destructeur, IClosable::Close ou les deux sont appelés. IClosable ::Close peut être appelé un nombre arbitraire de fois.

Dois-je appeler IClosable::Close sur les classes d’exécution que j’utilise ?

IClosable existe pour prendre en charge les langues qui n’ont pas de finalisation déterministe. Par conséquent, en général, vous n’avez pas besoin d’appeler IClosable ::Close à partir de C++/WinRT. Mais considérez ces exceptions à cette règle générale.

  • Il y a des cas très rares impliquant des concurrences de fermeture ou des étreintes semi-fatales (semi-deadly embraces), où vous devez appeler IClosable::Close. Si vous utilisez des types Windows.UI.Composition, par exemple, vous pouvez rencontrer des cas où vous souhaiterez disposer des objets dans une séquence définie, au lieu de laisser la destruction du wrapper C++/WinRT faire le travail pour vous.
  • Si vous ne pouvez pas garantir que vous disposez de la dernière référence restante à un objet (car vous l’avez passé à d’autres API, qui pourraient conserver une référence), l’appel d’IClosable ::Close est une bonne idée.
  • En cas de doute, vous pouvez appeler IClosable::Close manuellement sans problème, au lieu d’attendre que le wrapper l’appelle lors de la destruction.

Ainsi, si vous savez que vous disposez de la dernière référence, vous pouvez laisser le destructeur de wrapper faire le travail. Si vous devez fermer avant que la dernière référence ne disparaît, vous devez appeler Close. Pour parer à toute exception, appelez Close dans un type RAII (Resource-Acquisition-Is-Initialization) pour que la fermeture ait lieu au moment du déroulement. C++/WinRT n’a pas de wrapper unique_close , mais vous pouvez en faire votre propre.

Puis-je utiliser LLVM/Clang pour compiler avec C++/WinRT ?

Nous ne prenons pas en charge la chaîne d’outils LLVM et Clang pour C++/WinRT, mais nous l’utilisons en interne pour valider la conformité aux normes de C++/WinRT. Par exemple, si vous souhaitez émuler ce que nous faisons en interne, vous pouvez essayer une expérience telle que celle décrite ci-dessous.

Accédez à la page de téléchargement LLVM, recherchez Télécharger LLVM 6.0.0>binaires précompilés, et téléchargez Clang pour Windows (64 bits). Pendant l’installation, choisissez d’ajouter LLVM à la variable système PATH afin de pouvoir l’appeler à partir d’une invite de commandes. Pour les besoins de cette expérience, vous pouvez ignorer les erreurs « Échec de la recherche d’ensembles d’outils MSBuild » et/ou « Échec de l’installation d’intégration MSVC », si vous les voyez. Il existe différentes façons d’appeler LLVM/Clang ; l’exemple ci-dessous montre une seule façon.

C:\ExperimentWithLLVMClang>type main.cpp
// main.cpp
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "ole32")

#include <winrt/Windows.Foundation.h>
#include <stdio.h>
#include <iostream>

using namespace winrt;

int main()
{
    winrt::init_apartment();
    Windows::Foundation::Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    std::wcout << rssFeedUri.Domain().c_str() << std::endl;
}

C:\ExperimentWithLLVMClang>clang-cl main.cpp /EHsc /I ..\.. -Xclang -std=c++17 -Xclang -Wno-delete-non-virtual-dtor -o app.exe

C:\ExperimentWithLLVMClang>app
windows.com

Étant donné que C++/WinRT utilise des fonctionnalités de la norme C++17, vous devez utiliser les indicateurs de compilateur nécessaires pour obtenir cette prise en charge ; ces indicateurs diffèrent d’un compilateur à un autre.

Visual Studio est l’outil de développement que nous prenons en charge et recommandons pour C++/WinRT. Consultez la prise en charge de C++/WinRT dans Visual Studio.

Pourquoi la fonction d’implémentation générée pour une propriété en lecture seule n’a-t-elle pas le qualificateur const ?

Lorsque vous déclarez une propriété en lecture seule dans MIDL 3.0, vous pouvez vous attendre à ce que l’outil cppwinrt.exe génère une fonction d’implémentation pour vous qui est const-qualifiée (une fonction const traite ce pointeur comme const).

Nous recommandons bien entendu d’utiliser const chaque fois que possible, mais l’outil cppwinrt.exe lui-même n’essaie pas de déterminer quelles fonctions d’implémentation pourraient éventuellement être const et lesquelles ne le pourraient pas. Vous pouvez choisir de déclarer const n’importe laquelle de vos fonctions d’implémentation, comme dans cet exemple.

struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
    winrt::hstring ToString() const
    {
        return L"MyStringable";
    }
};

Vous pouvez supprimer ce const qualificateur sur ToString si vous décidez que vous devez modifier un état d’objet dans son implémentation. Mais rendez chacune de vos fonctions membres const ou non-const, pas les deux. En d’autres termes, ne surchargez pas une fonction d’implémentation sur const.

Outre vos fonctions d’implémentation, un autre cas où const entre en jeu est celui des projections de fonctions de Windows Runtime. Considérez ce code.

int main()
{
    winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
    auto result{ s.ToString() };
}

Pour l’appel à ToString ci-dessus, la commande Go To Declaration dans Visual Studio montre que la projection du Windows Runtime IStringable ::ToString dans C++/WinRT ressemble à ceci.

winrt::hstring ToString() const;

Les fonctions sur la projection sont de type const, quelle que soit la façon dont vous choisissez de qualifier leur implémentation. En arrière-plan, la projection appelle l’interface binaire d’application (ABI), qui équivaut à un appel via un pointeur d’interface COM. Le seul état avec lequel l’objet ToString projeté interagit est que le pointeur d’interface COM ; et il n’a certainement pas besoin de modifier ce pointeur, donc la fonction est const. Cela vous assure que cela ne modifiera en rien la référence IStringable via laquelle vous effectuez l’appel, et garantit que vous pouvez appeler ToString même avec une référence const à un IStringable.

Sachez que ces exemples de const sont des détails d’implémentation de projections et d’implémentations C++/WinRT ; ils constituent une hygiène de code pour votre bénéfice. Il n’existe rien de tel que const dans l’ABI de COM ou de Windows Runtime (pour les fonctions membres).

Avez-vous des recommandations pour réduire la taille du code pour les fichiers binaires C++/WinRT ?

Lorsque vous utilisez des objets Windows Runtime, vous devez éviter le modèle de codage indiqué ci-dessous, car il peut avoir un impact négatif sur votre application en provoquant plus de code binaire que nécessaire pour être généré.

anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();

Dans l’environnement Windows Runtime, le compilateur ne peut mettre en cache ni la valeur de c(), ni les interfaces de chaque méthode appelée via une indirection ('.'). À moins d’intervenir, cela entraîne davantage d’appels virtuels et de surcharge de comptage de références. Le modèle ci-dessus peut facilement générer deux fois plus de code que nécessaire. Au lieu de cela, préférez le modèle indiqué ci-dessous, où que vous puissiez. Il génère beaucoup moins de code et peut également améliorer considérablement les performances de votre exécution.

auto a{ anobject.b().c() };
a.d();
a.e();
a.f();

Le modèle recommandé ci-dessus s’applique non seulement à C++/WinRT, mais à toutes les projections de langage Windows Runtime.

Comment convertir une chaîne en un type (pour la navigation, par exemple) ?

À la fin de l’exemple de code de vue de navigation (qui est principalement en C#), il existe un extrait de code C++/WinRT montrant comment procéder.

Comment résoudre les ambiguïtés avec GetCurrentTime et/ou TRY ?

Le fichier winrt/Windows.UI.Xaml.Media.Animation.h d’en-tête déclare une méthode nommée GetCurrentTime, tandis que windows.h (via winbase.h) définit une macro nommée GetCurrentTime. Lorsque les deux collisions se produisent, le compilateur C++ génère « erreur C4002 : trop d’arguments pour l’appel de macro de type fonction GetCurrentTime ».

De même, winrt/Windows.Globalization.h déclare une méthode nommée TRY, tandis que afx.h définit une macro nommée TRY. Lorsque cela se produit, le compilateur C++ produit « erreur C2334 : token(s) inattendu(s) avant « { » ; corps de fonction apparent ignoré ».

Pour résoudre un ou les deux problèmes, vous pouvez le faire.

#pragma push_macro("GetCurrentTime")
#pragma push_macro("TRY")
#undef GetCurrentTime
#undef TRY
#include <winrt/include_your_cppwinrt_headers_here.h>
#include <winrt/include_your_cppwinrt_headers_here.h>
#pragma pop_macro("TRY")
#pragma pop_macro("GetCurrentTime")

Comment accélérer le chargement des symboles ?

Dans Visual Studio, Outils>Options>Débogage>Symboles>, cochez Charger uniquement les modules spécifiés. Vous pouvez ensuite cliquer avec le bouton droit sur des DLL dans la liste des piles et charger des modules individuels.

Note

Si cette rubrique n'a pas répondu à votre question, vous pouvez trouver de l'aide en visitant la communauté des développeurs Visual Studio C++ ou en utilisant la c++-winrt balise sur Stack Overflow.