Dela via


Flytta till C++/WinRT från C++/CX

Det här avsnittet är det första i en serie som beskriver hur du kan portera källkoden i ditt C++/CX- projekt till dess motsvarighet i C++/WinRT-.

Om ditt projekt också använder Windows Runtime C++-mallbibliotek (WRL)-typer, kan du läsa Flytta till C++/WinRT från WRL.

Strategier för portning

Det är värt att veta att portning från C++/CX till C++/WinRT i allmänhet är enkelt, med ett undantag för att flytta från Parallel Patterns Library (PPL) uppgifter till koroutiner. Modellerna är olika. Det finns ingen naturlig en-till-en-mappning från PPL-uppgifter till coroutines, och det finns inget enkelt sätt att mekaniskt överföra koden som fungerar i alla sammanhang. Om du vill ha hjälp med den här specifika aspekten av portning och dina alternativ för att samverka mellan de två modellerna, se Asynkronitet och interoperabilitet mellan C++/WinRT och C++/CX.

Utvecklingsteam rapporterar rutinmässigt att när de är över hindret med att porta sin asynkrona kod är resten av portningsarbetet till stor del mekaniskt.

Portering i ett steg

Om du kan portera hela projektet i ett enda pass behöver du bara det här avsnittet för den information du behöver (och du behöver inte interop avsnitt som följer). 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 du följer för att göra det beskrivs i Att 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.

Anmärkning

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. Förutom att läsa det här avsnittet kan du även läsa Interop mellan C++/WinRT och C++/CX samt Asynchrony och Interop 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 du följer för att göra det beskrivs i Att ta ett C++/CX-projekt och lägga till C++/WinRT-stöd. Du kan sedan porta gradvis därifrån.

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 detta beskrivs i följande steg: Ta ett C++/WinRT-projekt och lägg till C++/CX-stöd. Du kan sedan börja flytta källkoden till den och samtidigt porta vissa delar av C++/CX-källkoden till C++/WinRT.

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.

Anmärkning

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 Windows-typen, men den placeras i C++ winrt namnrymd. 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 av XAML-sidtyper i samma projekt (i dina modeller, vy-modeller och 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 en till en annan. 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 vad din portningsstrategi kommer att vara (portning i ett pass eller portning gradvis) är ditt första steg att förbereda projektet för portning. Här är en sammanfattning av vad vi beskrev i Strategier för att portera när det gäller vilken typ av projekt du ska börja med och hur du konfigurerar det.

  • Portering i ett pass. Skapa ett nytt projekt i Visual Studio med hjälp av en av C++/WinRT-projektmallarna. Flytta filerna från C++/CX-projektet till det nya projektet och portera C++/CX-källkoden.
  • Att porta gradvis ett icke-XAML-projekt. 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 C++/WinRT-stöd) 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 över filer och port gradvis.
  • att gradvis porta ett XAML-projekt. Skapa ett nytt C++/WinRT-projekt, flytta över filer och portera det 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 porterar gradvis vill du förmodligen också se Interop mellan C++/WinRT och C++/CX, Asynchrony samt interop mellan C++/WinRT och C++/CX.

Namngivningskonventioner för 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 författande av IDL finns i XAML-kontroller; binda till en C++/WinRT-egenskap.

Se även Faktorisera körningsklasser 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 körningsklass har en egen headerfil med namnet efter klassnamnet.

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

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.h
partial ref class LongNameForA { ... }
LongNameForA.h
namespace implements {
  struct LongNameForA { ... };
}

Krav för rubrikfil

C++/CX kräver inte att du inkluderar några särskilda huvudfiler, eftersom de internt automatiskt skapar huvudfiler från .winmd filer. Det är vanligt i C++/CX att använda using direktiven för namnområden som du refererar till med 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 ändrade även Windows.Media.Core namnrymd 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 inget using Windows.Media.Core 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 händelsen MediaPlaybackItem.AudioTracksChanged är av typen TypedEventHandler<MediaPlaybackItem, Windows.Foundation.Collections.IVectorChangedEventArgs>behöver vi inte inkludera winrt/Windows.Foundation.Collections.h eftersom vi inte använde 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 rektangel-klassen måste du lägga till den här inkluderingen.

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

