Overstappen op C++/WinRT van C++/CX

Dit onderwerp is de eerste in een reeks waarin wordt beschreven hoe u de broncode in uw C++/CX-project kunt overzetten naar het equivalent in C++/WinRT.

Als uw project ook Windows Runtime C++ Sjabloonbibliotheek (WRL)-typen gebruikt, raadpleegt u Verplaatsen naar C++/WinRT van WRL.

Strategieën voor overdracht

Het is de moeite waard te weten dat het overzetten van C++/CX naar C++/WinRT over het algemeen eenvoudig is, met uitzondering van het verplaatsen van PPL-taken (Parallel Patterns Library) naar coroutines. De modellen zijn verschillend. Er bestaat geen natuurlijke een-op-een-toewijzing van PPL-taken naar coroutines, en er is geen eenvoudige manier om de code mechanisch over te zetten die in alle gevallen werkt. Zie Asynchroon en interop tussen C++/WinRT en C++/CX voor hulp bij dit specifieke aspect van porting en uw opties voor samenwerking tussen de twee modellen.

Ontwikkelteams melden regelmatig dat de rest van het overdrachtswerk grotendeels mechanisch is wanneer ze de asynchrone code overdraagt.

Overdracht in één pas

Als u uw hele project in één keer kunt overzetten, hebt u alleen dit onderwerp nodig voor de informatie die u nodig hebt (en hebt u de onderwerpen over interop die hierop volgen niet nodig). We raden u aan om te beginnen met het maken van een nieuw project in Visual Studio met behulp van een van de C++/WinRT-projectsjablonen (zie Visual Studio ondersteuning voor C++/WinRT). Verplaats vervolgens uw broncodebestanden naar dat nieuwe project en zet alle C++/CX-broncode over naar C++/WinRT zoals u dat doet.

Als u liever het overdrachtswerk in uw bestaande C++/CX-project wilt uitvoeren, moet u er C++/WinRT-ondersteuning aan toevoegen. De stappen die u volgt, worden beschreven in Het nemen van een C++/CX-project en het toevoegen van C++/WinRT-ondersteuning. Tegen de tijd dat u klaar bent met overzetten, hebt u een puur C++/CX-project omgezet in een puur C++/WinRT-project.

Note

Als u een Windows Runtime onderdeelproject hebt, is het overzetten in één pas de enige optie. Een Windows Runtime onderdeelproject dat is geschreven in C++ moet alle C++/CX-broncode of alle C++/WinRT-broncode bevatten. Ze kunnen niet naast dit projecttype bestaan.

Een project geleidelijk overzetten

Met uitzondering van Windows Runtime onderdeelprojecten, zoals vermeld in de vorige sectie, als de grootte of complexiteit van uw codebasis het nodig maakt om uw project geleidelijk te overzetten, hebt u een overdrachtsproces nodig waarin voor een tijd C++/CX- en C++/WinRT-code naast elkaar in hetzelfde project bestaat. Naast het lezen van dit onderwerp, zie ook Interop tussen C++/WinRT en C++/CX en Asynchrony, en interop tussen C++/WinRT en C++/CX. Deze onderwerpen bevatten informatie- en codevoorbeelden die laten zien hoe u kunt samenwerken tussen de twee taalprojecties.

Als u een project wilt voorbereiden voor een geleidelijk overdrachtsproces, kunt u C++/WinRT-ondersteuning toevoegen aan uw C++/CX-project. De stappen die u volgt, worden beschreven in Het nemen van een C++/CX-project en het toevoegen van C++/WinRT-ondersteuning. Vervolgens kunt u vanaf daar geleidelijk porten.

Een andere optie is het maken van een nieuw project in Visual Studio met behulp van een van de C++/WinRT-projectsjablonen (zie Visual Studio ondersteuning voor C++/WinRT). Voeg vervolgens C++/CX-ondersteuning toe aan dat project. De stappen die u volgt om dit te doen, worden beschreven in Het nemen van een C++/WinRT-project en het toevoegen van C++/CX-ondersteuning. Vervolgens kunt u beginnen met het verplaatsen van de broncode naar die code en een deel van de C++/CX-broncode overzetten naar C++/WinRT zoals u dat doet.

In beide gevallen werkt u in beide richtingen samen tussen uw C++/WinRT-code en alle C++/CX-code die u nog niet hebt geporteerd.

Note

Zowel C++/CX als de Windows SDK declareert typen in de hoofdnaamruimte Windows. Een Windows type dat in C++/WinRT wordt geprojecteerd, heeft dezelfde volledig gekwalificeerde naam als het Windows type, maar wordt in de winrt-naamruimte C++ geplaatst. Met deze afzonderlijke naamruimten kunt u in uw eigen tempo overzetten van C++/CX naar C++/WinRT.

Een XAML-project geleidelijk overzetten

Belangrijk

