Passer de C# à C++/WinRT

Tip

Si vous avez déjà lu cette rubrique et que vous y revenez avec une tâche particulière à l’esprit, vous pouvez accéder au contenu Rechercher en fonction de la tâche que vous effectuez dans cette rubrique.

Cette rubrique répertorie en détail les détails techniques impliqués dans le portage du code source dans un projet C# vers son équivalent en C++/WinRT.

Pour une étude de cas sur le portage de l’un des exemples d’application Plateforme Windows universelle (UWP), consultez la rubrique complémentaire Portage de l’exemple Presse-papiers vers C++/WinRT à partir de C#. Vous pouvez acquérir une pratique de portage et une expérience en suivant cette procédure pas à pas et en transférant l’exemple pour vous-même à mesure que vous le souhaitez.

Comment préparer et ce à quoi s’attendre

L’étude de cas Portage de l’exemple Presse-papiers vers C++/WinRT depuis C# illustre des exemples de décisions de conception logicielle que vous serez amené à prendre lors du portage d’un projet vers C++/WinRT. Il est donc judicieux de se préparer au portage en obtenant une bonne compréhension du fonctionnement du code existant. De cette façon, vous obtiendrez une bonne vue d’ensemble des fonctionnalités de l’application et de la structure du code, puis les décisions que vous prenez vous aideront toujours à avancer, et dans la bonne direction.

En termes de types de modifications de portage à attendre, vous pouvez les regrouper en quatre catégories.

  • Migrer la projection linguistique. Le Windows Runtime (WinRT) est projeté dans différents langages de programmation. Chacune de ces projections de langage est conçue pour être idiomatique dans le langage de programmation concerné. Pour C#, certains types Windows Runtime sont projetés en tant que types .NET. Par exemple, vous allez traduire System.Collections.Generic.IReadOnlyList<T> en Windows. Foundation.Collections.IVectorView<T>. En outre, dans C#, certaines opérations Windows Runtime sont projetées en tant que fonctionnalités de langage C# pratiques. Par exemple, dans C#, vous utilisez la syntaxe de l’opérateur += pour inscrire un délégué de gestion des événements. Vous allez donc traduire des fonctionnalités de langage comme celle-ci vers l’opération fondamentale en cours d’exécution (inscription d’événements, dans cet exemple).
  • Syntaxe du langage de port. La plupart de ces changements sont des transformations mécaniques simples, en remplaçant un symbole pour un autre. Par exemple, en remplaçant le point (.) par deux-points (::).
  • Procédure de langue de port. Certains d’entre eux peuvent être des modifications simples et répétitives (par exemple myObject.MyPropertymyObject.MyProperty()). D’autres ont besoin de modifications plus approfondies (par exemple, le portage d’une procédure qui implique l’utilisation de System.Text.StringBuilder vers une autre qui implique l’utilisation de std ::wostringstream).
  • Tâches liées au portage spécifiques à C++/WinRT. Certains détails de la Windows Runtime sont pris en charge implicitement par C#, en arrière-plan. Ces détails sont effectués explicitement en C++/WinRT. Par exemple, vous utilisez un .idl fichier pour définir vos classes runtime.

Après l’index basé sur les tâches qui suit, les autres sections de cette rubrique sont structurées en fonction de la taxonomie ci-dessus.

Rechercher du contenu en fonction de la tâche que vous effectuez

Tâche Content
Créer un composant Windows Runtime (WRC) Certaines fonctionnalités peuvent être obtenues (ou certaines API appelées) uniquement avec C++. Vous pouvez prendre en compte cette fonctionnalité dans un WRC C++/WinRT, puis utiliser le WRC à partir (par exemple) d’une application C#. Consultez composants Windows Runtime avec C++/WinRT et Si vous créez une classe d’exécution dans un composant Windows Runtime.
Porter une méthode asynchrone Il est judicieux que la première ligne d’une méthode asynchrone dans une classe runtime C++/WinRT soit auto lifetime = get_strong(); (voir Accès sécurisé à ce pointeur dans une coroutine membre de classe).

