Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cette rubrique est la première d’une série décrivant comment porter le code source dans votre projet C++/CX vers son équivalent en C++/WinRT.
Si votre projet utilise également les types de la bibliothèque de modèles C++ Windows Runtime (WRL), consultez Migrer de WRL vers C++/WinRT.
Stratégies de portage
Il est intéressant de savoir que le portage de C++/CX vers C++/WinRT est généralement simple, à l’exception du passage de tâches PPL (Parallel Patterns Library) aux coroutines. Les modèles sont différents. Il n’existe pas de mappage un-à-un naturel des tâches PPL aux coroutines, et il n’existe aucun moyen simple de porter mécaniquement le code qui fonctionne pour tous les cas. Pour obtenir de l’aide sur cet aspect spécifique du portage et sur vos options d’interopérabilité entre les deux modèles, consultez Asynchronie et interopérabilité entre C++/WinRT et C++/CX.
Les équipes de développement signalent régulièrement qu’une fois qu’elles sont sur le point de porter leur code asynchrone, le reste du travail de portage est en grande partie mécanique.
Transfert en une seule opération
Si vous êtes en mesure de porter l’intégralité de votre projet en une seule passe, vous n’aurez besoin que de cette rubrique pour les informations dont vous avez besoin (et vous n’aurez pas besoin des rubriques d’interopérabilité qui suivent celle-ci). Nous vous recommandons de commencer par créer un projet dans Visual Studio à l’aide de l’un des modèles de projet C++/WinRT (consultez Visual Studio prise en charge de C++/WinRT). Déplacez ensuite vos fichiers de code source vers ce nouveau projet, puis transférez tout le code source C++/CX vers C++/WinRT comme vous le faites.
Sinon, si vous préférez effectuer le portage dans votre projet C++/CX existant, vous devez y ajouter la prise en charge de C++/WinRT. Les étapes que vous suivez pour ce faire sont décrites dans Prise d’un projet C++/CX et ajout de la prise en charge de C++/WinRT. Au moment où vous avez terminé le portage, vous aurez transformé ce qui était un projet C++/CX pur en un projet C++/WinRT pur.
Note
Si vous avez un projet de composant Windows Runtime, le portage d’une seule passe est votre seule option. Un projet de composant Windows Runtime écrit en C++ doit contenir tout le code source C++/CX ou tout le code source C++/WinRT. Ils ne peuvent pas coexister dans ce type de projet.
Portage progressif d’un projet
À l'exception de Windows Runtime projets de composants, comme indiqué dans la section précédente, si la taille ou la complexité de votre codebase rend nécessaire le portage progressif de votre projet, vous aurez besoin d'un processus de portage dans lequel le code C++/CX et C++/WinRT existe côte à côte dans le même projet. En plus de lire cette rubrique, consultez également Interop entre C++/WinRT et C++/CX et Asynchrony, et interopérabilité entre C++/WinRT et C++/CX. Ces rubriques fournissent des informations et des exemples de code montrant comment interagir entre les deux projections de langage.
Pour préparer un projet pour un processus de portage progressif, une option consiste à ajouter la prise en charge de C++/WinRT à votre projet C++/CX. Les étapes que vous suivez pour ce faire sont décrites dans Prise d’un projet C++/CX et ajout de la prise en charge de C++/WinRT. Vous pourrez ensuite migrer progressivement.
Une autre option consiste à créer un projet dans Visual Studio à l’aide de l’un des modèles de projet C++/WinRT (consultez Visual Studio prise en charge de C++/WinRT). Ajoutez ensuite la prise en charge de C++/CX à ce projet. Les étapes que vous suivez pour ce faire sont décrites dans Prise d’un projet C++/WinRT et ajout de la prise en charge de C++/CX. Vous pouvez ensuite commencer à déplacer votre code source vers celui-ci et à porter certains du code source C++/CX vers C++/WinRT comme vous le faites.
Dans les deux cas, vous allez interagir (de deux façons) entre votre code C++/WinRT et tout code C++/CX que vous n’avez pas encore porté.
Note
C++/CX et le SDK Windows déclarent des types dans l’espace de noms racine Windows. Un type Windows projeté en C++/WinRT a le même nom complet que le type Windows, mais il est placé dans l'espace de noms winrt C++. Ces espaces de noms distincts vous permettent de porter de C++/CX vers C++/WinRT à votre rythme.
Portage progressif d’un projet XAML
Important
Pour un projet qui utilise XAML, à tout moment, tous vos types de pages XAML doivent être entièrement C++/CX ou entièrement C++/WinRT. Vous pouvez toujours combiner C++/CX et C++/WinRT en dehors des types de pages XAML dans le même projet (dans vos modèles et modèles d’affichage, et ailleurs).
Pour ce scénario, le flux de travail que nous vous recommandons est de créer un projet C++/WinRT et de copier le code source et le balisage à partir du projet C++/CX. Tant que tous vos types de pages XAML sont C++/WinRT, vous pouvez ajouter de nouvelles pages XAML avec Project>Add New Item...>Visual C++>Page vide (C++/WinRT).
Vous pouvez également utiliser un composant Windows Runtime (WRC) pour factoriser le code du projet XAML C++/CX au fur et à mesure que vous le portez.
- Vous pouvez créer un projet WRC C++/CX, déplacer autant de code C++/CX que possible dans ce projet, puis remplacer le projet XAML par C++/WinRT.
- Vous pouvez également créer un projet WRC C++/WinRT, laisser le projet XAML en C++/CX, et commencer à migrer le code de C++/CX vers C++/WinRT, puis à déplacer le code obtenu hors du projet XAML et vers le projet de composant.
- Vous pouvez également avoir un projet de composant C++/CX en même temps qu’un projet de composant C++/WinRT au sein de la même solution, les référencer à partir de votre projet d’application et porter progressivement l’un vers l’autre. Là encore, consultez Interop entre C++/WinRT et C++/CX pour plus d’informations sur l’utilisation des deux projections de langage dans le même projet.
Premières étapes du portage d’un projet C++/CX vers C++/WinRT
Quelle que soit votre stratégie de portage (portage en une seule passe ou portage progressif), votre première étape consiste à préparer votre projet pour le portage. Voici un récapitulatif de ce que nous avons décrit dans Stratégies de portage en termes de type de projet que vous allez commencer et comment le configurer.
- Portage en une seule passe. Créez un projet dans Visual Studio à l’aide de l’un des modèles de projet C++/WinRT. Déplacez les fichiers de votre projet C++/CX vers ce nouveau projet et portez le code source C++/CX.
- Portage progressif d’un projet non XAML. Vous pouvez choisir d’ajouter la prise en charge C++/WinRT à votre projet C++/CX (voir Prise d’un projet C++/CX et ajout de la prise en charge C++/WinRT) et du port progressivement. Vous pouvez également choisir de créer un projet C++/WinRT et d’ajouter la prise en charge C++/CX à ce projet (voir Prise d’un projet C++/WinRT et ajout de la prise en charge C++/CX), déplacer des fichiers et déplacer progressivement les fichiers.
- Portage progressif d’un projet XAML. Créez un nouveau projet C++/WinRT, déplacez les fichiers et effectuez le portage progressivement. À tout moment, vos types de pages XAML doivent être tous C++/WinRT ou C++/CX.
Le reste de cette rubrique s’applique quelle que soit la stratégie de portage que vous choisissez. Il contient un catalogue de détails techniques impliqués dans le portage du code source de C++/CX vers C++/WinRT. Si vous effectuez un portage progressif, vous voudrez probablement également voir Interop entre C++/WinRT et C++/CXet Asynchrony, et l’interopérabilité entre C++/WinRT et C++/CX.
Conventions d’affectation des noms de fichiers
Fichiers de balisage XAML
| Origine du fichier | C++/CX | C++/WinRT |
|---|---|---|
| Fichiers XAML du développeur | MyPage.xaml MyPage.xaml.h MyPage.xaml.cpp |
MyPage.xaml MyPage.h MyPage.cpp MyPage.idl (voir ci-dessous) |
| Fichiers XAML générés | MyPage.xaml.g.h MyPage.xaml.g.hpp |
MyPage.xaml.g.h MyPage.xaml.g.hpp MyPage.g.h |
Notez que C++/WinRT supprime le .xaml des noms de fichiers *.h et *.cpp.
C++/WinRT ajoute un fichier développeur supplémentaire, le fichier Midl (.idl). C++/CX génère automatiquement ce fichier en interne, en lui ajoutant chaque membre public et protégé. Dans C++/WinRT, vous ajoutez et créez le fichier vous-même. Pour plus d’informations, des exemples de code et une procédure pas à pas de création d’IDL, consultez les contrôles XAML ; liaison à une propriété C++/WinRT.
Consultez également Déplacer les classes d’exécution vers des fichiers MIDL (.idl)
Classes d’exécution
C++/CX n’impose pas de restrictions sur les noms de vos fichiers d’en-tête ; il est courant de placer plusieurs définitions de classes runtime dans un seul fichier d’en-tête, en particulier pour les petites classes. Toutefois, C++/WinRT exige que chaque classe runtime possède son propre fichier d’en-tête nommé après le nom de la classe.
| C++/CX | C++/WinRT |
|---|---|
Common.href class A { ... }ref class B { ... } |
Common.idlruntimeclass A { ... }runtimeclass B { ... } |
A.hnamespace implements {struct A { ... };} |
|
B.hnamespace implements {struct B { ... };} |
Moins courant (mais toujours légal) dans C++/CX consiste à utiliser des fichiers d’en-tête nommés différemment pour les contrôles personnalisés XAML. Vous devez renommer ce fichier d’en-tête pour qu’il corresponde au nom de la classe.
| C++/CX | C++/WinRT |
|---|---|
A.xaml<Page x:Class="LongNameForA" ...> |
A.xaml<Page x:Class="LongNameForA" ...> |
A.hpartial ref class LongNameForA { ... } |
LongNameForA.hnamespace implements {struct LongNameForA { ... };} |
Configuration requise pour le fichier d’en-tête
C++/CX ne vous oblige pas à inclure de fichiers d’en-tête spéciaux, car il génère automatiquement automatiquement des fichiers d’en-tête à partir de .winmd fichiers. Il est courant, en C++/CX, d’utiliser des directives using pour les espaces de noms auxquels vous faites référence par leur nom.
using namespace Windows::Media::Playback;
String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
return item->VideoTracks->GetAt(0)->Name;
}
La using namespace Windows::Media::Playback directive nous permet d’écrire MediaPlaybackItem sans préfixe d’espace de noms. Nous avons également modifié l’espace de noms Windows.Media.Core, car item->VideoTracks->GetAt(0) renvoie un Windows.Media.Core.VideoTrack. Mais nous n'avons pas dû taper le nom VideoTrack n'importe où, donc nous n'avons pas besoin d'une using Windows.Media.Core directive.
Toutefois, C++/WinRT vous oblige à inclure un fichier d’en-tête correspondant à chaque espace de noms que vous consommez, même si vous ne le nommez pas.
#include <winrt/Windows.Media.Playback.h>
#include <winrt/Windows.Media.Core.h> // !!This is important!!
using namespace winrt;
using namespace Windows::Media::Playback;
winrt::hstring NameOfFirstVideoTrack(MediaPlaybackItem const& item)
{
return item.VideoTracks().GetAt(0).Name();
}
En revanche, même si l’événement MediaPlaybackItem.AudioTracksChanged est de type TypedEventHandler<MediaPlaybackItem, Windows. Foundation.Collections.IVectorChangedEventArgs>, nous n'avons pas besoin d'inclure winrt/Windows.Foundation.Collections.h car nous n'avons pas utilisé cet événement.
C++/WinRT vous oblige également à inclure des fichiers d’en-tête pour les espaces de noms consommés par le balisage XAML.
<!-- MainPage.xaml -->
<Rectangle Height="400"/>
L’utilisation de la classe Rectangle signifie que vous devez ajouter cet élément.
// MainPage.h
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
Si vous oubliez un fichier d’en-tête, tout se compilera correctement, mais vous obtiendrez des erreurs de l’éditeur de liens, parce que les classes consume_ sont absentes.
Passage de paramètres
Lorsque vous écrivez du code source C++/CX, vous passez les types C++/CX en tant que paramètres de fonction en tant que références hat (^).
void LogPresenceRecord(PresenceRecord^ record);
Dans C++/WinRT, pour les fonctions synchrones, vous devez utiliser const& des paramètres par défaut. Cela évitera les copies et le surcoût lié aux verrous. Toutefois, vos coroutines doivent utiliser le passage par valeur pour s’assurer de capturer par valeur et d’éviter les problèmes de durée de vie (pour plus d’informations, consultez Concurrence et opérations asynchrones avec C++/WinRT).
void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);
Un objet C++/WinRT est essentiellement une valeur qui contient un pointeur d’interface sur l’objet Windows Runtime sous-jacent. Lorsque vous copiez un objet C++/WinRT, le compilateur copie le pointeur d’interface encapsulé, incrémentant son nombre de références. La destruction éventuelle de la copie implique de décrémenter le nombre de références. N’encourez donc le surcoût d’une copie que lorsque cela est nécessaire.
Références de variables et de champs
Lorsque vous écrivez du code source C++/CX, vous utilisez des variables avec accent circonflexe (^) pour faire référence à des objets Windows Runtime, et l’opérateur flèche (->) pour déréférencer une variable avec accent circonflexe.
IVectorView<User^>^ userList = User::Users;
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
...
Lors du portage vers le code C++/WinRT équivalent, vous pouvez déjà aller loin en supprimant les chapeaux et en remplaçant l’opérateur de flèche (->) par l’opérateur point (.). Les types projetés C++/WinRT sont des valeurs et non des pointeurs.
IVectorView<User> userList = User::Users();
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
...
Le constructeur par défaut d’une référence de chapeau C++/CX l’initialise sur null. Voici un exemple de code C++/CX dans lequel nous créons une variable/champ du type correct, mais une variable non initialisée. En d’autres termes, il ne fait pas référence initialement à un TextBlock ; nous avons l’intention d’attribuer une référence ultérieurement.
TextBlock^ textBlock;
class MyClass
{
TextBlock^ textBlock;
};
Pour obtenir l’équivalent en C++/WinRT, consultez Initialisation différée.
Properties
Les extensions de langage C++/CX incluent le concept de propriétés. Lorsque vous écrivez du code source C++/CX, vous pouvez accéder à une propriété comme s’il s’agissait d’un champ. C++ standard n’a pas le concept d’une propriété. Dans C++/WinRT, vous appelez des fonctions get et set.
Dans les exemples qui suivent, XboxUserId, UserState, PresenceDeviceRecords et Size sont toutes les propriétés.
Récupération d’une valeur à partir d’une propriété
Voici comment obtenir une valeur de propriété en C++/CX.
void Sample::LogPresenceRecord(PresenceRecord^ record)
{
auto id = record->XboxUserId;
auto state = record->UserState;
auto size = record->PresenceDeviceRecords->Size;
}
Le code source C++/WinRT équivalent appelle une fonction portant le même nom que la propriété, mais sans paramètres.
void Sample::LogPresenceRecord(PresenceRecord const& record)
{
auto id = record.XboxUserId();
auto state = record.UserState();
auto size = record.PresenceDeviceRecords().Size();
}
Notez que la fonction PresenceDeviceRecords retourne un objet Windows Runtime qui a lui-même une fonction Size. Comme l’objet retourné est également un type projeté C++/WinRT, nous déréférons à l’aide de l’opérateur dot pour appeler Size.
Définition d’une propriété à une nouvelle valeur
La définition d’une propriété sur une nouvelle valeur suit un modèle similaire. Tout d’abord, en C++/CX.
record->UserState = newValue;
Pour effectuer l’équivalent en C++/WinRT, vous appelez une fonction portant le même nom que la propriété et transmettez un argument.
record.UserState(newValue);
Création d’une instance d’une classe
Vous travaillez avec un objet C++/CX par le biais d’un handle, communément appelé référence de chapeau (^). Vous créez un objet via le ref new mot clé, qui appelle à son tour RoActivateInstance pour activer une nouvelle instance de la classe runtime.
using namespace Windows::Storage::Streams;
class Sample
{
private:
Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};
Un objet C++/WinRT est une valeur ; vous pouvez donc l’allouer sur la pile ou en tant que champ d’un objet. Vous n’utilisezref new jamais (ni new) pour allouer un objet C++/WinRT. En arrière-plan, RoActivateInstance est toujours appelé.
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
private:
Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};
Si une ressource est coûteuse à initialiser, il est courant de retarder l’initialisation jusqu’à ce qu’elle soit réellement nécessaire. Comme indiqué précédemment, le constructeur par défaut d’une référence hat C++/CX l’initialise à null.
using namespace Windows::Storage::Streams;
class Sample
{
public:
void DelayedInit()
{
// Allocate the actual buffer.
m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
}
private:
Buffer^ m_gamerPicBuffer;
};
Le même code porté vers C++/WinRT. Notez l’utilisation du constructeur std ::nullptr_t . Pour plus d’informations sur ce constructeur, consultez Initialisation différée.
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
void DelayedInit()
{
// Allocate the actual buffer.
m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
}
private:
Buffer m_gamerPicBuffer{ nullptr };
};
Comment le constructeur par défaut affecte les collections
Les types de collection C++ utilisent le constructeur par défaut, ce qui peut entraîner une construction d’objet inattendue.
| Scénario | C++/CX | C++/WinRT (incorrect) | C++/WinRT (correct) |
|---|---|---|---|
| Variable locale, initialement vide | TextBox^ textBox; |
TextBox textBox; // Creates a TextBox! |
TextBox textBox{ nullptr }; |
| Variable membre, initialement vide | class C {TextBox^ textBox;}; |
class C {TextBox textBox; // Creates a TextBox!}; |
class C {TextBox textbox{ nullptr };}; |
| Variable globale, initialement vide | TextBox^ g_textBox; |
TextBox g_textBox; // Creates a TextBox! |
TextBox g_textBox{ nullptr }; |
| Vecteur de références vides | std::vector<TextBox^> boxes(10); |
// Creates 10 TextBox objects!std::vector<TextBox> boxes(10); |
std::vector<TextBox> boxes(10, nullptr); |
| Définir une valeur dans une carte | std::map<int, TextBox^> boxes;boxes[2] = value; |
std::map<int, TextBox> boxes;// Creates a TextBox at 2,// then overwrites it!boxes[2] = value; |
std::map<int, TextBox> boxes;boxes.insert_or_assign(2, value); |
| Tableau de références vides | TextBox^ boxes[2]; |
// Creates 2 TextBox objects!TextBox boxes[2]; |
TextBox boxes[2] = { nullptr, nullptr }; |
| Paire | std::pair<TextBox^, String^> p; |
// Creates a TextBox!std::pair<TextBox, String> p; |
std::pair<TextBox, String> p{ nullptr, nullptr }; |
En savoir plus sur les collections de références vides
Chaque fois que vous disposez d’une plateforme ::Array^ (voir Port Platform ::Array^) en C++/CX, vous avez le choix de le porter vers un std ::vector en C++/WinRT (en fait, n’importe quel conteneur contigu) plutôt que de le laisser comme tableau. Il existe des avantages pour choisir std ::vector.
Par exemple, s’il existe un raccourci pour créer un vecteur de références vides de taille fixe (voir le tableau ci-dessus), il n’existe aucun raccourci pour créer un tableau de références vides. Vous devez répéter nullptr pour chaque élément d’un tableau. Si vous avez trop peu, les extras seront construits par défaut.
Pour un vecteur, vous pouvez le remplir avec des références vides lors de l’initialisation (comme dans le tableau ci-dessus), ou vous pouvez le remplir avec des références vides après l’initialisation avec du code tel que celui-ci.
std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.
En savoir plus sur l’exemple std ::map
L’opérateur [] de std::map se comporte comme suit.
- Si la clé est trouvée dans la carte, retournez une référence à la valeur existante (que vous pouvez remplacer).
- Si la clé est introuvable dans la carte, créez une entrée dans la carte composée de la clé (déplacée, si mobile) et d’une valeur construite par défaut, puis retournez une référence à la valeur (que vous pouvez remplacer).
En d’autres termes, l’opérateur [] crée toujours une entrée dans la carte. Ceci est différent de C#, Java et JavaScript.
Conversion d’une classe runtime de base en une classe dérivée
Il est courant d’avoir une référence à une classe de base dont on sait qu’elle fait référence à un objet d’un type dérivé. Dans C++/CX, vous utilisez dynamic_cast pour convertir la référence à la base en référence à dérivée. Le dynamic_cast n’est en réalité qu’un appel caché à QueryInterface. Voici un exemple classique : vous gérez un événement de modification de propriété de dépendance et vous souhaitez effectuer un cast de DependencyObject vers le type réel propriétaire de la propriété de dépendance.
void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject^ d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };
if (theControl != nullptr)
{
// succeeded ...
}
}
Le code C++/WinRT équivalent remplace l’appel dynamic_cast à la fonction IUnknown ::try_as , qui encapsule QueryInterface. Vous pouvez également appeler IUnknown::as, ce qui lève une exception si la requête de l’interface requise (l’interface par défaut du type que vous demandez) n’aboutit pas. Voici un exemple de code C++/WinRT.
void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject const& d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
{
if (BgLabelControlApp::BgLabelControl theControl{ d.try_as<BgLabelControlApp::BgLabelControl>() })
{
// succeeded ...
}
try
{
BgLabelControlApp::BgLabelControl theControl{ d.as<BgLabelControlApp::BgLabelControl>() };
// succeeded ...
}
catch (winrt::hresult_no_interface const&)
{
// failed ...
}
}
Classes dérivées
Pour dériver d’une classe runtime, la classe de base doit être composable. C++/CX ne nécessite aucune mesure particulière pour rendre vos classes composables, mais C++/WinRT l’exige. Vous utilisez le mot clé non scellé pour indiquer que votre classe doit être utilisable en tant que classe de base.
unsealed runtimeclass BasePage : Microsoft.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
Dans votre classe d’en-tête d’implémentation, vous devez inclure le fichier d’en-tête de classe de base avant d’inclure l’en-tête généré automatiquement pour la classe dérivée. Sinon, vous obtiendrez des erreurs telles que « Utilisation illégale de ce type en tant qu’expression ».
// DerivedPage.h
#include "BasePage.h" // This comes first.
#include "DerivedPage.g.h" // Otherwise this header file will produce an error.
namespace winrt::MyNamespace::implementation
{
struct DerivedPage : DerivedPageT<DerivedPage>
{
...
}
}
Gestion des événements avec un délégué
Voici un exemple classique de gestion d’un événement en C++/CX, à l’aide d’une fonction lambda en tant que délégué dans ce cas.
auto token = myButton->Click += ref new RoutedEventHandler([=](Platform::Object^ sender, RoutedEventArgs^ args)
{
// Handle the event.
// Note: locals are captured by value, not reference, since this handler is delayed.
});
Il s’agit de l’équivalent en C++/WinRT.
auto token = myButton().Click([=](IInspectable const& sender, RoutedEventArgs const& args)
{
// Handle the event.
// Note: locals are captured by value, not reference, since this handler is delayed.
});
Au lieu d’une fonction lambda, vous pouvez choisir d’implémenter votre délégué en tant que fonction libre ou en tant que pointeur vers une fonction membre. Pour plus d’informations, consultez Gérer les événements à l’aide de délégués en C++/WinRT.
Si vous effectuez un portage à partir d’une base de code C++/CX où les événements et les délégués sont utilisés en interne (pas entre les fichiers binaires), winrt ::d elegate vous aidera à répliquer ce modèle en C++/WinRT. Consultez également les délégués paramétrables, les signaux simples et les rappels au sein d’un projet.
Révocation d’un délégué
En C++/CX, vous utilisez l’opérateur -= pour révoquer une inscription d’événement antérieure.
myButton->Click -= token;
Il s’agit de l’équivalent en C++/WinRT.
myButton().Click(token);
Pour plus d’informations et d’options, consultez Révoquer un délégué inscrit.
Boxing et unboxing
C++/CX place automatiquement les scalaires en objets. C++/WinRT vous oblige à appeler explicitement la fonction winrt ::box_value . Les deux langages exigent un déballage explicite. Consultez Boxing et unboxing avec C++/WinRT.
Dans les tables qui suivent, nous allons utiliser ces définitions.
| C++/CX | C++/WinRT |
|---|---|
int i; |
int i; |
String^ s; |
winrt::hstring s; |
Object^ o; |
IInspectable o; |
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Boxe | o = 1;o = "string"; |
o = box_value(1);o = box_value(L"string"); |
| Déballage | i = (int)o;s = (String^)o; |
i = unbox_value<int>(o);s = unbox_value<winrt::hstring>(o); |
C++/CX et C# déclenchent des exceptions si vous essayez de dissocier un pointeur Null vers un type valeur. C++/WinRT considère cette erreur de programmation et se bloque. Dans C++/WinRT, utilisez la fonction winrt ::unbox_value_or si vous souhaitez gérer le cas où l’objet n’est pas du type que vous pensiez.
| Scénario | C++/CX | C++/WinRT |
|---|---|---|
| Déboxer un entier connu | i = (int)o; |
i = unbox_value<int>(o); |
| Si o a la valeur Null | Platform::NullReferenceException |
Krach |
| Si o n’est pas un int encapsulé | Platform::InvalidCastException |
Krach |
| Déballer l’entier, utiliser la valeur de repli si null ; planter dans tous les autres cas | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Déballer un int si possible ; utiliser une solution de repli dans tous les autres cas | auto box = dynamic_cast<IBox<int>^>(o);i = box ? box->Value : fallback; |
i = unbox_value_or<int>(o, fallback); |
Boxing et unboxing d’une chaîne
Une chaîne est d’une certaine manière un type valeur et d’autres façons un type référence. C++/CX et C++/WinRT traitent les chaînes différemment.
Le type ABI HSTRING est un pointeur vers une chaîne comptée par référence. Mais il ne dérive pas d’IInspectable, donc il n’est pas techniquement un objet. En outre, un HSTRING null représente la chaîne vide. L’encapsulage des éléments qui ne dérivent pas de IInspectable s’effectue en les enveloppant dans un IReference<T>, et Windows Runtime fournit une implémentation standard sous la forme de l’objet PropertyValue (les types personnalisés sont signalés comme PropertyType::OtherType).
C++/CX représente une chaîne Windows Runtime en tant que type référence ; tandis que C++/WinRT projette une chaîne en tant que type valeur. Cela signifie qu’une chaîne null boxée peut avoir différentes représentations selon la façon dont vous y êtes arrivé.
En outre, C++/CX vous permet de déréférer une chaîne null^, auquel cas elle se comporte comme la chaîne "".
| Behavior | C++/CX | C++/WinRT |
|---|---|---|
| Déclarations | Object^ o;String^ s; |
IInspectable o;hstring s; |
| Catégorie de type chaîne | Type de référence | Type de valeur |
| null HSTRING est projeté comme | (String^)nullptr |
hstring{} |
null et "" sont-ils identiques ? |
Yes | Yes |
| Validité de null | s = nullptr;s->Length == 0 (valide) |
s = hstring{};s.size() == 0 (valide) |
| Si vous attribuez une chaîne Null à un objet | o = (String^)nullptr;o == nullptr |
o = box_value(hstring{});o != nullptr |
Si vous attribuez "" à l’objet |
o = "";o == nullptr |
o = box_value(hstring{L""});o != nullptr |
Boxing de base et unboxing.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Encadrer une chaîne | o = s;La chaîne vide devient nullptr. |
o = box_value(s);La chaîne vide devient un objet non null. |
| Extraire une chaîne connue | s = (String^)o;L’objet Null devient une chaîne vide. InvalidCastException si ce n’est pas une chaîne. |
s = unbox_value<hstring>(o);L’objet Null se bloque. Plante si ce n’est pas une chaîne. |
| Déballer une chaîne facultative | s = dynamic_cast<String^>(o);Un objet null ou une valeur non textuelle devient une chaîne vide. |
s = unbox_value_or<hstring>(o, fallback);Une valeur nulle ou non textuelle devient la valeur de repli. Chaîne vide conservée. |
Concurrence et opérations asynchrones
La Bibliothèque de modèles parallèles (PPL) (concurrency::task, par exemple) a été mise à jour afin de prendre en charge les *hat references* de C++/CX.
Pour C++/WinRT, vous devez utiliser les coroutines et co_await à la place. Pour plus d’informations et des exemples de code, consultez Accès concurrentiel et opérations asynchrones avec C++/WinRT.
Consommation d’objets à partir du balisage XAML
Dans un projet C++/CX, vous pouvez consommer des membres privés et des éléments nommés à partir du balisage XAML. Toutefois, dans C++/WinRT, toutes les entités consommées à l’aide de l’extension de balisage XAML {x :Bind} doivent être exposées publiquement dans IDL.
En outre, la liaison à un booléen affiche true ou false en C++/CX, mais elle affiche Windows. Foundation.IReference'1<Boolean> en C++/WinRT.
Pour plus d’informations et pour obtenir des exemples de code, consultez Consommation d’objets à partir du balisage.
Correspondance des types C++/CX Platform avec les types C++/WinRT
C++/CX fournit plusieurs types de données dans l’espace de noms Platform . Ces types ne sont pas standard C++. Vous pouvez donc les utiliser uniquement lorsque vous activez Windows Runtime extensions de langage (Visual Studio propriété de projet C/C++>Général>Consommer Windows Runtime Extension>Oui (/ZW)). Le tableau ci-dessous vous aide à transférer des types de plateforme vers leurs équivalents en C++/WinRT. Une fois que vous avez terminé cela, étant donné que C++/WinRT est standard C++, vous pouvez désactiver l’option /ZW .
| C++/CX | C++/WinRT |
|---|---|
| Platform ::Agile^ | winrt ::agile_ref |
| Platform ::Array^ | Voir Port Platform::Array^ |
| Platform::Exception^ | winrt::hresult_error |
| Platform::InvalidArgumentException^ | winrt ::hresult_invalid_argument |
| Platform ::Object^ | winrt::Windows::Foundation::IInspectable |
| Platform ::String^ | winrt ::hstring |
Porter Platform::Agile^ vers winrt::agile_ref
Le type Platform ::Agile^ en C++/CX représente une classe Windows Runtime accessible à partir de n’importe quel thread. L’équivalent C++/WinRT est winrt ::agile_ref.
En C++/CX.
Platform::Agile<Windows::UI::Core::CoreWindow> m_window;
Dans C++/WinRT (WinUI 3 utilise Microsoft ::UI ::Xaml ::Window au lieu de CoreWindow).
winrt::agile_ref<Microsoft::UI::Xaml::Window> m_window;
Port Platform::Array^
Dans les cas où C++/CX vous oblige à utiliser un tableau, C++/WinRT vous permet d’utiliser n’importe quel conteneur contigu. Découvrez comment le constructeur par défaut affecte les collections pour une raison pour laquelle std ::vector est un bon choix.
Par conséquent, chaque fois que vous disposez d’une plateforme ::Array^ en C++/CX, vos options de portage incluent l’utilisation d’une liste d’initialiseurs, d’un std ::array ou d’un std ::vector. Pour plus d’informations et des exemples de code, consultez les listes d’initialiseurs standard et les tableaux et vecteurs Standard.
Migrer Platform::Exception^ vers winrt::hresult_error
Le type Platform ::Exception^ est produit en C++/CX lorsqu’une API Windows Runtime retourne un HRESULT non S_OK. L’équivalent C++/WinRT est winrt ::hresult_error.
Pour porter vers C++/WinRT, modifiez tout le code qui utilise Platform ::Exception^ pour utiliser winrt ::hresult_error.
En C++/CX.
catch (Platform::Exception^ ex)
En C++/WinRT.
catch (winrt::hresult_error const& ex)
C++/WinRT fournit ces classes d’exception.
| Type d'exception | Classe de base | HRESULT |
|---|---|---|
| winrt::hresult_error | appeler hresult_error ::to_abi | |
| winrt ::hresult_access_denied | winrt::hresult_error | E_ACCESSDENIED |
| winrt ::hresult_canceled | winrt::hresult_error | ERROR_CANCELLED |
| winrt ::hresult_changed_state | winrt::hresult_error | E_CHANGED_STATE |
| winrt ::hresult_class_not_available | winrt::hresult_error | CLASS_E_CLASSNOTAVAILABLE |
| winrt ::hresult_illegal_delegate_assignment | winrt::hresult_error | E_ILLEGAL_DELEGATE_ASSIGNMENT |
| winrt ::hresult_illegal_method_call | winrt::hresult_error | E_ILLEGAL_METHOD_CALL |
| winrt ::hresult_illegal_state_change | winrt::hresult_error | E_ILLEGAL_STATE_CHANGE |
| winrt ::hresult_invalid_argument | winrt::hresult_error | E_INVALIDARG |
| winrt ::hresult_no_interface | winrt::hresult_error | E_NOINTERFACE |
| winrt ::hresult_not_implemented | winrt::hresult_error | E_NOTIMPL |
| winrt::hresult_out_of_bounds | winrt::hresult_error | E_BOUNDS |
| winrt ::hresult_wrong_thread | winrt::hresult_error | RPC_E_WRONG_THREAD |
Notez que chaque classe (via la classe de base hresult_error ) fournit une fonction to_abi , qui retourne le HRESULT de l’erreur et une fonction de message , qui retourne la représentation sous forme de chaîne de ce HRESULT.
Voici un exemple de levée d’une exception en C++/CX.
throw ref new Platform::InvalidArgumentException(L"A valid User is required");
Et l’équivalent en C++/WinRT.
throw winrt::hresult_invalid_argument{ L"A valid User is required" };
Portage de Platform::Object^ vers winrt::Windows::Foundation::IInspectable
Comme tous les types C++/WinRT, winrt ::Windows ::Foundation ::IInspectable est un type valeur. Voici comment initialiser une variable de ce type sur null.
winrt::Windows::Foundation::IInspectable var{ nullptr };
Migrer Platform::String^ vers winrt::hstring
Platform ::String^ équivaut au type ABI Windows Runtime HSTRING. Pour C++/WinRT, l’équivalent est winrt ::hstring. Toutefois, avec C++/WinRT, vous pouvez appeler Windows Runtime API à l’aide de types de chaînes larges de la bibliothèque C++ Standard, tels que std ::wstring et/ou des littéraux de chaîne large. Pour plus d’informations et des exemples de code, consultez Gestion des chaînes en C++/WinRT.
Avec C++/CX, vous pouvez accéder à la propriété Platform ::String ::D ata pour récupérer la chaîne en tant que tableau const wchar_t* de style C (par exemple, pour le transmettre à std ::wcout).
auto var{ titleRecord->TitleName->Data() };
Pour faire de même avec C++/WinRT, vous pouvez utiliser la fonction hstring ::c_str pour obtenir une version de chaîne de style C terminée par null, tout comme vous pouvez à partir de std ::wstring.
auto var{ titleRecord.TitleName().c_str() };
En ce qui concerne l’implémentation d’API qui prennent ou retournent des chaînes, vous modifiez généralement tout code C++/CX qui utilise Platform ::String^ pour utiliser winrt ::hstring à la place.
Voici un exemple d’API C++/CX qui prend une chaîne.
void LogWrapLine(Platform::String^ str);
Pour C++/WinRT, vous pouvez déclarer cette API dans MIDL 3.0 comme suit.
// LogType.idl
void LogWrapLine(String str);
La chaîne d’outils C++/WinRT génère ensuite le code source pour vous qui ressemble à ceci.
void LogWrapLine(winrt::hstring const& str);
ToString()
Les types C++/CX fournissent la méthode Object ::ToString .
int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".
C++/WinRT ne fournit pas directement cette fonctionnalité, mais vous pouvez recourir à des solutions de remplacement.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
C++/WinRT prend également en charge winrt ::to_hstring pour un nombre limité de types. Vous devrez ajouter des surcharges pour tous les types supplémentaires que vous souhaitez convertir en chaîne.
| Language | Stringify int | Convertir l’énumération en chaîne |
|---|---|---|
| C++/CX | String^ result = "hello, " + intValue.ToString(); |
String^ result = "status: " + status.ToString(); |
| C++/WinRT | hstring result = L"hello, " + to_hstring(intValue); |
// must define overload (see below)hstring result = L"status: " + to_hstring(status); |
Si vous souhaitez convertir une énumération en chaîne, vous devrez fournir l’implémentation de winrt::to_hstring.
namespace winrt
{
hstring to_hstring(StatusEnum status)
{
switch (status)
{
case StatusEnum::Success: return L"Success";
case StatusEnum::AccessDenied: return L"AccessDenied";
case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
default: return to_hstring(static_cast<int>(status));
}
}
}
Ces représentations sous forme de chaîne sont souvent utilisées implicitement par la liaison de données.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Ces liaisons appliquent winrt::to_hstring à la propriété liée. Dans le cas du deuxième exemple ( StatusEnum), vous devez fournir votre propre surcharge de winrt ::to_hstring, sinon vous obtiendrez une erreur du compilateur.
Construction de chaînes
C++/CX et C++/WinRT s’appuient sur le std::wstringstream standard pour la construction de chaînes.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Ajouter une chaîne de caractères, en conservant les valeurs nulles | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
| Ajouter une chaîne, s’arrêter à la première valeur nulle | stream << s->Data(); |
stream << s.c_str(); |
| Extraire le résultat | ws = stream.str(); |
ws = stream.str(); |
Autres exemples
Dans les exemples ci-dessous, ws est une variable de type std ::wstring. En outre, alors que C++/CX peut construire une chaîne Platform ::String à partir d’une chaîne 8 bits, C++/WinRT ne le fait pas.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Créer une chaîne à partir d’un littéral | String^ s = "hello";String^ s = L"hello"; |
// winrt::hstring s{ "hello" }; // Doesn't compilewinrt::hstring s{ L"hello" }; |
| Convertir à partir de std ::wstring, en conservant des valeurs Null | String^ s = ref new String(ws.c_str(),(uint32_t)ws.size()); |
winrt::hstring s{ ws };s = winrt::hstring(ws);// s = ws; // Doesn't compile |
| Convertir à partir de std ::wstring, arrêter sur la première valeur null | String^ s = ref new String(ws.c_str()); |
winrt::hstring s{ ws.c_str() };s = winrt::hstring(ws.c_str());// s = ws.c_str(); // Doesn't compile |
| Convertir en std ::wstring, en conservant des valeurs Null | std::wstring ws{ s->Data(), s->Length };ws = std::wstring(s>Data(), s->Length); |
std::wstring ws{ s };ws = s; |
| Convertir en std::wstring, s’arrêter au premier caractère nul | std::wstring ws{ s->Data() };ws = s->Data(); |
std::wstring ws{ s.c_str() };ws = s.c_str(); |
| Passer un littéral à une méthode | Method("hello");Method(L"hello"); |
// Method("hello"); // Doesn't compileMethod(L"hello"); |
| Transmettre std::wstring à la méthode | Method(ref new String(ws.c_str(),(uint32_t)ws.size()); // Stops on first null |
Method(ws);// param::winrt::hstring accepts std::wstring_view |
API importantes
- modèle de structure winrt::delegate
- struct winrt ::hresult_error
- struct winrt::hstring
- espace de noms WinRT
Rubriques connexes
Note
De nombreuses rubriques C++/WinRT sont en cours de migration de la documentation UWP vers cette section. Tant que la migration n’est pas terminée, les liens de la liste ci-dessous peuvent conduire à la section des documents UWP. La projection de langage C++/WinRT est la même pour les applications UWP et WinUI 3, afin que le contenu soit applicable dans les deux contextes. Tous les modèles spécifiques à UWP (tels que le cycle de vie des applications ou Windows.UI les API d’espace de noms) sont explicitement notés dans ces articles.
- C++/CX
- Créer des événements en C++/WinRT
- Concurrence et opérations asynchrones avec C++/WinRT
- Utiliser des API avec C++/WinRT
- Gérer les événements à l’aide de délégués en C++/WinRT
- Interopérabilité entre C++/WinRT et C++/CX
- Asynchronie et interopérabilité entre C++/WinRT et C++/CX
- Informations de référence sur le langage de définition de l’interface Microsoft 3.0
- Passer à C++/WinRT à partir de WRL
- Gestion des chaînes en C++/WinRT
Windows developer