Delen via


Verplaatsen naar C++/WinRT van C#

Aanbeveling

Als u dit onderwerp eerder hebt gelezen en u teruggaat naar het onderwerp met een bepaalde taak in gedachten, kunt u naar de Inhoud zoeken gaan op basis van de taak die u uitvoert sectie van dit onderwerp.

In dit onderwerp worden de technische details die betrokken zijn bij het overzetten van de broncode in een C# project uitgebreid beschreven naar een equivalent in C++/WinRT.

Voor een casestudy over het porteren van een van de Universal Windows Platform (UWP) app-voorbeelden, zie het bijbehorende onderwerp Porting the Clipboard sample to C++/WinRT from C#. U kunt porteren oefenen en ervaring opdoen door de stapsgewijze handleiding te volgen en het voorbeeld zelf te porteren terwijl u verdergaat.

Voorbereiden en wat u kunt verwachten

De casestudy Het Klembord-voorbeeld overzetten naar C++/WinRT van C# illustreert voorbeelden van de soorten beslissingen over softwareontwerp die u neemt tijdens het overzetten van een project naar C++/WinRT. Het is dus een goed idee om u voor te bereiden op porting door een goed inzicht te krijgen in de werking van de bestaande code. Op die manier krijgt u een goed overzicht van de functionaliteit van de app en de structuur van de code. De beslissingen die u neemt, zullen u altijd verder brengen in de juiste richting.

In termen van wat voor soort overdrachtswijzigingen u kunt verwachten, kunt u deze groeperen in vier categorieën.

  • De taalprojectieporteren. De Windows Runtime (WinRT) wordt geprojecteerd in verschillende programmeertalen. Elk van deze taalprojecties is ontworpen om idiomatisch te voelen voor de betreffende programmeertaal. Voor C# worden sommige Windows Runtime-typen geprojecteerd als .NET-typen. U vertaalt bijvoorbeeld System.Collections.Generic.IReadOnlyList<T> terug naar Windows.Foundation.Collections.IVectorView<T>. In C# worden sommige Windows Runtime-bewerkingen ook geprojecteerd als handige C#-taalfuncties. Een voorbeeld is dat u in C# de syntaxis van de operator += gebruikt om een gemachtigde voor gebeurtenisafhandeling te registreren. U vertaalt dus taalfuncties zoals die terug naar de fundamentele bewerking die wordt uitgevoerd (gebeurtenisregistratie in dit voorbeeld).
  • syntaxis van de poorttaal. Veel van deze wijzigingen zijn eenvoudige mechanische transformaties, waarbij het ene symbool voor het andere wordt vervangen. Als u bijvoorbeeld een punt (.) wijzigt in een dubbele punt (::).
  • procedure voor poorttaal. Sommige van deze kunnen eenvoudige, terugkerende wijzigingen zijn (zoals myObject.MyProperty in myObject.MyProperty()). Anderen hebben diepere wijzigingen nodig (bijvoorbeeld het overzetten van een procedure waarbij het gebruik van System.Text.StringBuilder is vereist voor een procedure waarbij gebruik wordt gemaakt van std::wostringstream).
  • Portering-gerelateerde taken die specifiek zijn voor C++/WinRT. Bepaalde details van de Windows Runtime worden impliciet verzorgd door C#, achter de schermen. Deze details worden expliciet uitgevoerd in C++/WinRT. Een voorbeeld is dat u een .idl-bestand gebruikt om uw runtimeklassen te definiëren.

Na de op taken gebaseerde index die volgt, zijn de rest van de secties in dit onderwerp gestructureerd volgens de bovenstaande taxonomie.

Inhoud zoeken op basis van de taak die u uitvoert