Voor een project dat gebruikmaakt van XAML, moeten alle XAML-paginatypen op elk gewenst moment volledig C++/CX of volledig C++/WinRT zijn. U kunt nog steeds C++/CX en C++/WinRT buiten XAML-paginatypen combineren binnen hetzelfde project (in uw modellen en viewmodels en elders).

Voor dit scenario is de werkstroom die we aanbevelen om een nieuw C++/WinRT-project te maken en broncode en markeringen te kopiëren vanuit het C++/CX-project. Zolang al uw XAML-paginatypen C++/WinRT zijn, kunt u nieuwe XAML-pagina's toevoegen met Project>Nieuw item toevoegen...>Visual C++>Lege pagina (C++/WinRT).

U kunt ook een Windows Runtime-onderdeel (WRC) gebruiken om code uit het XAML C++/CX-project te factoreren terwijl u het overdrat.

  • U kunt een nieuw C++/CX WRC-project maken, zoveel C++/CX-code als u in dat project kunt verplaatsen en vervolgens het XAML-project wijzigen in C++/WinRT.
  • U kunt ook een nieuw C++/WinRT WRC-project maken, het XAML-project laten staan als C++/CX en beginnen met het overzetten van C++/CX naar C++/WinRT en het verplaatsen van de resulterende code uit het XAML-project en naar het onderdeelproject.
  • U kunt ook een C++/CX-onderdeelproject naast een C++/WinRT-onderdeelproject in dezelfde oplossing hebben, beide vanuit uw toepassingsproject verwijzen en geleidelijk van het ene naar het andere project overzetten. Zie Interop tussen C++/WinRT en C++/CX voor meer informatie over het gebruik van de twee taalprojecties in hetzelfde project.

Eerste stappen bij het overzetten van een C++/CX-project naar C++/WinRT

Ongeacht welke migratiestrategie u kiest (migratie in één keer of geleidelijke migratie), is uw eerste stap uw project voor te bereiden op de migratie. Hier volgt een overzicht van wat we hebben beschreven in Strategieën voor overdracht in termen van het soort project waarmee u begint en hoe u het instelt.

  • Porteren in één pas. Maak een nieuw project in Visual Studio met behulp van een van de C++/WinRT-projectsjablonen. Verplaats de bestanden van uw C++/CX-project naar dat nieuwe project en zet de C++/CX-broncode over.
  • Een niet-XAML-project geleidelijk overzetten. U kunt ervoor kiezen om C++/WinRT-ondersteuning toe te voegen aan uw C++/CX-project (zie Een C++/CX-project nemen en C++/WinRT-ondersteuning toevoegen) en de poort geleidelijk. Of u kunt ervoor kiezen om een nieuw C++/WinRT-project te maken en C++/CX-ondersteuning toe te voegen (zie C++/WinRT-project nemen en C++/CX-ondersteuning toevoegen), bestanden geleidelijk verplaatsen en de poort geleidelijk verplaatsen.
  • Een XAML-project geleidelijk overzetten. Maak een nieuw C++/WinRT-project, verplaats bestanden geleidelijk en poort. Op elk moment moeten uw XAML-paginatypen ofwel allemaal C++/WinRT of allemaal C++/CX zijn.

De rest van dit onderwerp geldt ongeacht welke migratiestrategie u kiest. Het bevat een catalogus met technische details die betrokken zijn bij het overzetten van broncode van C++/CX naar C++/WinRT. Als u geleidelijk migreert, dan wilt u waarschijnlijk ook Interop tussen C++/WinRT en C++/CX en Asynchronie, en interop tussen C++/WinRT en C++/CX raadplegen.

Naamgevingsconventies voor bestanden

XAML-opmaakbestanden

Herkomst van het bestand C++/CX C++/WinRT
XAML-bestanden voor ontwikkelaars MyPage.xaml
MyPage.xaml.h
MyPage.xaml.cpp
MyPage.xaml
MyPage.h
MyPage.cpp
MyPage.idl (zie hieronder)
Gegenereerde XAML-bestanden MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.g.h

Merk op dat C++/WinRT de .xaml uit de bestandsnamen *.h en *.cpp verwijdert.

C++/WinRT voegt een extra ontwikkelaarsbestand toe, het Midl-bestand (.idl). Met C++/CX wordt dit bestand intern gegenereerd, waaraan elk openbaar en beveiligd lid wordt toegevoegd. In C++/WinRT voegt u het bestand zelf toe en stelt u het zelf op. Zie XAML-besturingselementen voor meer informatie, codevoorbeelden en een overzicht van het ontwerpen van IDL ; bind aan een C++/WinRT-eigenschap.

Zie ook Runtimeklassen onderbrengen in MIDL-bestanden (.idl)

Runtime-klassen

C++/CX legt geen beperkingen op voor de namen van uw headerbestanden; Het is gebruikelijk om meerdere runtimeklassedefinities in één headerbestand te plaatsen, met name voor kleine klassen. Maar C++/WinRT vereist dat elke runtimeklasse een eigen headerbestand heeft met de naam van de klasse.