Migration depuis Task, voir action asynchrone.
Portage depuis Task<T>, voir Opération asynchrone.
Migration depuis async void, voir la méthode de type « fire-and-forget ».
Porter une classe Tout d’abord, déterminez si la classe doit être une classe runtime ou s’il peut s’agir d’une classe ordinaire. Pour vous aider à en décider, consultez le tout début de Author APIs with C++/WinRT. Ensuite, consultez les trois lignes suivantes ci-dessous.
Porter une classe runtime Classe qui partage des fonctionnalités en dehors de l’application C++ ou une classe utilisée dans la liaison de données XAML. Voir si vous créez une classe runtime dans un composant Windows Runtime, ou si vous créez une classe runtime à référencer dans votre interface utilisateur XAML.

Ces liens décrivent cela plus en détail, mais une classe runtime doit être déclarée dans IDL. Si votre project contient déjà un fichier IDL (par exemple, Project.idl), nous vous recommandons de déclarer une nouvelle classe runtime dans ce fichier. Dans IDL, déclarez les méthodes et les membres de données qui seront utilisés en dehors de votre application, ou qui seront utilisés en XAML. Après avoir mis à jour le fichier IDL, régénérez, puis examinez les fichiers stub générés (.h et .cpp) dans le dossier Generated Files de votre projet (dans Explorateur de solutions, lorsque le nœud du projet est sélectionné, assurez-vous que l’option Show All Files est activée). Comparez les fichiers stub aux fichiers déjà présents dans votre projet, en ajoutant des fichiers ou en ajoutant/mettant à jour les signatures de fonction si nécessaire. La syntaxe du fichier stub est toujours correcte. Nous vous recommandons donc de l’utiliser pour réduire les erreurs de génération. Une fois que les stubs de votre projet correspondent à ceux des fichiers stub, vous pouvez aller de l’avant et les implémenter en transférant le code C#.
Porter une classe ordinaire Voir si vous ne créez pas de classe runtime.
Auteur IDL Présentation de Microsoft Interface Definition Language 3.0
Si vous créez une classe runtime à référencer dans votre interface utilisateur XAML
Consommation d’objets à partir du balisage XAML
Définir vos classes runtime dans IDL
Porter une collection Collections avec C++/WinRT
Mise à disposition d’une source de données pour le balisage XAML
Conteneur associatif
Accès aux membres d’un vecteur
Transférer un événement Délégué du gestionnaire d’événements en tant que membre de classe
Révoquer le délégué du gestionnaire d’événements
Port d’une méthode À partir de C# : private async void SampleButton_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Dans le fichier C++/WinRT .h : fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Dans le fichier C++/WinRT .cpp : fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Chaînes de port Gestion des chaînes en C++/WinRT
ToString
Construction de chaînes
Boxing et unboxing d’une chaîne
Conversion de type (transtypage) C# : o.ToString()
C++/WinRT : to_hstring(static_cast<int>(o))
Voir également ToString.

C# : (Value)o
C++/WinRT : unbox_value<Value>(o)
Lève si unboxing échoue. Voir également Boxing et unboxing.

C# : o as Value? ?? fallback
C++/WinRT : unbox_value_or<Value>(o, fallback)
Retourne la valeur de secours si le déballage échoue. Voir également Boxing et unboxing.

C# : (Class)o
C++/WinRT : o.as<Class>()
Lève si la conversion échoue.

C# : o as Class
C++/WinRT : o.try_as<Class>()
Retourne null si la conversion échoue.

Modifications qui impliquent la projection de langage