Opdracht Inhoud
Een Windows Runtime-onderdeel maken (WRC) Bepaalde functionaliteit kan alleen worden bereikt (of bepaalde API's die worden aangeroepen) met C++. U kunt deze functionaliteit in een C++/WinRT WRC inschakelen en vervolgens de WRC gebruiken vanuit (bijvoorbeeld) een C#-app. Zie Windows Runtime-onderdelen met C++/WinRT- en Als u een runtimeklasse maakt in een Windows Runtime-onderdeel.
Een asynchrone methode overzetten Het is een goed idee als de eerste regel van een asynchrone methode in een C++/WinRT-klasse voor runtime auto lifetime = get_strong(); te zijn (zie Veilig toegang krijgen tot de -aanwijzer in een coroutine van een klasse-lid).

Overdracht van Task, zie Asynchrone actie.
Porteren vanaf Task<T>, zie Asynchrone operatie.
Overzetten van async void, zie Fire-and-forget-methode.
Een klasse overzetten Bepaal eerst of de klasse een runtimeklasse moet zijn of dat deze een gewone klasse kan zijn. Zie het begin van Author API's met C++/WinRTom u te helpen dat te bepalen. Bekijk vervolgens de volgende drie rijen hieronder.
Een runtimeklasse overzetten Een klasse die functionaliteit deelt buiten de C++-app of een klasse die wordt gebruikt in XAML-gegevensbinding. Zie Als u een runtimeklasse maakt in een Windows Runtime-onderdeelof Als u een runtimeklasse maakt waarnaar moet worden verwezen in de XAML-gebruikersinterface.

Deze koppelingen beschrijven dit in meer detail, maar een runtimeklasse moet worden gedeclareerd in IDL. Als uw project al een IDL-bestand bevat (bijvoorbeeld Project.idl), wordt u aangeraden een nieuwe runtimeklasse in dat bestand te declareren. Declareer in IDL alle methoden en gegevensleden die buiten uw app worden gebruikt of die worden gebruikt in XAML. Nadat u het IDL-bestand hebt bijgewerkt, bouwt u het bestand opnieuw en bekijkt u de gegenereerde stub-bestanden (.h en .cpp) in de Generated Files map van uw project (in Solution Explorer, waarbij het projectknooppunt is geselecteerd, controleert u of Alle bestanden weergeven is ingeschakeld). Vergelijk de stub-bestanden met de bestanden die al in uw project aanwezig zijn, voeg zo nodig bestanden toe of voeg functiehandtekeningen toe of werk deze bij. Stub-bestandssyntaxis is altijd correct, dus we raden u aan deze te gebruiken om buildfouten te minimaliseren. Zodra de stubs in uw project overeenkomen met die in de stub-bestanden, kunt u deze implementeren door de C#-code over te zetten.
Een gewone klasse overzetten Zie als u geen runtime-klasse ontwerpt.
Auteur IDL Inleiding tot Microsoft Interface Definition Language 3.0
Als u een runtimeklasse maakt waarnaar moet worden verwezen in de XAML-gebruikersinterface
Objecten gebruiken uit XAML-markup
Definieer uw runtime-klassen in IDL
Een verzameling overzetten Verzamelingen met C++/WinRT
Een gegevensbron beschikbaar maken voor XAML-markeringen
koppelende container
vectorlidtoegang
Een gebeurtenis overzetten Gebeurtenishandler delegeren als klasselid
Intrekken gebeurtenishandlergedelegeerde
Een methode overzetten Vanuit C#: private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Naar het bestand C++/WinRT .h: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Naar het bestand C++/WinRT .cpp: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Poorttekenreeksen Verwerking van tekenreeksen in C++/WinRT
ToString
tekenreeksopbouw
Converteren en ontpakken van een tekenreeks
Typeconversie (typeomzetting) C#: o.ToString()
C++/WinRT: to_hstring(static_cast<int>(o))
Zie ook ToString.

C#: (Value)o
C++/WinRT: unbox_value<Value>(o)
Werpt als het uitpakken mislukt. Zie ook Boxing en Unboxing.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Geeft de standaardwaarde terug als het uitpakken mislukt. Zie ook Boxing en Unboxing.

C#: (Class)o
C++/WinRT: o.as<Class>()
Werpt als de conversie mislukt.

C#: o as Class
C++/WinRT: o.try_as<Class>()
Retourneert null als de conversie mislukt.

Wijzigingen die betrekking hebben op de taalprojectie

Categorie C# C++/WinRT Zie ook
Ongedefinieerd object objectof System.Object Windows::Foundation::IInspectable Overzetten van de EnableClipboardContentChangedNotifications methode
Projectienaamruimten using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Grootte van een verzameling collection.Count collection.Size() Het overzetten van de methode BuildClipboardFormatsOutputString
Typisch verzamelingstype IList<T->en Voeg toe om een element toe te voegen. IVector<T->en toevoegen om een element toe te voegen. Als u ergens een std::vector gebruikt, kunt u met push_back een element toevoegen.
Alleen-lezen-verzamelingstype IReadOnlyList<T> IVectorView<T> Het overzetten van de methode BuildClipboardFormatsOutputString
Gebeurtenishandler delegeren als klasselid myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); Overzetten van de EnableClipboardContentChangedNotifications methode
Gedelegeerde gebeurtenisafhandelaar opheffen myObject.EventName -= Handler; myObject.EventName(token); Overzetten van de EnableClipboardContentChangedNotifications methode
Associatieve container IDictionary<K, V> IMap<K, V>
Vectorleden-toegang x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Een gebeurtenis-handler registreren/intrekken