C++/CX C++/WinRT
Common.h
ref class A { ... }
ref class B { ... }
Common.idl
runtimeclass A { ... }
runtimeclass B { ... }
A.h.
namespace implements {
  struct A { ... };
}
B.h.
namespace implements {
  struct B { ... };
}

Minder gangbaar (maar nog steeds juridisch) in C++/CX is het gebruik van headerbestanden met een andere naam voor aangepaste XAML-besturingselementen. U moet de naam van dit headerbestand wijzigen zodat dit overeenkomt met de klassenaam.

C++/CX C++/WinRT
A.xaml
<Page x:Class="LongNameForA" ...>
A.xaml
<Page x:Class="LongNameForA" ...>
A.h
partial ref class LongNameForA { ... }
LongNameForA.h
namespace implements {
  struct LongNameForA { ... };
}

Vereisten voor headerbestanden

Voor C++/CX hoeft u geen speciale headerbestanden op te nemen, omdat hiermee intern headerbestanden van .winmd bestanden worden gegenereerd. In C++/CX is het gebruikelijk om using-richtlijnen te gebruiken voor naamruimtes die u bij naam gebruikt.

using namespace Windows::Media::Playback;

String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
    return item->VideoTracks->GetAt(0)->Name;
}

Met de using namespace Windows::Media::Playback instructie kunnen we zonder een naamruimtevoorvoegsel schrijven MediaPlaybackItem . We hebben ook de naamruimte Windows.Media.Core behandeld, omdat item->VideoTracks->GetAt(0) een Windows.Media.Core.VideoTrack retourneert. Maar we hoefden de naam VideoTrack nergens te typen, dus we hadden geen instructie nodig using Windows.Media.Core .

Maar C++/WinRT vereist dat u een headerbestand opneemt dat overeenkomt met elke naamruimte die u gebruikt, zelfs als u deze niet noemt.

#include <winrt/Windows.Media.Playback.h>
#include <winrt/Windows.Media.Core.h> // !!This is important!!

using namespace winrt;
using namespace Windows::Media::Playback;

winrt::hstring NameOfFirstVideoTrack(MediaPlaybackItem const& item)
{
    return item.VideoTracks().GetAt(0).Name();
}

Anderzijds hoeven we winrt/Windows.Foundation.Collections.h niet op te nemen, ook al is de gebeurtenis MediaPlaybackItem.AudioTracksChanged van het type TypedEventHandler<MediaPlaybackItem, Windows.Foundation.Collections.IVectorChangedEventArgs>, omdat we deze gebeurtenis niet hebben gebruikt.

Voor C++/WinRT moet u ook headerbestanden opnemen voor naamruimten die worden gebruikt door XAML-markeringen.

<!-- MainPage.xaml -->
<Rectangle Height="400"/>

Als u de klasse Rechthoek gebruikt, moet u deze include toevoegen.

// MainPage.h
#include <winrt/Microsoft.UI.Xaml.Shapes.h>

Als u een headerbestand vergeet, dan compileert alles gewoon goed, maar krijgt u linkerfouten omdat de consume_-klassen ontbreken.

Parameteroverdracht

Bij het schrijven van C++/CX-broncode geeft u C++/CX-typen als functieparameters door als hat-verwijzingen (^).

void LogPresenceRecord(PresenceRecord^ record);

In C++/WinRT moet u voor synchrone functies standaard parameters gebruiken const& . Hiermee voorkomt u kopieën en onderling vergrendelde overhead. Maar uw coroutines moeten pass-by-value gebruiken om ervoor te zorgen dat ze worden vastgelegd op waarde en levensduurproblemen voorkomen (zie Gelijktijdigheid en asynchrone bewerkingen met C++/WinRT) voor meer informatie.

void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);

Een C++/WinRT-object is in wezen een waarde die een interfaceaanwijzer bevat naar het backing-Windows Runtime-object. Wanneer u een C++/WinRT-object kopieert, kopieert de compiler de ingekapselde interfaceaanwijzer, waardoor het aantal verwijzingen wordt verhoogd. Uiteindelijke vernietiging van de kopie omvat het verlagen van het aantal verwijzingen. Neem dus alleen de overhead van een kopie voor lief wanneer dat nodig is.

Variabele en veldverwijzingen

Bij het schrijven van C++/CX-broncode gebruikt u hat-variabelen (^) om te verwijzen naar Windows Runtime objecten en de pijloperator (->) om een hat-variabele te deducteren.

IVectorView<User^>^ userList = User::Users;