Om du glömmer en rubrikfil kompileras allt okej, men du får länkfel eftersom consume_-klasserna saknas.

Parameteröverföring

När du skriver C++/CX-källkod skickar du C++/CX-typer som funktionsparametrar som hattreferenser (^).

void LogPresenceRecord(PresenceRecord^ record);

För synkrona funktioner i C++/WinRT bör du som standard använda const& parametrar. Då undviks kopior och sammanflätade omkostnader. Men dina coroutines bör använda värdeöverföring för att säkerställa att de fångar värden och undviker problem med objektens livslängd (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 innehåller en gränssnittspekare till det stödda 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 pilen (->) för att avrefereras 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 och 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 hänvisar den inte till en TextBlock-; vi avser att tilldela en referens senare.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

Motsvarande i C++/WinRT finns i Fördröjd initiering.

Egenskaper

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 följande exempel är XboxUserId, UserState, PresenceDeviceRecordsoch 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 funktion Storlek. Eftersom det returnerade objektet också är en C++/WinRT-projicerad typ, avrefererar vi med punktoperatorn för att anropa Storlek.

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 det, ofta kallad en ^-referens (hatt). 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 aldrig använda ref new (eller new) för att allokera ett C++/WinRT-objekt. I bakgrunden anropas RoActivateInstance fortfarande.

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.

Scenarium 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 };
Par 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 Platform::Array^ (se Port Platform::Array^) 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 av tomma referenser (se tabellen ovan), finns det ingen motsvarande förkortning för att skapa en array av 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 std::map exempel

Den [] indexeringsoperatorn för std::map beter sig på följande sätt.

  • 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ärdeoch 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.

Konvertera från en baskö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 man dynamic_cast för att omvandla en basreferens till en härledd referens. dynamic_cast är egentligen bara ett dolt anrop till QueryInterface. Ett typiskt exempel är att du hanterar en händelse för ändringen av en beroendeegenskap och du vill kasta från DependencyObject tillbaka till den faktiska typen som innehåller beroendeegenskapen.

void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject^ d, Windows::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 utlöser ett undantag om frågan om 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(Windows::UI::Xaml::DependencyObject const& d, Windows::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 härledas från en körningsklass 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 det gör C++/WinRT. Du använder nyckelordet oförseglat för att ange att du vill att klassen ska kunna användas som basklass.

unsealed runtimeclass BasePage : Windows.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-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 mellan binärfiler), kommer winrt::delegate att hjälpa dig att replikera 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 omsluter 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;
Verksamhet 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# genererar undantag om du försöker ta bort 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 det fall där objektet inte är av den typ som du trodde att det var.

Scenarium C++/CX C++/WinRT
Ta bort ett känt heltal i = (int)o; i = unbox_value<int>(o);
Om o är null Platform::NullReferenceException Krasch
Om o inte är en inkapslad int Platform::InvalidCastException Krasch
Avboxa int, använd övergångslösning om null; krascha om något annat i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Avboxa int om möjligt; använd reservmetod för 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 sett ett objekt. Dessutom representerar en null-HSTRING- den tomma strängen. Inkapsling av objekt som inte härrör från IInspectable utförs genom att omsluta dem i ett IReference<T>. 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 tillåter C++/CX dig att avreferera en null-String^, då den fungerar som strängen "".

Beteende 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? Ja Ja
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 "" till objekt o = "";
o == nullptr
o = box_value(hstring{L""});
o != nullptr

Grundläggande boxning och avboxning.

Verksamhet C++/CX C++/WinRT
Placera en sträng i en låda 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 den inte är en sträng.
s = unbox_value<hstring>(o);
Nullobjektet kraschar.
Kör i kras 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 icke-sträng blir reserv.
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.

Konsumera objekt från XAML-markup

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 hjälp av XAML-{x:Bind} markeringstillägget exponeras offentligt i IDL.