Category C# C++/WinRT Voir aussi
Objet non typé objectou System.Object Windows::Foundation::IInspectable Portage de la méthode EnableClipboardContentChangedNotifications
Espaces de noms de projection using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Taille d’une collection collection.Count collection.Size() Portage de la méthode BuildClipboardFormatsOutputString
Type de collection classique IList<T> et Ajouter pour ajouter un élément. IVector<T> et Append pour ajouter un élément. Si vous utilisez un std::vector quelque part, alors utilisez push_back pour ajouter un élément.
Type de collection en lecture seule IReadOnlyList<T> IVectorView<T> Portage de la méthode BuildClipboardFormatsOutputString
Délégué du gestionnaire d’événements en tant que membre de classe myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Portage de la méthode EnableClipboardContentChangedNotifications
Révoquer le délégué du gestionnaire d’événements myObject.EventName -= Handler; myObject.EventName(token); Portage de la méthode EnableClipboardContentChangedNotifications
Conteneur associatif IDictionary<K, V> IMap<K, V>
Accès aux membres d’un vecteur x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Inscrire/révoquer un gestionnaire d’événements

Dans C++/WinRT, vous avez plusieurs options syntactiques pour inscrire/révoquer un délégué de gestionnaire d’événements, comme décrit dans Gérer les événements à l’aide de délégués en C++/WinRT. Voir également Portage de la méthode EnableClipboardContentChangedNotifications.

Parfois, quand un destinataire d’événement (objet qui gère un événement) est sur le point d’être détruit, vous voudrez révoquer un gestionnaire d’événements afin que la source d’événement (l’objet qui déclenche l’événement) n’appelle pas un objet détruit. Voir Révoquer un délégué inscrit. Dans ce cas, créez une variable membre event_token pour vos gestionnaires d’événements. Pour obtenir un exemple, consultez Portage de la méthode EnableClipboardContentChangedNotifications.

Vous pouvez également inscrire un gestionnaire d’événements dans le balisage XAML.

<Button x:Name="OpenButton" Click="OpenButton_Click" />

En C#, votre méthode de OpenButton_Click peut être privée et XAML peut toujours la connecter à l’événement ButtonBase.Click déclenché par OpenButton.

Dans C++/WinRT, votre méthode OpenButton_Click doit être publique dans votre type d’implémentationsi vous souhaitez l’inscrire dans le balisage XAML. Si vous inscrivez un gestionnaire d’événements uniquement dans le code impératif, le gestionnaire d’événements n’a pas besoin d’être public.

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
        void OpenButton_Click(
            winrt::Windows::Foundation::IInspectable const& sender,
            winrt::Microsoft::UI::Xaml::RoutedEventArgs const& args);
    }
};

Vous pouvez également accorder à la page XAML qui effectue l’enregistrement un accès ami à votre type d’implémentation et déclarer OpenButton_Click comme privé.

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
    private:
        friend MyPageT;
        void OpenButton_Click(
            winrt::Windows::Foundation::IInspectable const& sender,
            winrt::Microsoft::UI::Xaml::RoutedEventArgs const& args);
    }
};

Un scénario final est l’endroit où le projet C# que vous transférez est lié au gestionnaire d’événements à partir du balisage (pour plus d’informations sur ce scénario, consultez Functions dans x :Bind).

<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />

Vous pourriez simplement changer ce balisage en plus simple Click="OpenButton_Click". Ou, si vous préférez, vous pouvez conserver ce balisage tel qu’il est. Pour le prendre en charge, il vous suffit de déclarer le gestionnaire d’événement dans l’IDL.

void OpenButton_Click(Object sender, Microsoft.UI.Xaml.RoutedEventArgs e);

Note

Déclarez la fonction en tant que void même si vous l’implémentez en fire-and-forget.

Modifications qui impliquent la syntaxe du langage

Category C# C++/WinRT Voir aussi
Modificateurs d’accès public \<member\> public:
    \<member\>