In C++/WinRT hebt u verschillende syntactische opties voor het registreren/intrekken van een gemachtigde voor een gebeurtenis-handler, zoals beschreven in Gebeurtenissen verwerken met behulp van gemachtigden in C++/WinRT-. Zie ook Het porteren van de EnableClipboardContentChangedNotifications methode.

Soms, bijvoorbeeld wanneer een gebeurtenisontvanger (een object dat een gebeurtenis behandelt) op het punt staat vernietigd te worden, wilt u misschien een gebeurtenis-handler intrekken zodat de gebeurtenisbron (het object dat de gebeurtenis aanroept) niet in een vernietigd object aanroept. Zie Het intrekken van een geregistreerde gemachtigde. In dergelijke gevallen maakt u een event_token lidvariabele voor uw eventhandlers. Zie bijvoorbeeld Porting the EnableClipboardContentChangedNotifications methode.

U kunt ook een gebeurtenis-handler registreren in XAML-markeringen.

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

In C# kan uw OpenButton_Click methode privé zijn en kan XAML deze nog steeds verbinden met de ButtonBase.Click gebeurtenis die wordt opgeroepen door OpenButton.

In C++/WinRT moet uw OpenButton_Click methode openbaar zijn in uw implementatietypeals u deze wilt registreren in XAML-markeringen. Als u een gebeurtenis-handler alleen registreert in imperatieve code, hoeft de gebeurtenis-handler niet openbaar te zijn.

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

U kunt ook de registrerende XAML-pagina een vriend van uw implementatietype maken en OpenButton_Click privé.

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

Een laatste scenario is waar het C#-project dat u overzet, verbindt met de event handler vanuit markup (zie Functions in x:Bind) voor meer achtergrondinformatie over dat scenario.

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

U kunt die markering gewoon wijzigen in de eenvoudigere Click="OpenButton_Click". Je kunt ook, als je wilt, die markeringen behouden zoals ze zijn. Het enige dat u hoeft te doen om het te ondersteunen, is de event handler in IDL te declareren.

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

Opmerking

Declareer de functie als void, zelfs als u implementeren als Fire en vergeet.

Wijzigingen die betrekking hebben op de taalsyntaxis

Categorie C# C++/WinRT Zie ook
Toegangsaanpassingen public \<member\> public:
    \<member\>
de methode Button_Click overzetten
Toegang tot een gegevenslid this.variable this->variable  
Asynchrone actie async Task ... IAsyncAction ... IAsyncAction interface, gelijktijdige en asynchrone operaties met C++/WinRT
Asynchrone bewerking async Task<T> ... IAsyncOperation<T> ... IAsyncOperation interface, concurrentie en asynchrone bewerkingen met C++/WinRT
methode Fire-and-forget (impliceert asynchroon) async void ... winrt::fire_and_forget ... de methode CopyButton_Click overzetten, Fire en vergeet
Toegang krijgen tot een geïnventariseerd constante E.Value E::Value Overzetten van de DisplayChangedFormats-methode
Coöperatief wachten await ... co_await ... De CopyButton_Click methode overzetten
Verzameling van geprojecteerde typen als een privéveld private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
GUID-constructie 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} };
Scheidingsteken voor namespace A.B.T A::B::T
Nul null nullptr Het overzetten van de UpdateStatus methode
Een typeobject verkrijgen typeof(MyType) winrt::xaml_typename<MyType>() de eigenschap Scenarios overzetten
Parameterdeclaratie voor een methode MyType MyType const& parameter doorgeven
Parameterdeclaratie voor een asynchrone methode MyType MyType parameter doorgeven
Een statische methode aanroepen T.Method() T::Method()
Tekenreeksen stringof System.String winrt::hstring Verwerking van tekenreeksen in C++/WinRT
Letterlijke tekenreeks "a string literal" L"a string literal" de constructor overzetten, Huidigeen FEATURE_NAME
Afgeleid (of afgeleid) type var auto Het overzetten van de methode BuildClipboardFormatsOutputString
Using-richtlijn using A.B.C; using namespace A::B::C; de constructor overzetten, Huidigeen FEATURE_NAME
Letterlijk/ruwe tekenreeks-letteraliteit @"verbatim string literal" LR"(raw string literal)" Het overzetten van de methode DisplayToast

