Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Det här avsnittet är det första i en serie som beskriver hur du kan portera källkoden i C++/CX-projektet till dess motsvarighet i C++/WinRT.
Om ditt projekt också använder Windows Runtime C++ Template Library (WRL)-typer kan du läsa Flytta till C++/WinRT från WRL.
Strategier för portning
Det är värt att känna till att portning från C++/CX till C++/WinRT i regel är ganska okomplicerad, med det enda undantaget att man går från Parallel Patterns Library (PPL)-uppgifter till korutiner. Modellerna är olika. Det finns ingen naturlig en-till-en-mappning mellan uppgifter i PPL och korutiner, och det finns inget enkelt sätt att mekaniskt porta koden som fungerar i samtliga fall. Om du vill ha hjälp med den här specifika aspekten av portning och dina alternativ för samverkan mellan de två modellerna kan du läsa Asynkron och interop mellan C++/WinRT och C++/CX.
Utvecklingsteam brukar rapportera att när de väl har tagit sig över tröskeln med att portera sin asynkrona kod, är återstoden av portningsarbetet till stor del mekaniskt.
Portning i ett steg
Om du har möjlighet att portera hela projektet i ett steg behöver du bara det här ämnet för all information du behöver (och du behöver inte de ämnen om interoperabilitet som följer efter detta). Vi rekommenderar att du börjar med att skapa ett nytt projekt i Visual Studio med någon av C++/WinRT-projektmallarna (se Visual Studio stöd för C++/WinRT). Flytta sedan över källkodsfilerna till det nya projektet och portera all C++/CX-källkod till C++/WinRT när du gör det.
Om du föredrar att utföra portningsarbetet i ditt befintliga C++/CX-projekt måste du också lägga till C++/WinRT-stöd till det. De steg som du följer för att göra som beskrivs i Ta ett C++/CX-projekt och lägga till C++/WinRT-stöd. När du är klar med porteringen har du förvandlat det som var ett rent C++/CX-projekt till ett rent C++/WinRT-projekt.
Note
Om du har ett Windows Runtime komponentprojekt är portning i ett enda pass ditt enda alternativ. Ett Windows Runtime komponentprojekt som skrivits i C++ måste innehålla antingen all C++/CX-källkod eller all C++/WinRT-källkod. De kan inte samexistera i den här projekttypen.
Portering av ett projekt gradvis
Med undantag för Windows Runtime komponentprojekt, som nämnts i föregående avsnitt, om storleken eller komplexiteten i din kodbas gör det nödvändigt att portera projektet gradvis, behöver du en portningsprocess där C++/CX- och C++/WinRT-kod finns sida vid sida i samma projekt. Utöver att läsa det här ämnet kan du också läsa Samverkan mellan C++/WinRT och C++/CX och Asynkronitet och samverkan mellan C++/WinRT och C++/CX. De här avsnitten innehåller information och kodexempel som visar hur du samverkar mellan de två språkprojektionerna.
För att förbereda ett projekt för en gradvis portningsprocess är ett alternativ att lägga till C++/WinRT-stöd i ditt C++/CX-projekt. De steg som du följer för att göra som beskrivs i Ta ett C++/CX-projekt och lägga till C++/WinRT-stöd. Du kan sedan gradvis migrera utifrån det.
Ett annat alternativ är att skapa ett nytt projekt i Visual Studio med någon av C++/WinRT-projektmallarna (se Visual Studio stöd för C++/WinRT). Lägg sedan till stöd för C++/CX i projektet. De steg som du följer för att göra beskrivs i Ta ett C++/WinRT-projekt och lägga till C++/CX-stöd. Du kan sedan börja flytta källkoden till den och portera en del av C++/CX-källkoden till C++/WinRT när du gör det.
I båda fallen samverkar du (åt båda hållen) mellan din C++/WinRT-kod och eventuell C++/CX-kod som du ännu inte har porterat.
Note
Både C++/CX och Windows SDK deklarerar typer i rotnamnområdet Windows. En Windows typ som projiceras i C++/WinRT har samma fullständigt kvalificerade namn som den Windows typen, men den placeras i namnområdet C++ winrt. Med dessa distinkta namnområden kan du portera från C++/CX till C++/WinRT i din egen takt.
Portera ett XAML-projekt gradvis
Viktigt!
För ett projekt som använder XAML måste alla dina XAML-sidtyper vid en viss tidpunkt antingen vara helt C++/CX eller helt C++/WinRT. Du kan fortfarande blanda C++/CX och C++/WinRT utanför XAML-sidtyper i samma projekt (i dina modeller och visningsmodeller och på andra ställen).
I det här scenariot rekommenderar vi att du skapar ett nytt C++/WinRT-projekt och kopierar källkod och markering från C++/CX-projektet. Så länge alla dina XAML-sidtyper är C++/WinRT kan du lägga till nya XAML-sidor med Project>Lägg till nytt objekt...>Visual C++>Tom sida (C++/WinRT).
Du kan också använda en Windows Runtime komponent (WRC) för att räkna ut kod från XAML C++/CX-projektet när du porterar den.
- Du kan skapa ett nytt C++/CX WRC-projekt, flytta så mycket C++/CX-kod som möjligt till projektet och sedan ändra XAML-projektet till C++/WinRT.
- Eller så kan du skapa ett nytt C++/WinRT WRC-projekt, lämna XAML-projektet som C++/CX och börja portera C++/CX till C++/WinRT och flytta den resulterande koden från XAML-projektet och in i komponentprojektet.
- Du kan också ha ett C++/CX-komponentprojekt tillsammans med ett C++/WinRT-komponentprojekt i samma lösning, referera till dem båda från ditt programprojekt och gradvis porta från det ena till det andra. Mer information om hur du använder de två språkprojektionerna i samma projekt finns i Interop mellan C++/WinRT och C++/CX .
De första stegen för att portera ett C++/CX-projekt till C++/WinRT
Oavsett vilken porteringsstrategi du väljer (portering i ett steg eller gradvis portering) är det första steget att förbereda projektet för att portera det. Här är en sammanfattning av vad vi beskrev i Strategier för portning när det gäller vilken typ av projekt du ska börja med och hur du konfigurerar det.
- Portering i ett steg. Skapa ett nytt projekt i Visual Studio med någon av C++/WinRT-projektmallarna. Flytta filerna från C++/CX-projektet till det nya projektet och portera C++/CX-källkoden.
- Portera ett icke-XAML-projekt gradvis. Du kan välja att lägga till C++/WinRT-stöd i ditt C++/CX-projekt (se Ta ett C++/CX-projekt och lägga till stöd för C++/WinRT) och porten gradvis. Eller så kan du välja att skapa ett nytt C++/WinRT-projekt och lägga till C++/CX-stöd till det (se Ta ett C++/WinRT-projekt och lägga till C++/CX-stöd), flytta filer över och porten gradvis.
- Portera ett XAML-projekt gradvis. Skapa ett nytt C++/WinRT-projekt, flytta över filer och portera gradvis. Vid varje given tidpunkt måste dina XAML-sidtyper vara antingen alla C++/WinRT eller alla C++/CX.
Resten av det här avsnittet gäller oavsett vilken portningsstrategi du väljer. Den innehåller en katalog med teknisk information som ingår i portning av källkod från C++/CX till C++/WinRT. Om du migrerar gradvis så vill du förmodligen också läsa Samverkan mellan C++/WinRT och C++/CX och Asynkronitet och samverkan mellan C++/WinRT och C++/CX.
Regler för namngivning av filer
XAML-markeringsfiler
| Filursprung | C++/CX | C++/WinRT |
|---|---|---|
| XAML-filer för utvecklare | MyPage.xaml MyPage.xaml.h MyPage.xaml.cpp |
MyPage.xaml MyPage.h MyPage.cpp MyPage.idl (se nedan) |
| Genererade XAML-filer | MyPage.xaml.g.h MyPage.xaml.g.hpp |
MyPage.xaml.g.h MyPage.xaml.g.hpp MyPage.g.h |
Observera att C++/WinRT tar bort .xaml från filnamnen *.h och *.cpp .
C++/WinRT lägger till ytterligare en utvecklarfil, Midl-filen (.idl). C++/CX återskapar filen internt och lägger till alla offentliga och skyddade medlemmar i den. I C++/WinRT lägger du till och skapar filen själv. Mer information, kodexempel och en genomgång av redigering av IDL finns i XAML-kontroller; bind till en C++/WinRT-egenskap.
Se även Dela upp runtime-klasser i Midl-filer (.idl)
Körningsklasser
C++/CX har inga begränsningar för namnen på huvudfilerna. Det är vanligt att placera flera körningsklassdefinitioner i en enda rubrikfil, särskilt för små klasser. Men C++/WinRT kräver att varje runtime-klass har en egen headerfil med samma namn som klassnamnet.
| C++/CX | C++/WinRT |
|---|---|
Common.href class A { ... }ref class B { ... } |
Common.idlruntimeclass A { ... }runtimeclass B { ... } |
A.hnamespace implements {struct A { ... };} |
|
B.hnamespace implements {struct B { ... };} |
Mindre vanligt (men fortfarande lagligt) i C++/CX är att använda rubrikfiler med olika namn för anpassade XAML-kontroller. Du måste byta namn på huvudfilen så att den matchar klassnamnet.
| C++/CX | C++/WinRT |
|---|---|
A.xaml<Page x:Class="LongNameForA" ...> |
A.xaml<Page x:Class="LongNameForA" ...> |
A.hpartial ref class LongNameForA { ... } |
LongNameForA.hnamespace implements {struct LongNameForA { ... };} |
Krav för rubrikfil
C++/CX kräver inte att du inkluderar några särskilda huvudfiler, eftersom det automatiskt skapar huvudfiler från .winmd filer internt. Det är vanligt i C++/CX att använda using-direktiv för namnområden som du använder vid namn.
using namespace Windows::Media::Playback;
String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
return item->VideoTracks->GetAt(0)->Name;
}
Med using namespace Windows::Media::Playback direktivet kan vi skriva MediaPlaybackItem utan ett namnområdesprefix. Vi berörde även namnområdet Windows.Media.Core, eftersom item->VideoTracks->GetAt(0) returnerar en Windows.Media.Core.VideoTrack. Men vi behövde inte skriva namnet VideoTrack någonstans, så vi behövde using Windows.Media.Core inget direktiv.
Men C++/WinRT kräver att du inkluderar en rubrikfil som motsvarar varje namnområde som du använder, även om du inte namnger den.
#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();
}
Å andra sidan, även om mediaPlaybackItem.AudioTracksChanged-händelsen är av typen TypedEventHandler<MediaPlaybackItem, Windows. Foundation.Collections.IVectorChangedEventArgs> behöver vi inte inkludera winrt/Windows.Foundation.Collections.h eftersom vi inte använde den händelsen.
C++/WinRT kräver också att du inkluderar huvudfiler för namnområden som används av XAML-markering.
<!-- MainPage.xaml -->
<Rectangle Height="400"/>
Om du använder rektangelklassen måste du lägga till den här inkluderingen.
// MainPage.h
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
Om du glömmer en headerfil kommer allt att kompilera utan problem, men du får länkningsfel eftersom klasserna consume_ saknas.
Parameteröverföring
När du skriver C++/CX-källkod, anger du C++/CX-typer som funktionsparametrar som referenser med hatttecken (^).
void LogPresenceRecord(PresenceRecord^ record);
För synkrona funktioner i C++/WinRT bör du använda const& parametrar som standard. Då undviks kopior och sammanflätade omkostnader. Men dina coroutines bör använda direktvärde för att säkerställa att de samlar in efter värde och undviker livstidsproblem (mer information finns i Samtidighet och asynkrona åtgärder med C++/WinRT).
void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);
Ett C++/WinRT-objekt är i grunden ett värde som innehar en gränssnittspekare till det underliggande Windows Runtime-objektet. När du kopierar ett C++/WinRT-objekt kopierar kompilatorn den inkapslade gränssnittspekaren och ökar dess referensantal. Eventuell destruktion av kopian innebär att referensantalet minskas. Det medför alltså bara kostnader för en kopia när det behövs.
Variabler och fältreferenser
När du skriver C++/CX-källkod använder du hattvariabler (^) för att referera till Windows Runtime-objekt och piloperatorn (->) för att avreferera en hattvariabel.
IVectorView<User^>^ userList = User::Users;
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
...
När du porterar till motsvarande C++/WinRT-kod kan du komma långt genom att ta bort hattarna och ändra piloperatorn (->) till punktoperatorn (.). C++/WinRT-projicerade typer är värden, inte pekare.
IVectorView<User> userList = User::Users();
if (userList != nullptr)
{
for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
...
Standardkonstruktorn för en C++/CX-hattreferens initierar den till null. Här är ett C++/CX-kodexempel där vi skapar en variabel/ett fält av rätt typ, men ett som inte är initierat. Med andra ord refererar den inte till en textblockering från början. vi tänker tilldela en referens senare.
TextBlock^ textBlock;
class MyClass
{
TextBlock^ textBlock;
};
Motsvarande i C++/WinRT finns i Fördröjd initiering.
Properties
Språktilläggen C++/CX innehåller begreppet egenskaper. När du skriver C++/CX-källkod kan du komma åt en egenskap som om det vore ett fält. Standard C++ har inte begreppet egenskap, så i C++/WinRT anropar du funktionerna hämta och ange.
I exemplen som följer är XboxUserId, UserState, PresenceDeviceRecords och Size alla egenskaper.
Hämta ett värde från en egenskap
Så här får du ett egenskapsvärde i C++/CX.
void Sample::LogPresenceRecord(PresenceRecord^ record)
{
auto id = record->XboxUserId;
auto state = record->UserState;
auto size = record->PresenceDeviceRecords->Size;
}
Motsvarande C++/WinRT-källkod anropar en funktion med samma namn som egenskapen, men utan parametrar.
void Sample::LogPresenceRecord(PresenceRecord const& record)
{
auto id = record.XboxUserId();
auto state = record.UserState();
auto size = record.PresenceDeviceRecords().Size();
}
Observera att funktionen PresenceDeviceRecords returnerar ett Windows Runtime objekt som i sig har en storleksfunktion. Eftersom det returnerade objektet också är en projicerad typ i C++/WinRT avrefererar vi det med hjälp av punktoperatorn för att anropa Size.
Ange en egenskap till ett nytt värde
Om du anger en egenskap till ett nytt värde följer ett liknande mönster. Först i C++/CX.
record->UserState = newValue;
Om du vill göra motsvarande i C++/WinRT anropar du en funktion med samma namn som egenskapen och skickar ett argument.
record.UserState(newValue);
Skapa en instans av en klass
Du arbetar med ett C++/CX-objekt via en referens till objektet, vanligen kallad en hattreferens (^). Du skapar ett nytt objekt via nyckelordet ref new , som i sin tur anropar RoActivateInstance för att aktivera en ny instans av körningsklassen.
using namespace Windows::Storage::Streams;
class Sample
{
private:
Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};
Ett C++/WinRT-objekt är ett värde. så att du kan allokera den på stacken eller som ett fält i ett objekt. Du använder ref new (eller new) för att allokera ett C++/WinRT-objekt.
RoActivateInstance anropas fortfarande i bakgrunden.
using namespace winrt::Windows::Storage::Streams;
struct Sample
{
private:
Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};
Om en resurs är dyr att initiera är det vanligt att fördröja initieringen av den tills den faktiskt behövs. Som redan nämnts initierar standardkonstruktorn för en C++/CX-hattreferens den till 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;
};
Samma kod som porterats till C++/WinRT. Observera användningen av konstruktorn std::nullptr_t . Mer information om konstruktorn finns i Fördröjd initiering.
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 };
};
Hur standardkonstruktorn påverkar samlingar
C++-samlingstyper använder standardkonstruktorn, vilket kan resultera i oavsiktlig objektkonstruktion.
| Scenario | C++/CX | C++/WinRT (felaktigt) | C++/WinRT (korrekt) |
|---|---|---|---|
| Lokal variabel, ursprungligen tom | TextBox^ textBox; |
TextBox textBox; // Creates a TextBox! |
TextBox textBox{ nullptr }; |
| Medlemsvariabel, ursprungligen tom | class C {TextBox^ textBox;}; |
class C {TextBox textBox; // Creates a TextBox!}; |
class C {TextBox textbox{ nullptr };}; |
| Global variabel, ursprungligen tom | TextBox^ g_textBox; |
TextBox g_textBox; // Creates a TextBox! |
TextBox g_textBox{ nullptr }; |
| Vektor för tomma referenser | std::vector<TextBox^> boxes(10); |
// Creates 10 TextBox objects!std::vector<TextBox> boxes(10); |
std::vector<TextBox> boxes(10, nullptr); |
| Ange ett värde i en karta | 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); |
| Matris med tomma referenser | TextBox^ boxes[2]; |
// Creates 2 TextBox objects!TextBox boxes[2]; |
TextBox boxes[2] = { nullptr, nullptr }; |
| Paira | std::pair<TextBox^, String^> p; |
// Creates a TextBox!std::pair<TextBox, String> p; |
std::pair<TextBox, String> p{ nullptr, nullptr }; |
Mer om samlingar med tomma referenser
När du har en plattform::Matris^ (se Portplattform::Matris^) i C++/CX kan du välja att portera den till en std::vector i C++/WinRT (i själva verket valfri sammanhängande container) i stället för att lämna den som en matris. Det finns fördelar med att välja std::vector.
Även om det till exempel finns en förkortning för att skapa en vektor med fast storlek med tomma referenser (se tabellen ovan), finns det ingen sådan förkortning för att skapa en matris med tomma referenser. Du måste upprepa nullptr för varje element i en matris. Om du har för få kommer extrafunktionerna att vara standardkonstruerade.
För en vektor kan du fylla den med tomma referenser vid initiering (som i tabellen ovan), eller så kan du fylla den med tomma referenser efter initiering med kod som den här.
std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.
Mer om exemplet std::map
Operatorn [] för std::map fungerar så här.
- Om nyckeln finns på kartan returnerar du en referens till det befintliga värdet (som du kan skriva över).
- Om nyckeln inte finns på kartan skapar du en ny post på kartan som består av nyckeln (flyttas, om den går att flytta) och ett standardkonstruerat värde och returnerar en referens till värdet (som du sedan kan skriva över).
Med andra ord skapar operatorn [] alltid en post i kartan. Detta skiljer sig från C#, Java och JavaScript.
Konvertering från en bas-körningsklass till en härledd klass
Det är vanligt att ha en basreferens som du vet refererar till ett objekt av en härledd typ. I C++/CX använder du dynamic_cast för att typkonvertera en referens till basklassen till en referens till den härledda klassen.
dynamic_cast är egentligen bara ett dolt anrop till QueryInterface. Här är ett typiskt exempel – du hanterar en händelse som ändrats av beroendeegenskapen och du vill återställa från DependencyObject till den faktiska typ som äger beroendeegenskapen.
void BgLabelControl::OnLabelChanged(Microsoft::UI::Xaml::DependencyObject^ d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };
if (theControl != nullptr)
{
// succeeded ...
}
}
Motsvarande C++/WinRT-kod ersätter dynamic_cast med ett anrop till funktionen IUnknown::try_as som kapslar in QueryInterface. Du kan också anropa IUnknown::as i stället, vilket genererar ett undantag om frågan för det nödvändiga gränssnittet (standardgränssnittet för den typ du begär) inte returneras. Här är ett C++/WinRT-kodexempel.
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 ...
}
}
Härledda klasser
För att kunna ärva från en runtimeklass måste basklassen vara komponerbar. C++/CX kräver inte att du vidtar några särskilda åtgärder för att göra dina klasser komponerbara, men C++/WinRT gör det. Du använder det oförseglade nyckelordet för att ange att du vill att din klass ska kunna användas som basklass.
unsealed runtimeclass BasePage : Microsoft.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
I implementeringshuvudklassen måste du inkludera huvudfilen för basklassen innan du tar med den automatiskt genererade rubriken för den härledda klassen. Annars får du fel som "Olaglig användning av den här typen som ett uttryck".
// 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>
{
...
}
}
Händelsehantering med ett ombud
Här är ett typiskt exempel på hur du hanterar en händelse i C++/CX med hjälp av en lambda-funktion som ombud i det här fallet.
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.
});
Detta är motsvarigheten i 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.
});
I stället för en lambda-funktion kan du välja att implementera din delegat som en fristående funktion eller som en pekare till en medlemsfunktion. Mer information finns i Hantera händelser med hjälp av ombud i C++/WinRT.
Om du porterar från en C++/CX-kodbas där händelser och delegater används internt (inte över binärgränser), hjälper winrt::delegate dig att återskapa det mönstret i C++/WinRT. Se även Parametriserade ombud, enkla signaler och återanrop i ett projekt.
Återkalla ett ombud
I C++/CX använder du operatorn -= för att återkalla en tidigare händelseregistrering.
myButton->Click -= token;
Detta är motsvarigheten i C++/WinRT.
myButton().Click(token);
Mer information och alternativ finns i Återkalla ett registrerat ombud.
Boxning och avboxning
C++/CX boxar automatiskt skalärer till objekt. C++/WinRT kräver att du anropar funktionen winrt::box_value explicit. Båda språken kräver att du packar upp explicit. Se Boxning och avboxning med C++/WinRT.
I tabellerna som följer använder vi dessa definitioner.
| C++/CX | C++/WinRT |
|---|---|
int i; |
int i; |
String^ s; |
winrt::hstring s; |
Object^ o; |
IInspectable o; |
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Boxning | o = 1;o = "string"; |
o = box_value(1);o = box_value(L"string"); |
| Öppna förpackningen | i = (int)o;s = (String^)o; |
i = unbox_value<int>(o);s = unbox_value<winrt::hstring>(o); |
C++/CX och C# utlöser undantag om du försöker avboxa en null-pekare till en värdetyp. C++/WinRT anser att detta är ett programmeringsfel och kraschar. I C++/WinRT använder du funktionen winrt::unbox_value_or om du vill hantera fallet där objektet inte är av den typ som du trodde att det var.
| Scenario | C++/CX | C++/WinRT |
|---|---|---|
| Avboxa ett känt heltal | i = (int)o; |
i = unbox_value<int>(o); |
| Om o är null | Platform::NullReferenceException |
Krasch |
| Om o inte är en boxad int | Platform::InvalidCastException |
Krasch |
| Avboxa int, använd reservvärde om null; krascha om det är något annat | i = o ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
| Avboxa int om möjligt; använd reservlösning för allt annat | auto box = dynamic_cast<IBox<int>^>(o);i = box ? box->Value : fallback; |
i = unbox_value_or<int>(o, fallback); |
Boxning och avboxning av en sträng
En sträng är på sätt och vis en värdetyp och på andra sätt en referenstyp. C++/CX och C++/WinRT behandlar strängar på olika sätt.
ABI-typen HSTRING är en pekare till en referensberäkningssträng. Men det härleds inte från IInspectable, så det är inte tekniskt ett objekt. Dessutom representerar en null HSTRING den tomma strängen. Boxning av saker som inte härleds från IInspectable görs genom att omsluta dem i en IReference<T>, och Windows Runtime tillhandahåller en standardimplementering i form av PropertyValue-objektet (anpassade typer rapporteras som PropertyType::OtherType).
C++/CX representerar en Windows Runtime sträng som en referenstyp, medan C++/WinRT projicerar en sträng som en värdetyp. Det innebär att en rutad null-sträng kan ha olika representationer beroende på hur du kom dit.
Dessutom kan du i C++/CX dereferera en null-String^, och då beter den sig som strängen "".
| Behavior | C++/CX | C++/WinRT |
|---|---|---|
| Deklarationer | Object^ o;String^ s; |
IInspectable o;hstring s; |
| Kategori för strängtyp | Referenstyp | Värdetyp |
| null HSTRING-projekt som | (String^)nullptr |
hstring{} |
Är null och "" identiska? |
Yes | Yes |
| Giltighet för null | s = nullptr;s->Length == 0 (giltigt) |
s = hstring{};s.size() == 0 (giltigt) |
| Om du tilldelar null-sträng till objekt | o = (String^)nullptr;o == nullptr |
o = box_value(hstring{});o != nullptr |
Om du tilldelar "" objektet |
o = "";o == nullptr |
o = box_value(hstring{L""});o != nullptr |
Grundläggande boxning och avboxning.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Boxa en sträng | o = s;Tom sträng blir nullptr. |
o = box_value(s);Tom sträng blir ett icke-null-objekt. |
| Packa upp en känd sträng | s = (String^)o;Null-objektet blir en tom sträng. InvalidCastException om det inte är en sträng. |
s = unbox_value<hstring>(o);Null-objekt kraschar. Kraschar om det inte är en sträng. |
| Packa upp en möjlig sträng | s = dynamic_cast<String^>(o);Null-objekt eller icke-sträng blir tom sträng. |
s = unbox_value_or<hstring>(o, fallback);Null eller ett värde som inte är en sträng ger ett återgångsvärde. Tom sträng bevarad. |
Samtidighet och asynkrona åtgärder
PPL (Parallel Patterns Library) (concurrency::task, till exempel) uppdaterades för att stödja C++/CX-hattreferenser.
För C++/WinRT bör du använda coroutines och co_await i stället. Mer information och kodexempel finns i Samtidighet och asynkrona åtgärder med C++/WinRT.
Använda objekt från XAML-markering
I ett C++/CX-projekt kan du använda privata medlemmar och namngivna element från XAML-markering. Men i C++/WinRT måste alla entiteter som används med XAML {x:Bind}-markeringstillägget exponeras offentligt i IDL.
Dessutom visar bindning till ett booleskt värde true eller false i C++/CX, men Windows.Foundation.IReference`1<Boolean> i C++/WinRT.
Mer information och kodexempel finns i Använda objekt från markup.
Mappa C++/ CX-plattformstyper till C++/WinRT-typer
C++/CX innehåller flera datatyper i plattformsnamnområdet . Dessa typer är inte standard-C++, så du kan bara använda dem när du aktiverar Windows Runtime språktillägg (Visual Studio projektegenskapen C/C++>Allmänt>Förbruka Windows Runtime Tillägg>Ja (/ZW)). Tabellen nedan hjälper dig att portera från plattformstyper till deras motsvarigheter i C++/WinRT. När du har gjort det kan du inaktivera /ZW alternativet eftersom C++/WinRT är standard C++.
| C++/CX | C++/WinRT |
|---|---|
| Plattform::Agile^ | winrt::agile_ref |
| Platform::Array^ | Se Port Platform::Array^ |
| Platform::Exception^ | winrt::hresult_error |
| Plattform::InvalidArgumentException^ | winrt::hresult_invalid_argument |
| Plattform::Objekt^ | winrt::Windows::Foundation::IInspectable |
| Platform::String^ | winrt::hstring |
Portera Platform::Agile^ till winrt::agile_ref
Typen Plattform::Agile^ i C++/CX representerar en Windows Runtime-klass som kan nås från valfri tråd. C++/WinRT-motsvarigheten är winrt::agile_ref.
I C++/CX.
Platform::Agile<Windows::UI::Core::CoreWindow> m_window;
I C++/WinRT (WinUI 3 använder Microsoft::UI::Xaml::Window i stället för CoreWindow).
winrt::agile_ref<Microsoft::UI::Xaml::Window> m_window;
Port Platform::Array^
I de fall där C++/CX kräver att du använder en matris kan du använda valfri sammanhängande container med C++/WinRT. Se Hur standardkonstruktorn påverkar samlingar av en anledning till att std::vector är ett bra val.
Så, när du har en Platform::Array^ i C++/CX kan du vid portering använda en initialiseringslista, en std::array eller en std::vector. Mer information och kodexempel finns i Standardinitieringslistor och Standardmatriser och vektorer.
Porta Platform::Exception^ till winrt::hresult_error
Typen Platform::Exception^ skapas i C++/CX när ett Windows Runtime API returnerar en icke-S_OK HRESULT. C++/WinRT-motsvarigheten är winrt::hresult_error.
Om du porterar till C++/WinRT, ändrar du all kod som använder Platform::Exception^ så att den använder winrt::hresult_error.
I C++/CX.
catch (Platform::Exception^ ex)
I C++/WinRT.
catch (winrt::hresult_error const& ex)
C++/WinRT tillhandahåller dessa undantagsklasser.
| Undantagstyp | Basklass | HRESULT |
|---|---|---|
| winrt::hresult_error | anropa hresult_error::to_abi | |
| winrt::hresult_access_denied | winrt::hresult_error | E_ACCESSDENIED |
| winrt::hresult_canceled | winrt::hresult_error | FEL_AVBRUTET |
| 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 |
Observera att varje klass (via hresult_error basklass) tillhandahåller en to_abi funktion som returnerar HRESULT för felet och en meddelandefunktion som returnerar strängrepresentationen av HRESULT.
Här är ett exempel på hur du utlöser ett undantag i C++/CX.
throw ref new Platform::InvalidArgumentException(L"A valid User is required");
Och motsvarande i C++/WinRT.
throw winrt::hresult_invalid_argument{ L"A valid User is required" };
Portera Platform::Object^ till winrt::Windows::Foundation::IInspectable
Precis som alla C++/WinRT-typer är winrt::Windows::Foundation::IInspectable en värdetyp. Så här initierar du en variabel av den typen till null.
winrt::Windows::Foundation::IInspectable var{ nullptr };
Portera Platform::String^ till winrt::hstring
Platform::String^ motsvarar ABI-typen HSTRING i Windows Runtime. För C++/WinRT är motsvarigheten winrt::hstring. Men med C++/WinRT kan du anropa Windows Runtime API:er med hjälp av breda strängtyper för C++-standardbiblioteket, till exempel std::wstring och/eller breda strängliteraler. Mer information och kodexempel finns i Stränghantering i C++/WinRT.
Med C++/CX kan du komma åt egenskapen Platform::String::D ata för att hämta strängen som en C-format const wchar_t* matris (till exempel för att skicka den till std::wcout).
auto var{ titleRecord->TitleName->Data() };
Om du vill göra samma sak med C++/WinRT kan du använda funktionen hstring::c_str för att hämta en null-avslutad C-strängversion, precis som du kan från std::wstring.
auto var{ titleRecord.TitleName().c_str() };
När det gäller att implementera API:er som tar eller returnerar strängar ändrar du vanligtvis valfri C++/CX-kod som använder Plattform::Sträng^ för att använda winrt::hstring i stället.
Här är ett exempel på ett C++/CX-API som tar en sträng.
void LogWrapLine(Platform::String^ str);
För C++/WinRT kan du deklarera api:et i MIDL 3.0 så här.
// LogType.idl
void LogWrapLine(String str);
C++/WinRT-verktygskedjan genererar sedan källkod för dig som ser ut så här.
void LogWrapLine(winrt::hstring const& str);
ToString()
C++/CX-typer tillhandahåller metoden Object::ToString .
int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".
C++/WinRT tillhandahåller inte den här funktionen direkt, men du kan vända dig till alternativ.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
C++/WinRT stöder också winrt::to_hstring för ett begränsat antal typer. Du måste lägga till överlagrade funktioner för alla de ytterligare typer som du vill konvertera till sträng.
| Language | Konvertera int till sträng | Konvertera enum till sträng |
|---|---|---|
| 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); |
Om du vill konvertera en enum till en sträng måste du tillhandahålla en implementering av 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));
}
}
}
Dessa strängrepresentationer används ofta implicit vid databindning.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Dessa bindningar kommer att använda winrt::to_hstring på den bundna egenskapen. När det gäller det andra exemplet ( StatusEnum) måste du ange din egen överbelastning av winrt::to_hstring, annars får du ett kompilatorfel.
Strängkonstruktion
C++/CX och C++/WinRT förlitar sig på standarden std::wstringstream för att bygga strängar.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Lägg till sträng, bevara null | stream.print(s->Data(), s->Length); |
stream << std::wstring_view{ s }; |
| Lägg till sträng, stanna vid första nolltecken | stream << s->Data(); |
stream << s.c_str(); |
| Extrahera resultat | ws = stream.str(); |
ws = stream.str(); |
Fler exempel
I exemplen nedan är ws en variabel av typen std::wstring. Även om C++/CX kan konstruera en plattform::Sträng från en 8-bitarssträng, gör inte C++/WinRT det.
| Operation | C++/CX | C++/WinRT |
|---|---|---|
| Skapa sträng från literal | String^ s = "hello";String^ s = L"hello"; |
// winrt::hstring s{ "hello" }; // Doesn't compilewinrt::hstring s{ L"hello" }; |
| Konvertera från std::wstring, bevara nulltecken | 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 |
| Konvertera från std::wstring, stoppa vid första nolltecken | 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 |
| Konvertera till std::wstring, bevara null-värden | std::wstring ws{ s->Data(), s->Length };ws = std::wstring(s>Data(), s->Length); |
std::wstring ws{ s };ws = s; |
| Konvertera till std::wstring, stoppa vid första nolltecken | std::wstring ws{ s->Data() };ws = s->Data(); |
std::wstring ws{ s.c_str() };ws = s.c_str(); |
| Skicka literal till en metod | Method("hello");Method(L"hello"); |
// Method("hello"); // Doesn't compileMethod(L"hello"); |
| Skicka std::wstring till metod | Method(ref new String(ws.c_str(),(uint32_t)ws.size()); // Stops on first null |
Method(ws);// param::winrt::hstring accepts std::wstring_view |
Viktiga API:er
Relaterade ämnen
Note
Många C++/WinRT-ämnen håller på att migreras från UWP-dokumentationen till det här avsnittet. Tills migreringen är klar kan länkar i listan nedan leda till avsnittet UWP-dokument. C++/WinRT-språkprojektionen är densamma för både UWP- och WinUI 3-appar, så att innehållet är tillämpligt i båda kontexterna. Alla UWP-specifika mönster (till exempel applivscykel eller Windows.UI namnområdes-API:er) anges uttryckligen i dessa artiklar.
- C++/CX
- Skapa händelser i C++/WinRT
- Samtidighet och asynkrona åtgärder med C++/WinRT
- Använda API:er med C++/WinRT
- Hantera händelser med hjälp av ombud i C++/WinRT
- Interop mellan C++/WinRT och C++/CX
- Asynkronitet och samverkan mellan C++/WinRT och C++/CX
- referens för Microsoft Interface Definition Language 3.0
- Flytta till C++/WinRT från WRL
- Stränghantering i C++/WinRT
Windows developer