if (userList != nullptr)
{
    for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
    ...

Wanneer u de code omzet naar de equivalente C++/WinRT-code, komt u al een heel eind door de dakjes te verwijderen en de pijloperator (->) te veranderen in de puntoperator (.). C++/WinRT-projecttypen zijn waarden en geen aanwijzers.

IVectorView<User> userList = User::Users();

if (userList != nullptr)
{
    for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
    ...

De standaardconstructor voor een C++/CX-hat-verwijzing initialiseert deze naar null. Hier volgt een C++/CX-codevoorbeeld waarin we een variabele/veld van het juiste type maken, maar een veld dat niet geïnitialiseerd is. Met andere woorden, het verwijst in eerste instantie niet naar een TextBlock; we zijn van plan om later een verwijzing toe te wijzen.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

Zie Vertraagde initialisatie voor het equivalent in C++/WinRT.

Properties

De C++/CX-taalextensies bevatten het concept van eigenschappen. Wanneer u C++/CX-broncode schrijft, hebt u toegang tot een eigenschap alsof het een veld is. Standard C++ beschikt niet over het concept van een eigenschap. In C++/WinRT roept u functies ophalen en instellen aan.

In de volgende voorbeelden zijn XboxUserId, UserState, PresenceDeviceRecords en Size alle eigenschappen.

Een waarde ophalen uit een eigenschap

U krijgt als volgt een eigenschapswaarde in C++/CX.

void Sample::LogPresenceRecord(PresenceRecord^ record)
{
    auto id = record->XboxUserId;
    auto state = record->UserState;
    auto size = record->PresenceDeviceRecords->Size;
}

De equivalente C++/WinRT-broncode roept een functie aan met dezelfde naam als de eigenschap, maar zonder parameters.

void Sample::LogPresenceRecord(PresenceRecord const& record)
{
    auto id = record.XboxUserId();
    auto state = record.UserState();
    auto size = record.PresenceDeviceRecords().Size();
}

Houd er rekening mee dat de functie PresenceDeviceRecords een Windows Runtime-object retourneert dat zelf een functie Grootte heeft. Omdat het geretourneerde object ook een door C++/WinRT geprojecteerd type is, derefereren we het met de puntoperator om Size aan te roepen.

Een eigenschap instellen op een nieuwe waarde

Het instellen van een eigenschap op een nieuwe waarde volgt een vergelijkbaar patroon. Eerst in C++/CX.

record->UserState = newValue;

Als u het equivalent in C++/WinRT wilt uitvoeren, roept u een functie aan met dezelfde naam als de eigenschap en geeft u een argument door.

record.UserState(newValue);

Een exemplaar van een klasse maken

U werkt met een C++/CX-object via een handle ernaar, algemeen bekend als een hat-referentie (^). U maakt een nieuw object via het ref new trefwoord, dat op zijn beurt RoActivateInstance aanroept om een nieuw exemplaar van de runtimeklasse te activeren.

using namespace Windows::Storage::Streams;

class Sample
{
private:
    Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};

Een C++/WinRT-object is een waarde; U kunt deze dus toewijzen aan de stapel of als een veld van een object. U gebruikt ref new (nochnew) om een C++/WinRT-object toe te wijzen. Achter de schermen wordt RoActivateInstance nog steeds aangeroepen.

using namespace winrt::Windows::Storage::Streams;

struct Sample
{
private:
    Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};

Als een resource duur is om te initialiseren, is het gebruikelijk om de initialisatie ervan te vertragen totdat deze daadwerkelijk nodig is. Zoals al vermeld, initialiseert de standaardconstructor voor een C++/CX-hat-verwijzing deze naar null.

using namespace Windows::Storage::Streams;

class Sample
{
public:
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer^ m_gamerPicBuffer;
};

Dezelfde code die is overgezet naar C++/WinRT. Let op het gebruik van de std::nullptr_t constructor. Zie Vertraagde initialisatie voor meer informatie over die constructor.

using namespace winrt::Windows::Storage::Streams;

struct Sample
{
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer m_gamerPicBuffer{ nullptr };
};

Hoe de standaardconstructor van invloed is op verzamelingen

C++ verzamelingstypen maken gebruik van de standaardconstructor, wat kan leiden tot onbedoelde objectconstructie.

Scenario C++/CX C++/WinRT (onjuist) C++/WinRT (juist)
Lokale variabele, in eerste instantie leeg TextBox^ textBox; TextBox textBox; // Creates a TextBox! TextBox textBox{ nullptr };
Lidvariabele, in eerste instantie leeg class C {
  TextBox^ textBox;
};
class C {
  TextBox textBox; // Creates a TextBox!
};
class C {
  TextBox textbox{ nullptr };
};
Globale variabele, in eerste instantie leeg TextBox^ g_textBox; TextBox g_textBox; // Creates a TextBox! TextBox g_textBox{ nullptr };
Vector van lege verwijzingen std::vector<TextBox^> boxes(10); // Creates 10 TextBox objects!
std::vector<TextBox> boxes(10);
std::vector<TextBox> boxes(10, nullptr);
Een waarde instellen in een kaart std::map<int, TextBox^> boxes;
boxes[2] = value;
std::map<int, TextBox> boxes;
// Creates a TextBox at 2,
// then overwrites it!
boxes[2] = value;
std::map<int, TextBox> boxes;
boxes.insert_or_assign(2, value);
Matrix met lege verwijzingen TextBox^ boxes[2]; // Creates 2 TextBox objects!
TextBox boxes[2];
TextBox boxes[2] = { nullptr, nullptr };
Paar std::pair<TextBox^, String^> p; // Creates a TextBox!
std::pair<TextBox, String> p;
std::pair<TextBox, String> p{ nullptr, nullptr };

Meer informatie over verzamelingen lege verwijzingen

Wanneer u een Platform::Array^ hebt (zie Port Platform::Array^) in C++/CX, hebt u de keuze om die over te zetten naar een std::vector in C++/WinRT (sterker nog, naar elke aaneengesloten container) in plaats van deze als array te laten staan. Er zijn voordelen bij het kiezen van std::vector.

Hoewel er bijvoorbeeld een afkorting is voor het maken van een vector met een vaste grootte van lege verwijzingen (zie de bovenstaande tabel), is er geen dergelijke afkorting voor het maken van een matrix met lege verwijzingen. U moet nullptr herhalen voor elk element in een array. Als u te weinig hebt, worden de extra's standaard samengesteld.

Voor een vector kunt u deze vullen met lege verwijzingen bij initialisatie (zoals in de bovenstaande tabel), of u kunt deze vullen met lege verwijzingen na initialisatie met code zoals deze.

std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.

Meer informatie over het std::map-voorbeeld

De [] subscript-operator voor std::map gedraagt zich als volgt.

  • Als de sleutel in de kaart wordt gevonden, retourneert u een verwijzing naar de bestaande waarde (die u kunt overschrijven).
  • Als de sleutel niet in de kaart wordt gevonden, maakt u een nieuwe vermelding in de kaart die bestaat uit de sleutel (verplaatst, indien verplaatst) en een standaard samengestelde waarde en retourneert u een verwijzing naar de waarde (die u vervolgens kunt overschrijven).

Met andere woorden, de [] operator maakt altijd een vermelding op de kaart. Dit verschilt van C#, Java en JavaScript.

Converteren van een basisruntimeklasse naar een afgeleide

Het komt vaak voor dat je een verwijzing naar een basisklasse hebt waarvan je weet dat die naar een object van een afgeleid type verwijst. In C++/CX gebruikt u dynamic_cast om de verwijzing naar de basisklasse te casten naar een verwijzing naar de afgeleide klasse. Het dynamic_cast is eigenlijk gewoon een verborgen aanroep van QueryInterface. Hier volgt een typisch voorbeeld: u verwerkt een gewijzigde gebeurtenis van een afhankelijkheidseigenschap en u wilt casten van DependencyObject terug naar het werkelijke type dat eigenaar is van de afhankelijkheidseigenschap.

void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject^ d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
    BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };

    if (theControl != nullptr)
    {
        // succeeded ...
    }
}