Opmerking

Als een headerbestand geen using namespace instructie voor een bepaalde naamruimte bevat, moet u alle typenamen voor die naamruimte volledig kwalificeren; of ze ten minste voldoende kwalificeren voor de compiler om ze te vinden. Zie voor een voorbeeld de methodeDisplayToast porting.

Overzetten van klassen en leden

U moet voor elk C#-type bepalen of u deze wilt overzetten naar een Windows Runtime-type of naar een reguliere C++-klasse/-struct/opsomming. Zie Het Klembord-voorbeeld overzetten naar C++/WinRT van C#voor meer informatie en gedetailleerde voorbeelden die illustreren hoe u deze beslissingen kunt nemen.

Een C#-eigenschap wordt doorgaans een accessorfunctie, een mutatorfunctie en een ondersteunend gegevenslid. Zie De eigenschap IsClipboardContentChangedEnabled portingvoor meer informatie en een voorbeeld.

Maak voor niet-statische velden gegevensleden van uw implementatietype .

Een statisch C#-veld wordt een statische C++/WinRT-toegangsfunctie en/of mutatorfunctie. Voor meer informatie en een voorbeeld, zie De constructor overzetten, Huidigeen FEATURE_NAME.

Voor lidfuncties moet u voor elke functie beslissen of deze deel uitmaakt van de IDL, of het een openbare of persoonlijke lidfunctie van uw implementatietype is. Zie IDL voor de MainPage typevoor meer informatie en voorbeelden van hoe u kunt beslissen.

XAML-markeringen en assetbestanden overzetten

In het geval van het Klembord-voorbeeld overzetten naar C++/WinRT van C#, konden we dezelfde XAML-markup (inclusief resources) en hulpmiddelenbestanden in het C#-project en het C++/WinRT-project gebruiken. In sommige gevallen zijn bewerkingen voor markeringen nodig om dat te bereiken. Zie De XAML en stijlen kopiëren die nodig zijn om het overzetten van MainPage-te voltooien.

Wijzigingen die betrekking hebben op procedures in de taal