Portage de la méthode Button_Click
Accéder à un membre de données this.variable this->variable  
Action asynchrone async Task ... IAsyncAction ... Interface IAsyncAction, concurrence et opérations asynchrones avec C++/WinRT
Opération asynchrone async Task<T> ... IAsyncOperation<T> ... Interface IAsyncOperation, concurrence et opérations asynchrones avec C++/WinRT
Méthode « fire-and-forget » (implique un comportement asynchrone) async void ... winrt::fire_and_forget ... Portage de la méthode CopyButton_Click, Fire et forget
Accéder à une constante énumérée E.Value E::Value Portage de la méthode DisplayChangedFormats
Patientez de manière coopérative await ... co_await ... Portage de la méthode CopyButton_Click
Collection de types projetés en tant que champ privé private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
Construction d’un GUID private static readonly Guid myGuid = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };
Séparateur d’espace de noms A.B.T A::B::T
Null null nullptr Portage de la méthode UpdateStatus
Obtenir un objet de type typeof(MyType) winrt::xaml_typename<MyType>() Portage de la propriété Scenarios
Déclaration de paramètre pour une méthode MyType MyType const& Passage de paramètres
Déclaration de paramètre pour une méthode asynchrone MyType MyType Passage de paramètres
Appeler une méthode statique T.Method() T::Method()
Chaînes stringou System.String winrt ::hstring Gestion des chaînes en C++/WinRT
Chaîne littérale "a string literal" L"a string literal" Portage du constructeur, actuel et FEATURE_NAME
Type inféré (ou déduit) var auto Portage de la méthode BuildClipboardFormatsOutputString
Using-directive using A.B.C; using namespace A::B::C; Portage du constructeur, Current et FEATURE_NAME
Littéral de chaîne brute/verbatim @"verbatim string literal" LR"(raw string literal)" Portage de la méthode DisplayToast

Note

Si un fichier d’en-tête ne contient pas de using namespace directive pour un espace de noms donné, vous devez qualifier entièrement tous les noms de types pour cet espace de noms, ou au moins les qualifier suffisamment pour que le compilateur les trouve. Pour obtenir un exemple, consultez Portage de la méthode DisplayToast.

Portage de classes et de membres

Vous devez décider, pour chaque type C#, s'il faut le porter vers un type Windows Runtime ou vers une classe/struct/énumération C++ standard. Pour plus d’informations, ainsi que des exemples détaillés illustrant comment prendre ces décisions, consultez Portage de l’exemple Clipboard de C# vers C++/WinRT.

Une propriété C# devient généralement une fonction d’accesseur, une fonction de modification et un membre de données sous-jacent. Pour plus d’informations et un exemple, consultez Portage de la propriété IsClipboardContentChangedEnabled.

Pour les champs non statiques, faites-les membres de données de votre type d’implémentation.

Un champ statique C# devient un accesseur statique C++/WinRT et/ou une fonction mutateur. Pour plus d’informations et un exemple, consultez Portage du constructeur, Current et FEATURE_NAME.

Pour les fonctions membres, là encore, vous devez décider pour chacun d’eux s’il appartient ou non à l’IDL, ou s’il s’agit d’une fonction membre publique ou privée de votre type d’implémentation. Pour plus d’informations et des exemples de choix, consultez IDL pour le type MainPage.

Portage du balisage XAML et des fichiers de ressources

Dans le cas du portage de l’exemple Presse-papiers de C# vers C++/WinRT, nous avons pu utiliser le même code XAML (y compris les ressources) et les fichiers de ressources graphiques dans les projets C# et C++/WinRT. Dans certains cas, les modifications de balisage seront nécessaires pour y parvenir. Consultez Copier le code XAML et les styles nécessaires pour terminer le portage de MainPage.

Modifications impliquant des procédures dans la langue