De equivalente C++/WinRT-code vervangt de dynamic_cast door een aanroep naar de functie IUnknown::try_as , die QueryInterface inkapselt. U kunt in plaats daarvan ook IUnknown::as aanroepen. Deze methode genereert een uitzondering als de vereiste interface (de standaardinterface van het type dat u aanvraagt) niet wordt geretourneerd. Hier volgt een C++/WinRT-codevoorbeeld.

void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject const& d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
{
    if (BgLabelControlApp::BgLabelControl theControl{ d.try_as<BgLabelControlApp::BgLabelControl>() })
    {
        // succeeded ...
    }

    try
    {
        BgLabelControlApp::BgLabelControl theControl{ d.as<BgLabelControlApp::BgLabelControl>() };
        // succeeded ...
    }
    catch (winrt::hresult_no_interface const&)
    {
        // failed ...
    }
}

Afgeleide klassen

Om van een runtime-klasse te kunnen overerven, moet de basisklasse samenstelbaar zijn. C++/CX vereist niet dat u speciale stappen uitvoert om uw klassen composable te maken, maar C++/WinRT wel. U gebruikt het niet-verzegelde trefwoord om aan te geven dat uw klasse bruikbaar moet zijn als basisklasse.

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

In uw implementatieheaderklasse moet u het bestand met de basisklasseheader 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>
    {
        ...
    }
}

Gebeurtenisafhandeling met een gemachtigde

Hier volgt een typisch voorbeeld van het verwerken van een gebeurtenis in C++/CX met behulp van een lambda-functie als gemachtigde in dit geval.

auto token = myButton->Click += ref new RoutedEventHandler([=](Platform::Object^ sender, RoutedEventArgs^ args)
{
    // Handle the event.
    // Note: locals are captured by value, not reference, since this handler is delayed.
});

Dit is het equivalent in C++/WinRT.

auto token = myButton().Click([=](IInspectable const& sender, RoutedEventArgs const& args)
{
    // Handle the event.
    // Note: locals are captured by value, not reference, since this handler is delayed.
});

In plaats van een lambda-functie kunt u ervoor kiezen om uw gemachtigde te implementeren als een gratis functie of als aanwijzer-naar-lid-functie. Zie Gebeurtenissen verwerken met behulp van gemachtigden in C++/WinRT voor meer informatie.