Bindning till ett Boolean-värde visar också true eller false i C++/CX, men det visar Windows.Foundation.IReference`1<Boolean> i C++/WinRT.

För mer information och kodexempel, se Konsumera objekt från markup.

Kartläggning av C++/CX -plattformens-typer till C++/WinRT-typer

C++/CX innehåller flera datatyper i namnområdet Platform. Dessa typer är inte standard-C++, så du kan bara använda dem när du aktiverar Språktillägg för Windows Runtime (Visual Studio-projektegenskap C/C++>Allmänt>Använda Windows Runtime-tillägg>Ja (/ZW)). Tabellen nedan hjälper dig att portera från Platform typer till deras motsvarigheter i C++/WinRT. När du har gjort det kan du inaktivera alternativet /ZW eftersom C++/WinRT är standard C++.

C++/CX C++/WinRT
Plattform::Agile^ winrt::agile_ref
Plattform::Matris^ Se Port Platform::Array^
Plattform::Undantag^ winrt::hresult_error
Plattform::InvalidArgumentException^ winrt::hresult_invalid_argument
Plattform::Objekt^ winrt::Windows::Foundation::IInspectable
Plattform::Sträng^ winrt::hstring

Portera Platform::Agile^ till winrt::agile_ref

Platform::Agile^ typ 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.

winrt::agile_ref<Windows::UI::Core::CoreWindow> m_window;

Portplattform::Matris^

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 inkluderar portningsalternativen att använda en initialiserarlista, en std::arrayeller en std::vector. Mer information och kodexempel finns i Standard-initieringslistor och standardmatriser och vektorer.

Port Platform::Exception^ för winrt::hresult_error

Platform::Exception^-typen skapas i C++/CX när ett Windows Runtime-API returnerar ett HRESULT som inte är S_OK. C++/WinRT-motsvarigheten är winrt::hresult_error.

Om du vill portera till C++/WinRT ändrar du all kod som använder Platform::Exception^ för att använda 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_AVBRYTET
winrt::hresult_changed_state winrt::hresult_error E_TILLSTÅND_FÖRÄNDRAT
winrt::hresult_class_not_available winrt::hresult_error CLASS_E_CLASSNOTAVAILABLE
winrt::hresult_illegal_delegate_assignment winrt::hresult_error E_OLAGLIG_DELEGAT_TILLDELNING
winrt::hresult_illegal_method_call winrt::hresult_error E_OLAGLIG_METODANROP
winrt::hresult_illegal_state_change winrt::hresult_error E_OGILTIG_STATÄNDRING
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 ett meddelande funktion, 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" };

Port 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 Windows Runtime HSTRING. 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++-standardbibliotek, till exempel std::wstringoch/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::Data för att hämta strängen som en C-stil const wchar_t* array (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 C++/CX-kod som använder Platform::String^ 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 att 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 överlagringar för alla ytterligare typer som du vill konvertera till strängar.

Språk Stringify int (konvertera ett heltal till en sträng) Stringify-uppräkning
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);

När det gäller att strängifiera en enum måste du ange implementeringen 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ängifieringar konsumeras ofta implicit av 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 utföra winrt::to_hstring för den bundna egenskapen. När det gäller det andra exemplet (StatusEnum) måste du ange en egen överlagring av winrt::to_hstring, annars får du ett kompilatorfel.

Strängkonstruering

C++/CX och C++/WinRT förlitar sig på standard-std::wstringstream för strängkonstruktion.

Verksamhet 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, avbryt vid första null 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-bitars sträng, gör inte C++/WinRT det.

Verksamhet C++/CX C++/WinRT
Skapa sträng från literal String^ s = "hello";
String^ s = L"hello";
// winrt::hstring s{ "hello" }; // Doesn't compile
winrt::hstring s{ L"hello" };
Konvertera från std::wstring, bevara nollvärden 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 på första nulltecken 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 noll std::wstring ws{ s->Data() };
ws = s->Data();
std::wstring ws{ s.c_str() };
ws = s.c_str();
Skicka literal till metod Method("hello");
Method(L"hello");
// Method("hello"); // Doesn't compile
Method(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