Category C# C++/WinRT Voir aussi
Gestion de la durée de vie dans une méthode asynchrone N/A auto lifetime{ get_strong() }; ou
auto lifetime = get_strong();
Portage de la méthode CopyButton_Click
Élimination using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Portage de la méthode CopyImage
Construire un objet new MyType(args) MyType{ args } ou
MyType(args)
Portage de la propriété Scenarios
Créer une référence non initialisée MyType myObject; MyType myObject{ nullptr }; ou
MyType myObject = nullptr;
Portage du constructeur, Actuel et FEATURE_NAME
Construire un objet dans une variable avec des arguments var myObject = new MyType(args); auto myObject{ MyType{ args } }; ou
auto myObject{ MyType(args) }; ou
auto myObject = MyType{ args }; ou
auto myObject = MyType(args); ou
MyType myObject{ args }; ou
MyType myObject(args);
Portage de la méthode Footer_Click
Construire un objet dans une variable sans arguments var myObject = new T(); MyType myObject; Portage de la méthode BuildClipboardFormatsOutputString
Syntaxe abrégée d’initialisation d’objet var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Opération vectorielle en masse var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
Portage de la méthode CopyButton_Click
Itérer sur la collection foreach (var v in c) for (auto&& v : c) Portage de la méthode BuildClipboardFormatsOutputString
Intercepter une exception catch (Exception ex) catch (winrt::hresult_error const& ex) Portage de la méthode PasteButton_Click
Détails de l’exception ex.Message ex.message() Portage de la méthode PasteButton_Click
Récupérer la valeur d’une propriété myObject.MyProperty myObject.MyProperty() Portage de la méthode NotifyUser
Définir une valeur de propriété myObject.MyProperty = value; myObject.MyProperty(value);
Incrémenter une valeur de propriété myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Pour les chaînes de caractères, utilisez un builder
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Chaîne de langue vers chaîne Windows Runtime N/A winrt::hstring{ s }
Construction de chaînes StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Construction de chaînes
Interpolation de chaîne $"{i++}) {s.Title}" winrt ::to_hstring et/ou winrt ::hstring ::operator+ Portage de la méthode OnNavigatedTo
Chaîne vide pour la comparaison System.String.Empty winrt ::hstring ::empty Portage de la méthode UpdateStatus
Créer une chaîne vide var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Opérations de dictionnaire map[k] = v; // replaces any existing
v = map[k]; // throws if not present
map.ContainsKey(k)
map.Insert(k, v); // replaces any existing
v = map.Lookup(k); // throws if not present
map.HasKey(k)
Conversion de type (levée en cas d’échec) (MyType)v v.as<MyType>() Portage de la méthode Footer_Click
Conversion de type (null en cas d’échec) v as MyType v.try_as<MyType>() Portage de la méthode PasteButton_Click
Les éléments XAML avec x :Name sont des propriétés MyNamedElement MyNamedElement() Portage du constructeur, Actuel et FEATURE_NAME
Basculer vers le thread d’interface utilisateur CoreDispatcher.RunAsync DispatcherQueue.TryEnqueue, ou winrt::resume_foreground Portage de la méthode NotifyUser et portage de la méthode HistoryAndRoaming
Construction d’éléments d’interface utilisateur dans le code impératif dans une page XAML Voir la construction d’éléments d’interface utilisateur Voir la construction d’éléments d’interface utilisateur

Les sections suivantes décrivent plus en détail certains des éléments du tableau.

Construction d’éléments d’interface utilisateur

Ces exemples de code montrent la construction d’un élément d’interface utilisateur dans le code impératif d’une page XAML.