Als u code uit een C++/CX-codebasis migreert waarin gebeurtenissen en delegates intern worden gebruikt (niet over binaire grenzen heen), helpt winrt::delegate u om dat patroon in C++/WinRT na te bootsen. Zie ook Geparameteriseerde gemachtigden, eenvoudige signalen en callbacks binnen een project.

Een gemachtigde intrekken

In C++/CX gebruikt u de -= operator om een eerdere gebeurtenisregistratie in te trekken.

myButton->Click -= token;

Dit is het equivalent in C++/WinRT.

myButton().Click(token);

Zie Een geregistreerde gemachtigde intrekken voor meer informatie en opties.

Verpakken en uitpakken

C++/CX boxet scalaire waarden automatisch naar objecten. C++/WinRT vereist dat u de winrt::box_value-functie expliciet aanroept. Voor beide talen moet u het selectievakje expliciet uitvinken. Zie Boksen en uitpakken met C++/WinRT.

In de volgende tabellen gebruiken we deze definities.

C++/CX C++/WinRT
int i; int i;
String^ s; winrt::hstring s;
Object^ o; IInspectable o;
Operation C++/CX 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# werpen een uitzondering op als u probeert een null-pointer te unboxen naar een waardetype. 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.

Scenario C++/CX C++/WinRT
Een bekend geheel getal uitpakken i = (int)o; i = unbox_value<int>(o);
Als o null is Platform::NullReferenceException Ongeluk
Als o geen geboxte int is Platform::InvalidCastException Ongeluk
Pak int uit, gebruik de fallback als deze null is; crash in alle andere gevallen i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Unbox int indien mogelijk; gebruik anders een fallback voor alle andere gevallen auto box = dynamic_cast<IBox<int>^>(o);
i = box ? box->Value : fallback;
i = unbox_value_or<int>(o, fallback);

Boxing en unboxing van een tekenreeks

Een tekenreeks is in sommige opzichten een waardetype en in andere opzichten een referentietype. C++/CX en C++/WinRT behandelen tekenreeksen anders.

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

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

Bovendien kunt u met C++/CX een null-String^ derefereren, in welk geval deze zich gedraagt als de tekenreeks "".

Behavior C++/CX C++/WinRT
Verklaringen Object^ o;
String^ s;
IInspectable o;
hstring s;
Categorie van het tekenreekstype Verwijzingstype Waardetype
null HSTRING projecteert als (String^)nullptr hstring{}
Zijn null en "" identiek? Yes Yes
Geldigheid van null s = nullptr;
s->Length == 0 (geldig)
s = hstring{};
s.size() == 0 (geldig)
Als u null-tekenreeks toewijst aan object o = (String^)nullptr;
o == nullptr
o = box_value(hstring{});
o != nullptr
Als u "" aan een object toewijst o = "";
o == nullptr
o = box_value(hstring{L""});
o != nullptr

Basisprincipes van boxing en unboxing.

Operation C++/CX C++/WinRT
Plaats een kader om een tekenreeks o = s;
Een lege tekenreeks wordt nullptr.
o = box_value(s);
Een lege tekenreeks wordt een object dat niet null is.
Een bekende tekenreeks uitpakken s = (String^)o;
Een null-object wordt omgezet in een lege tekenreeks.
InvalidCastException als het geen string is.
s = unbox_value<hstring>(o);
Null-object loopt vast.
Crash als het geen tekenreeks is.
Een mogelijke tekenreeks uitpakken s = dynamic_cast<String^>(o);
Een null-object of een niet-tekenreekswaarde wordt een lege tekenreeks.
s = unbox_value_or<hstring>(o, fallback);
Null of niet-tekenreeks wordt terugval.
De lege tekenreeks is behouden.

Gelijktijdigheid en asynchrone bewerkingen

De PPL (Parallel Patterns Library) (concurrency::task, bijvoorbeeld) is bijgewerkt ter ondersteuning van C++/CX-hat-referenties.

Voor C++/WinRT moet u in plaats daarvan coroutines en co_await gebruiken. Zie Gelijktijdigheid en asynchrone bewerkingen met C++/WinRT voor meer informatie en codevoorbeelden.

Objecten uit XAML-opmaak gebruiken

In een C++/CX-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 -markeringsextensie {x:Bind} openbaar worden weergegeven in IDL.