Categorie C# C++/WinRT Zie ook
Levensduurbeheer in een asynchrone methode Niet van toepassing. auto lifetime{ get_strong() }; of
auto lifetime = get_strong();
De CopyButton_Click methode overzetten
Verwijdering using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
Het porteren van de CopyImage methode
Object samenstellen new MyType(args) MyType{ args } of
MyType(args)
de eigenschap Scenarios overzetten
Niet-geïnitialiseerde verwijzing maken MyType myObject; MyType myObject{ nullptr }; of
MyType myObject = nullptr;
de constructor overzetten, Huidigeen FEATURE_NAME
Object samenstellen in een variabele met args var myObject = new MyType(args); auto myObject{ MyType{ args } }; of
auto myObject{ MyType(args) }; of
auto myObject = MyType{ args }; of
auto myObject = MyType(args); of
MyType myObject{ args }; of
MyType myObject(args);
De methode Footer_Click porten
Object maken in variabele zonder argumenten var myObject = new T(); MyType myObject; Het overzetten van de methode BuildClipboardFormatsOutputString
Verkorte initialisatie van objecten var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Bulkvectorbewerking var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
De CopyButton_Click methode overzetten
Itereren over verzameling foreach (var v in c) for (auto&& v : c) Het overzetten van de methode BuildClipboardFormatsOutputString
Een uitzondering vangen catch (Exception ex) catch (winrt::hresult_error const& ex) De methode PasteButton_Click overzetten
Details van uitzondering ex.Message ex.message() De methode PasteButton_Click overzetten
Een eigenschapswaarde ophalen myObject.MyProperty myObject.MyProperty() de methode NotifyUser porting
Een eigenschapswaarde instellen myObject.MyProperty = value; myObject.MyProperty(value);
Een eigenschapswaarde verhogen myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Voor tekenreeksen schakelt u over naar een opbouwfunctie
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Taaltekenreeks naar Windows Runtime-tekenreeks Niet van toepassing. winrt::hstring{ s }
Stringopbouw StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
tekenreeksopbouw
Tekenreeksinterpolatie $"{i++}) {s.Title}" winrt::to_hstringen/of winrt::hstring::operator+ Porting van de OnNavigatedTo methode
Lege tekenreeks voor vergelijking System.String.Empty winrt::hstring::empty Het overzetten van de UpdateStatus methode
Lege tekenreeks maken var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Woordenlijstbewerkingen 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)
Typeconversie (bij foutmelding) (MyType)v v.as<MyType>() De methode Footer_Click porten
Typeconversie (null bij fout) v as MyType v.try_as<MyType>() De methode PasteButton_Click overzetten
XAML-elementen met x:Name zijn eigenschappen MyNamedElement MyNamedElement() de constructor overzetten, Huidigeen FEATURE_NAME
Overschakelen naar de ui-thread CoreDispatcher.RunAsync CoreDispatcher.RunAsyncof winrt::resume_foreground de methodeNotifyUser porting and Porting the HistoryAndRoaming method
Ui-elementconstructie in imperatieve code op een XAML-pagina Zie UI-elementconstructie Zie UI-elementconstructie

In de volgende secties wordt dieper ingegaan op een aantal items in de tabel.

UI-elementconstructie

Deze codevoorbeelden tonen de constructie van een UI-element in de imperatieve code van een XAML-pagina.

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

ToString()

C#-typen bieden de methode Object.ToString.

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

C++/WinRT biedt deze faciliteit niet rechtstreeks, maar u kunt ook alternatieven gebruiken.

int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".

C++/WinRT ondersteunt ook winrt::to_hstring voor een beperkt aantal typen. Je moet extra overbelastingen toevoegen voor elk extra type dat je naar een string wilt omzetten.

Taal Omzetten naar string Enum naar string converteren
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);

In het geval van het omzetten van een enum naar een string, moet u de implementatie van winrt::to_hstringopgeven.

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));
        }
    }
}

Deze stringificaties worden vaak impliciet verwerkt door middel van gegevensbinding.

<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>

Deze bindingen voeren winrt::to_hstring van de gebonden eigenschap uit. In het geval van het tweede voorbeeld (de StatusEnum), moet u uw eigen overload van winrt::to_hstringdefiniëren, anders krijgt u een compilerfout.

Zie ook De Footer_Click methode overzetten.

Stringopbouw

Voor het bouwen van tekenreeksen heeft C# een ingebouwd tekenreeksbouwer-type.

Categorie C# C++/WinRT
Stringopbouw StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Een Windows Runtime-tekenreeks toevoegen, met behoud van null-waarden builder.Append(s); builder << std::wstring_view{ s };
Een nieuwe regel toevoegen builder.Append(Environment.NewLine); builder << std::endl;
Het resultaat openen s = builder.ToString(); ws = builder.str();

Zie ook het aanpassen van de BuildClipboardFormatsOutputString methodeen het aanpassen van de DisplayChangedFormats methode.

Code uitvoeren op de hoofd-UI-thread

Dit voorbeeld is afkomstig uit het voorbeeld van de streepjescodescanner.

Als u wilt werken aan de hoofd-UI-thread in een C#-project, gebruikt u doorgaans de methode CoreDispatcher.RunAsync, zoals deze.

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // Do work on the main UI thread here.
    });
}

Het is veel eenvoudiger om dat in C++/WinRT uit te drukken. U ziet dat we parameters accepteren per waarde, uitgaande van de veronderstelling dat we ze na het eerste ophangpunt willen aanspreken (de co_awaitin dit geval). Voor meer informatie, zie Parameter-passing.

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

Als u het werk met een andere prioriteit dan de standaardwaarde wilt uitvoeren, raadpleegt u de winrt::resume_foreground functie, die een overload-variant heeft waarmee u een prioriteit kunt opgeven. Zie voor codevoorbeelden die laten zien hoe u een oproep naar winrt::resume_foregroundkunt afwachten, Programmeren met threadaffiniteit in gedachten.