var myTextBlock = new TextBlock()
{
    Text = "Text",
    Style = (Microsoft.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
    winrt::unbox_value<Microsoft::UI::Xaml::Style>(
        Resources().Lookup(
            winrt::box_value(L"MyTextBlockStyle")
        )
    )
);

ToString()

Les types C# fournissent la méthode Object.ToString .

int i = 2;
var s = i.ToString(); // s is a System.String with value "2".

C++/WinRT ne fournit pas directement cette fonctionnalité, mais vous pouvez vous tourner vers des alternatives.

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 tout type supplémentaire que vous souhaitez convertir en chaîne.

Language Convertir int en chaîne Convertir l’énumération en chaîne de caractères
C# string result = "hello, " + intValue.ToString();
string result = $"hello, {intValue}";
string result = "status: " + status.ToString();
string result = $"status: {status}";
C++/WinRT hstring result = L"hello, " + to_hstring(intValue); // must define overload (see below)
hstring result = L"status: " + to_hstring(status);

Dans le cas de la conversion d’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 conversions en chaîne de caractères 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 de données 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.

Consultez également Portage de la méthode Footer_Click.

Construction de chaînes de caractères

Pour la génération de chaînes, C# a un type StringBuilder intégré.

Category C# C++/WinRT
Construction de chaînes StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Ajouter une chaîne Windows Runtime, en conservant les caractères nuls builder.Append(s); builder << std::wstring_view{ s };
Ajouter un saut de ligne builder.Append(Environment.NewLine); builder << std::endl;
Accéder au résultat s = builder.ToString(); ws = builder.str();

Consultez également Portage de la méthode BuildClipboardFormatsOutputString et Portage de la méthode DisplayChangedFormats.

Exécution de code sur le thread d’interface utilisateur principal

Cet exemple est extrait de l’exemple de scanneur de codes-barres.

Lorsque vous souhaitez travailler sur le thread d’interface utilisateur principal dans un projet C#, vous utilisez généralement la méthode DispatcherQueue.TryEnqueue (ou l’ancienne coreDispatcher.RunAsync dans UWP). Voici à quoi ressemble le modèle en C#.

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        // Do work on the main UI thread here.
    });
}

Il est beaucoup plus simple d’exprimer cela en C++/WinRT. Notez que nous acceptons les paramètres par valeur sur l’hypothèse que nous voulons y accéder après le premier point de suspension ( co_awaitdans ce cas). Pour plus d’informations, consultez Passage de paramètre.

winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
    co_await DispatcherQueue();
    // Do work on the main UI thread here.
}

Si vous devez effectuer le travail avec une priorité autre que celle par défaut, reportez-vous à la fonction winrt::resume_foreground, qui possède une surcharge acceptant une priorité en paramètre. Pour obtenir des exemples de code montrant comment attendre un appel à winrt ::resume_foreground, consultez Programmation avec affinité de thread à l’esprit.

Définissez vos classes d’exécution en IDL

Consultez IDL pour le type MainPage et Consolidez vos .idl fichiers.

Incluez les fichiers d’en-tête de l’espace de noms Windows C++/WinRT dont vous avez besoin

Dans C++/WinRT, chaque fois que vous souhaitez utiliser un type à partir d’un espace de noms Windows, vous devez inclure le fichier d’en-tête d’espace de noms C++/Win Windows RT correspondant. Pour obtenir un exemple, consultez Portage de la méthode NotifyUser.

Boxing et unboxing