Ook geeft een binding aan een Booleaanse waarde in C++/CX true of false weer, maar in C++/WinRT wordt Windows.Foundation.IReference`1<Boolean> weergegeven.

Zie Objecten in markup gebruiken voor meer informatie en codevoorbeelden.

C++/CX-Platform-typen toewijzen aan C++/WinRT-typen

C++/CX biedt verschillende gegevenstypen in de platformnaamruimte . Deze typen zijn niet standaard C++, dus u kunt ze alleen gebruiken wanneer u Windows Runtime taalextensies inschakelt (Visual Studio projecteigenschap C/C++>Algemeen>verbruik Windows Runtime Extensie>Ja (/ZW)). De onderstaande tabel helpt u bij het overzetten van platformtypen naar hun equivalenten in C++/WinRT. Zodra u dat hebt gedaan, kunt u de /ZW optie uitschakelen, omdat C++/WinRT standaard C++is.

C++/CX C++/WinRT
Platform::Agile^ winrt::agile_ref
Platform::Array^ Zie Port Platform::Array^
Platform::Exception^ winrt::hresult_error
Platform::InvalidArgumentException^ winrt::hresult_invalid_argument
Platform::Object^ winrt::Windows::Foundation::IInspectable
Platform::String^ winrt::hstring

Zet Platform::Agile^ over naar winrt::agile_ref

Het type Platform::Agile^ in C++/CX vertegenwoordigt een Windows Runtime klasse die toegankelijk is vanuit elke thread. Het C++/WinRT-equivalent is winrt::agile_ref.

In C++/CX.

Platform::Agile<Windows::UI::Core::CoreWindow> m_window;

In C++/WinRT (WinUI 3 gebruikt Microsoft::UI::Xaml::Window in plaats van CoreWindow).

winrt::agile_ref<Microsoft::UI::Xaml::Window> m_window;

Port Platform::Array^

In gevallen waarin C++/CX vereist dat u een matrix gebruikt, kunt u met C++/WinRT elke aaneengesloten container gebruiken. Zie Hoe de standaardconstructor van invloed is op verzamelingen om een reden waarom std::vector een goede keuze is.

Dus wanneer u in C++/CX een Platform::Array^ hebt, omvatten uw opties voor het overzetten een initialisatielijst, een std::array of een std::vector. Zie Standard Initializer-lijsten en Standard-matrices en vectoren voor meer informatie en codevoorbeelden.

Port Platform::Exception^ naar winrt::hresult_error

Het type Platform::Exception^ wordt geproduceerd in C++/CX wanneer een Windows Runtime-API een niet-S_OK HRESULT retourneert. Het C++/WinRT-equivalent is winrt::hresult_error.

Als u wilt overzetten naar C++/WinRT, wijzigt u alle code die gebruikmaakt van Platform::Exception^ om winrt::hresult_error te gebruiken.

In C++/CX.

catch (Platform::Exception^ ex)

In C++/WinRT.

catch (winrt::hresult_error const& ex)

C++/WinRT biedt deze uitzonderingsklassen.

Uitzonderingstype Basisklasse HRESULT
winrt::hresult_error roep hresult_error::to_abi aan
winrt::hresult_access_denied winrt::hresult_error E_ACCESSDENIED
winrt::hresult_canceled winrt::hresult_error ERROR_CANCELLED
winrt::hresult_changed_state winrt::hresult_error E_CHANGED_STATE
winrt::hresult_class_not_available winrt::hresult_error CLASS_E_CLASSNOTAVAILABLE
winrt::hresult_illegal_delegate_assignment winrt::hresult_error E_ILLEGAL_DELEGATE_ASSIGNMENT
winrt::hresult_illegal_method_call winrt::hresult_error E_ILLEGAL_METHOD_CALL
winrt::hresult_illegal_state_change winrt::hresult_error E_ILLEGAL_STATE_CHANGE
winrt::hresult_invalid_argument winrt::hresult_error E_INVALIDARG
winrt::hresult_no_interface winrt::hresult_error E_NOINTERFACE
winrt::hresult_not_implemented winrt::hresult_error E_NOTIMPL
winrt::hresult_out_of_bounds winrt::hresult_error E_BOUNDS
winrt::hresult_wrong_thread winrt::hresult_error RPC_E_WRONG_THREAD

Houd er rekening mee dat elke klasse (via de hresult_error basisklasse) een to_abi functie biedt, die het HRESULT van de fout retourneert en een berichtfunctie die de tekenreeksweergave van dat HRESULT retourneert.

Hier volgt een voorbeeld van het genereren van een uitzondering in C++/CX.

throw ref new Platform::InvalidArgumentException(L"A valid User is required");

En het equivalent in C++/WinRT.

throw winrt::hresult_invalid_argument{ L"A valid User is required" };

Zet Platform::Object^ over naar winrt::Windows::Foundation::IInspectable

Net als alle C++/WinRT-typen is winrt::Windows::Foundation::IInspectable een waardetype. U initialiseert als volgt een variabele van dat type in null.

winrt::Windows::Foundation::IInspectable var{ nullptr };

Converteer Platform::String^ naar winrt::hstring

Platform::String^ is gelijk aan het type Windows Runtime HSTRING ABI. Voor C++/WinRT is het equivalent winrt::hstring. Maar met C++/WinRT kunt u Windows Runtime API's aanroepen met behulp van tekenreekstypen voor de brede C++-standaardbibliotheek, zoals std::wstring en/of brede letterlijke tekenreeksen. Zie Tekenreeksafhandeling in C++/WinRT voor meer informatie en codevoorbeelden.

Met C++/CX hebt u toegang tot de eigenschap Platform::String::D ata om de tekenreeks op te halen als een C-stijl const wchar_t* matrix (bijvoorbeeld om deze door te geven aan std::wcout).

auto var{ titleRecord->TitleName->Data() };

Als u hetzelfde wilt doen met C++/WinRT, kunt u de functie hstring::c_str gebruiken om een tekenreeksversie met null-beëindigde C-stijl op te halen, net zoals u kunt doen vanuit std::wstring.

auto var{ titleRecord.TitleName().c_str() };

Als het gaat om het implementeren van API's die tekenreeksen aannemen of retourneren, wijzigt u doorgaans elke C++/CX-code die gebruikmaakt van Platform::String^ om winrt::hstring te gebruiken.

Hier volgt een voorbeeld van een C++/CX-API die een tekenreeks gebruikt.

void LogWrapLine(Platform::String^ str);

Voor C++/WinRT kunt u deze API in MIDL 3.0 als volgt declareren.

// LogType.idl
void LogWrapLine(String str);

De C++/WinRT-hulpprogrammaketen genereert vervolgens de broncode die er als volgt uitziet.

void LogWrapLine(winrt::hstring const& str);

ToString()

C++/CX-typen bieden de methode Object::ToString .

int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"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. U moet overbelastingen toevoegen voor eventuele extra typen die u wilt stringifyen.

Language Stringify int Enum naar tekenreeks converteren
C++/CX String^ result = "hello, " + intValue.ToString(); String^ result = "status: " + status.ToString();
C++/WinRT hstring result = L"hello, " + to_hstring(intValue); // must define overload (see below)
hstring result = L"status: " + to_hstring(status);

Als u een enum naar een tekenreeks converteert, moet u de implementatie van winrt::to_hstring aanleveren.

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 tekenreeksrepresentaties worden vaak impliciet verwerkt door gegevensbinding.

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

Met deze bindingen wordt winrt::to_hstring van de afhankelijke eigenschap uitgevoerd. In het tweede voorbeeld ( statusenum) moet u uw eigen overbelasting van winrt::to_hstring opgeven, anders krijgt u een compilerfout.

Tekenreeks bouwen

C++/CX en C++/WinRT defereren naar de standaard std::wstringstream voor het bouwen van tekenreeksen.

Operation C++/CX C++/WinRT
Tekenreeks toevoegen, null-waarden behouden stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
Tekenreeks toevoegen, stoppen bij het eerste nul-teken stream << s->Data(); stream << s.c_str();
Resultaat extraheren ws = stream.str(); ws = stream.str();

Meer voorbeelden

In de onderstaande voorbeelden is ws een variabele van het type std::wstring. Bovendien kan C++/CX een platform::tekenreeks maken op basis van een 8-bits tekenreeks, doet C++/WinRT dat niet.

Operation C++/CX C++/WinRT
Tekenreeks maken van letterlijke waarde String^ s = "hello";
String^ s = L"hello";
// winrt::hstring s{ "hello" }; // Doesn't compile
winrt::hstring s{ L"hello" };
Converteren van std::wstring, met behoud van null-waarden String^ s = ref new String(ws.c_str(),
  (uint32_t)ws.size());
winrt::hstring s{ ws };
s = winrt::hstring(ws);
// s = ws; // Doesn't compile
Converteren vanuit std::wstring, stop bij de eerste null String^ s = ref new String(ws.c_str()); winrt::hstring s{ ws.c_str() };
s = winrt::hstring(ws.c_str());
// s = ws.c_str(); // Doesn't compile
Converteren naar std::wstring, met behoud van null-waarden std::wstring ws{ s->Data(), s->Length };
ws = std::wstring(s>Data(), s->Length);
std::wstring ws{ s };
ws = s;
Omzetten naar std::wstring, stop bij de eerste null std::wstring ws{ s->Data() };
ws = s->Data();
std::wstring ws{ s.c_str() };
ws = s.c_str();
Letterlijke gegevens doorgeven aan methode Method("hello");
Method(L"hello");
// Method("hello"); // Doesn't compile
Method(L"hello");
Std::wstring doorgeven aan methode Method(ref new String(ws.c_str(),
  (uint32_t)ws.size()); // Stops on first null
Method(ws);
// param::winrt::hstring accepts std::wstring_view

Belangrijke API's

Note

Veel C++/WinRT-onderwerpen zijn bezig met het migreren van de UWP-documentatie naar deze sectie. Totdat de migratie is voltooid, kunnen koppelingen in de onderstaande lijst leiden tot de sectie UWP-documenten. De C++/WinRT-taalprojectie is hetzelfde voor zowel UWP- als WinUI 3-apps, zodat inhoud in beide contexten van toepassing is. Eventuele UWP-specifieke patronen (zoals app-levenscyclus of Windows.UI naamruimte-API's) worden expliciet vermeld in deze artikelen.