Definieer uw runtime-klassen in IDL

Zie IDL voor de MainPage type, en consolideer uw .idl-bestanden.

Neem de C++/WinRT Windows-naamruimteheaderbestanden op die u nodig hebt

Wanneer u in C++/WinRT een type uit een Windows-naamruimte wilt gebruiken, moet u het bijbehorende C++/WinRT Windows-naamruimteheaderbestand opnemen. Zie bijvoorbeeld Porting the NotifyUser method.

Verpakken en uitpakken

C# zet scalairs automatisch om in objecten. C++/WinRT vereist dat u de functie winrt::box_value expliciet aanroept. In beide talen moet u expliciet uitpakken. Zie Verpakken en uitpakken met C++/WinRT.

In de volgende tabellen gebruiken we deze definities.

C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operatie C# C++/WinRT
Boksen o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Uitpakken i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX en C# veroorzaken uitzonderingen als u probeert een null-aanwijzer naar een waardetype te unboxen. C++/WinRT beschouwt deze als een programmeerfout en loopt vast. Gebruik in C++/WinRT de functie winrt::unbox_value_or als u de case wilt afhandelen waarin het object niet van het type is dat u dacht dat het was.

Scenariobeschrijving C# C++/WinRT
Haal een bekend geheel getal uit de verpakking i = (int)o; i = unbox_value<int>(o);
Als o null is System.NullReferenceException Ongeluk
Indien o geen 'boxed int' is System.InvalidCastException Ongeluk
Haal int uit de verpakking, gebruik een backup als het null is; crasht bij andere waarden. i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Doos integer uitpakken indien mogelijk; alternatieve methode gebruiken voor iets anders i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Zie bijvoorbeeld De methode OnNavigatedToporting and Porting the Footer_Click method.

Het boxen en unboxen van een tekenreeks

Een tekenreeks is in bepaalde opzichten een waardetype en in andere opzichten een verwijzingstype. C# en C++/WinRT behandelen tekenreeksen anders.

Het ABI-type HSTRING is een pointer naar een referentiegetelde tekenreeks. Maar het is niet afgeleid van IInspectable, dus het is technisch geen object. Bovendien vertegenwoordigt een null-HSTRING- de lege tekenreeks. Het verpakken van objecten die niet zijn afgeleid van IInspectable wordt uitgevoerd door ze te verpakken in een IReference<T>, en de Windows Runtime biedt een standaardimplementatie in de vorm van het PropertyValue-object (aangepaste typen worden gerapporteerd als PropertyType::OtherType).

C# vertegenwoordigt een Windows Runtime-tekenreeks als referentietype; terwijl C++/WinRT een tekenreeks projecteert als een waardetype. Dit betekent dat een in een vak geplaatste null-tekenreeks verschillende weergaven kan hebben, afhankelijk van hoe u daar bent gekomen.

Gedrag C# C++/WinRT
Verklaringen object o;
string s;
IInspectable o;
hstring s;
Categorie tekenreekstype Verwijzingstype Waardetype
HSTRING projecten als null "" hstring{}
Zijn null en "" identiek? Nee. Ja
Geldigheid van null s = null;
s.Length genereert NullReferenceException
s = hstring{};
s.size() == 0 (geldig)
Als u null-tekenreeks toewijst aan object o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Als u "" aan object toewijst o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Eenvoudige boxing en unboxing.

Operatie C# C++/WinRT
Een tekenreeks in een doos plaatsen o = s;
Een lege tekenreeks wordt een niet-nul-object.
o = box_value(s);
Een lege tekenreeks wordt een niet-nul-object.
Een bekende reeks uitpakken s = (string)o;
Null-object wordt null-tekenreeks.
InvalidCastException als het geen tekenreeks is.
s = unbox_value<hstring>(o);
Null-object loopt vast.
Crash als het geen tekenreeks is.
Een mogelijke tekenreeks uitpakken s = o as string;
Een null-object of geen tekenreeks wordt een null-tekenreeks.

OF

s = o as string ?? fallback;
Nul of niet-tekstrijk wordt omgezet naar de terugvaloptie.
Lege string gehandhaafd.
s = unbox_value_or<hstring>(o, fallback);
Nul of niet-tekstrijk wordt omgezet naar de terugvaloptie.
Lege string gehandhaafd.