C# encapsule automatiquement les valeurs scalaires dans des 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# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operation C# 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# C++/WinRT
Déboxer un entier connu i = (int)o; i = unbox_value<int>(o);
Si o a la valeur Null System.NullReferenceException Krach
Si o n’est pas un int encapsulé System.InvalidCastException Krach
Déballer l’entier, utiliser la valeur de repli si null ; planter dans tous les autres cas i = o != null ? (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 i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Pour obtenir un exemple, consultez Portage de la méthode OnNavigatedTo et Portage de la méthode Footer_Click.

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# 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# 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é.

Behavior C# 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 "" hstring{}
null et "" sont-ils identiques ? No Yes
Validité de null s = null;
s.Length déclenche NullReferenceException
s = hstring{};
s.size() == 0 (valide)
Si vous attribuez une chaîne Null à un objet o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Si vous attribuez "" à l’objet o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Boxing de base et unboxing.

Operation C# C++/WinRT
Encadrer une chaîne o = s;
La chaîne vide devient un objet non null.
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 Null.
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 = o as string;
Un objet null ou une valeur non chaîne devient une chaîne null.

OR

s = o as string ?? fallback;
Une valeur nulle ou qui n’est pas une chaîne devient une valeur de repli.
Chaîne vide conservée.
s = unbox_value_or<hstring>(o, fallback);
Une valeur nulle ou non textuelle devient la valeur de repli.
Chaîne vide conservée.

Mise à disposition d’une classe pour l’extension de balisage {Binding}

Si vous envisagez d’utiliser l’extension de balisage {Binding} pour lier des données à votre type de données, consultez l’objet Binding déclaré à l’aide de {Binding}.

Consommation d’objets à partir du balisage XAML

Dans un projet C#, 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#, 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.

Mise à disposition d’une source de données pour le balisage XAML

Dans la version 2.0.190530.8 ou ultérieure de C++/WinRT, winrt::single_threaded_observable_vector crée un vecteur observable qui prend en charge à la fois IObservableVector<T> et IObservableVector<IInspectable>. Pour obtenir un exemple, consultez Portage de la propriété Scenarios.

Vous pouvez rédiger votre fichier Midl (.idl) comme ceci (voir également la factorisation des classes d’exécution dans des fichiers Midl (.idl)).

namespace Bookstore
{
    runtimeclass BookSku { ... }

    runtimeclass BookstoreViewModel
    {
        Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
    }

    runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

Et implémentez comme ça.

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel()
    {
        m_bookSkus = winrt::single_threaded_observable_vector<Bookstore::BookSku>();
        m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
    }
    
	Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> BookSkus();
    {
        return m_bookSkus;
    }

private:
    Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> m_bookSkus;
};
...

Pour plus d’informations, consultez contrôles d’éléments XAML ; liaison à une collection C++/WinRT et Collections avec C++/WinRT.

Mise à disposition d’une source de données pour le balisage XAML (avant C++/WinRT 2.0.190530.8)

La liaison de données XAML nécessite qu’une source d’éléments implémente IIterable<IInspectable>, ainsi que l’une des combinaisons d’interfaces suivantes.

  • IObservableVector<IInspectable>
  • IBindableVector et INotifyCollectionChanged
  • IBindableVector et IBindableObservableVector
  • IBindableVector lui-même (ne répond pas aux modifications)
  • IVector<IInspectable>
  • IBindableIterable (itérera et enregistrera des éléments dans une collection privée)

Une interface générique telle que IVector<T> ne peut pas être détectée au moment de l’exécution. Chaque IVector<T> a un identificateur d’interface (IID), qui est une fonction de T. Tout développeur peut développer arbitrairement l’ensemble de T , de sorte que le code de liaison XAML ne peut jamais connaître l’ensemble complet à interroger. Cette restriction n’est pas un problème pour C# car chaque objet CLR qui implémente IEnumerable<T> implémente automatiquement IEnumerable. Au niveau ABI, cela signifie que chaque objet qui implémente IObservableVector<T> implémente automatiquement IObservableVector<IInspectable>.

C++/WinRT n’offre pas cette garantie. Si une classe runtime C++/WinRT implémente IObservableVector<T>, nous ne pouvons pas supposer qu’une implémentation d’IObservableVector<IInspectable> est également fournie.

Par conséquent, voici comment l’exemple précédent devra ressembler.

...
runtimeclass BookstoreViewModel
{
    // This is really an observable vector of BookSku.
    Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}

Et l’implémentation.

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel()
    {
        m_bookSkus = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>();
        m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
    }
    
    // This is really an observable vector of BookSku.
	Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BookSkus();
    {
        return m_bookSkus;
    }

private:
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> m_bookSkus;
};
...

Si vous avez besoin d’accéder à des objets dans m_bookSkus, vous devez revenir à Bookstore ::BookSku.

Widget MyPage::BookstoreViewModel(winrt::hstring title)
{
    for (auto&& obj : m_bookSkus)
    {
        auto bookSku = obj.as<Bookstore::BookSku>();
        if (bookSku.Title() == title) return bookSku;
    }
    return nullptr;
}

Classes dérivées

Pour dériver d’une classe runtime, la classe de base doit être composable. C# ne vous oblige pas à prendre des mesures particulières pour rendre vos classes composables, alors que 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 le fichier d’en-tête de votre type 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>
    {
        ...
    }
}

API importantes