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.
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
.idlfichier 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)oC++/WinRT : unbox_value<Value>(o)Lève si unboxing échoue. Voir également Boxing et unboxing. C# : o as Value? ?? fallbackC++/WinRT : unbox_value_or<Value>(o, fallback)Retourne la valeur de secours si le déballage échoue. Voir également Boxing et unboxing. C# : (Class)oC++/WinRT : o.as<Class>()Lève si la conversion échoue. C# : o as ClassC++/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() }; ouauto 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 } ouMyType(args) |
Portage de la propriété Scenarios |
| Créer une référence non initialisée | MyType myObject; |
MyType myObject{ nullptr }; ouMyType 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 existingv = map[k]; // throws if not presentmap.ContainsKey(k) |
map.Insert(k, v); // replaces any existingv = map.Lookup(k); // throws if not presentmap.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.
Tâches liées au portage spécifiques à C++/WinRT
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
Rubriques connexes
Windows developer