Een klasse beschikbaar maken voor de markeringsextensie {Binding}

Als u de markeringsextensie {Binding} wilt gebruiken om gegevens te binden aan uw gegevenstype, raadpleegt u Bindingsobject dat is gedeclareerd met {Binding}.

Objecten uit XAML-markeringen consumeren

In een C#-project kunt u privéleden en benoemde elementen uit XAML-markeringen gebruiken. Maar in C++/WinRT moeten alle entiteiten die worden gebruikt met behulp van de XAML-{x:Bind}-markeringsextensie openbaar worden weergegeven in IDL.

Als u een Booleaanse waarde bindt, wordt true of false in C# weergegeven, maar wordt Windows.Foundation.IReference\`1<Booleaanse> in C++/WinRT weergegeven.

Zie Objecten gebruiken uit markupvoor codevoorbeelden en meer informatie.

Een gegevensbron beschikbaar maken voor XAML-markeringen

In C++/WinRT versie 2.0.190530.8 of hoger maakt winrt::single_threaded_observable_vector een waarneembare vector die ondersteuning biedt voor zowel IObservableVector<T> als IObservableVector<IInspectable>. Zie voor een voorbeeld de scenario-eigenschapoverzetten.

U kunt uw Midl-bestand (.idl) als volgt schrijven (zie ook Factoring runtime classes in Midl-bestanden (.idl)).

namespace Bookstore
{
    runtimeclass BookSku { ... }

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

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

En implementeer zo.

// 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;
};
...

Zie voor meer informatie over besturingselementen van XAML-items, het binden aan een C++/WinRT-verzamelingen de Verzamelingen met C++/WinRT.

Een gegevensbron beschikbaar maken voor XAML-opmaak (vóór C++/WinRT 2.0.190530.8)

XAML-gegevensbinding vereist dat een itemsbron IIterable<IInspectable>implementeert, evenals een van de volgende combinaties van interfaces.

  • IObservableVector<IInspectable>
  • IBindableVector en INotifyCollectionChanged
  • IBindableVector en IBindableObservableVector
  • IBindableVector zelf (reageert niet op wijzigingen)
  • IVector<IInspectable>
  • IBindableIterable (worden elementen herhaald en opgeslagen in een persoonlijke verzameling)

Een algemene interface, zoals IVector<T->, kan tijdens runtime niet worden gedetecteerd. Elke IVector<T-> heeft een andere interface-id (IID), een functie van T-. Elke ontwikkelaar kan de set van T willekeurig uitbreiden, zodat de XAML-bindingscode nooit de volledige set kan kennen waarvoor een query moet worden uitgevoerd. Deze beperking is geen probleem voor C# omdat elk CLR-object dat IEnumerable implementeert<T-> automatisch IEnumerableimplementeert. Op ABI-niveau betekent dit dat elk object dat IObservableVector implementeert<T> automatisch IObservableVector<IInspectable>implementeert.

C++/WinRT biedt geen garantie. Als een C++/WinRT-runtimeklasse IObservableVector<T->implementeert, kunnen we er niet van uitgaan dat een implementatie van IObservableVector<IInspectable> ook wordt geboden.

Daarom moet het vorige voorbeeld er als volgt uitzien.

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

En de uitvoering.

// 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;
};
...

Als u toegang wilt krijgen tot objecten in m_bookSkus, moet u ze weer terugzetten naar boekwinkel::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;
}

Afgeleide klassen

Als u wilt afleiden van een runtimeklasse, moet de basisklasse composablezijn. C# vereist niet dat u speciale stappen uitvoert om uw klassen composable te maken, maar C++/WinRT wel. U gebruikt de niet-verzegelde trefwoorden om aan te geven dat uw klasse bruikbaar moet zijn als basisklasse.

unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
    ...
}
runtimeclass DerivedPage : BasePage
{
    ...
}

In het headerbestand voor het implementatietypemoet u het headerbestand van de basisklasse opnemen voordat u de automatisch gegenereerde header voor de afgeleide klasse opneemt. Anders krijgt u fouten zoals 'Ongeldig gebruik van dit type als een expressie'.

// 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>
    {
        ...
    }
}

Belangrijke API's