Ugrás a C++/WinRT fájlra a C++/CX fájlból

Ez a témakör az első olyan sorozatban, amely leírja, hogyan portozhatja a C++/CX projekt forráskódját a C++/WinRT megfelelőjéhez.

Ha a projekt Windows-futtatókörnyezet C++ sablontárat (WRL) is használ, tekintse meg az Áthelyezés a C++/WinRT fájlba a WRL-ből című témakört.

A portolás stratégiái

Érdemes tisztában lennie azzal, hogy a C++/CX-ről a C+++/WinRT-be történő portolás általában egyszerű, azzal a kivétellel, hogy a párhuzamos minták tárából (PPL) a coroutines-feladatokba kell áttérni. A modellek eltérőek. A PPL-feladatok és a korutinok között nincs természetes egy az egyhez megfeleltetés, és nincs egyszerű mód a kód olyan mechanikus átültetésére, amely minden esetben működik. Ha segítségre van szüksége a portolás ezen konkrét aspektusával kapcsolatban, valamint a két modell közötti együttműködés lehetőségeiről, olvassa el az Aszinkronizálást, valamint a C++/WinRT és a C++/CX közötti interopciót.

A fejlesztői csapatok rutinszerűen jelentik, hogy ha túllépik az aszinkron kód portolásának akadályát, a portálási munka fennmaradó része nagyrészt mechanikus.

Migrálás egy lépésben

Ha olyan helyzetben van, hogy a teljes projektet egyetlen lépésben tudja portolni, akkor csak erre a témakörre lesz szüksége a szükséges információkhoz (és nem lesz szüksége az ezt követő interoptémákra ). Javasoljuk, hogy először hozzon létre egy új projektet Visual Studio a C++/WinRT projektsablonok egyikével (lásd Visual Studio C++/WinRT támogatásával). Ezután vigye át a forráskódfájlokat az új projektbe, és ennek során az összes C++/CX forráskódot portolja C++/WinRT-re.

Másik lehetőségként, ha a meglévő C++/CX projektben szeretné elvégezni a portálási munkát, akkor C++/WinRT-támogatást kell hozzáadnia hozzá. Az ehhez szükséges lépéseket a Egy C++/CX-projekt átalakítása és a C++/WinRT támogatás hozzáadása című témakör ismerteti. Mire végzett a portolással, a tiszta C++/CX projekt egy tiszta C+++/WinRT-projektté vált.

Note

Ha Windows-futtatókörnyezet összetevőprojekttel rendelkezik, akkor az egyetlen lehetőség az egy lépésben történő portolás. A C++ nyelven írt Windows-futtatókörnyezet összetevőprojektnek tartalmaznia kell az összes C++/CX forráskódot, vagy az összes C++/WinRT-forráskódot. Ebben a projekttípusban nem létezhetnek együtt.

Projekt fokozatos átültetése

Az előző szakaszban említett Windows-futtatókörnyezet összetevőprojektek kivételével, ha a kódbázis mérete vagy összetettsége szükségessé teszi a projekt fokozatos portolását, akkor szüksége lesz egy portálási folyamatra, amelyben egy ideig a C++/CX és a C++/WinRT kód egymás mellett található ugyanabban a projektben. A témakör elolvasása mellett lásd még a C++/WinRT és C++/CX közötti együttműködés, valamint a Aszinkron működés, valamint a C++/WinRT és C++/CX közötti együttműködés. Ezek a témakörök olyan információkat és kód példákat nyújtanak, amelyek bemutatják, hogyan lehet együttműködni a két nyelvi kivetítés között.

Ha egy projektet fel szeretne készíteni a fokozatos portálási folyamatra, az egyik lehetőség a C++/WinRT-támogatás hozzáadása a C++/CX projekthez. Az ehhez szükséges lépéseket a C++/CX-projekt használata és a C++/WinRT-támogatás hozzáadása című témakör ismerteti. Ezután onnan fokozatosan portozhat.

Egy másik lehetőség egy új projekt létrehozása Visual Studio a C++/WinRT-projektsablonok egyikével (lásd Visual Studio C++/WinRT támogatásával). Ezután adja hozzá a C++/CX támogatást a projekthez. Az ehhez szükséges lépéseket a C++/WinRT-projekt készítése és a C++/CX támogatás hozzáadása című cikk ismerteti. Ezután elkezdheti áthelyezni oda a forráskódját, és közben a C++/CX forráskód egy részét átültetheti C++/WinRT-re.

Mindkét esetben (mindkét módon) együttműködik a C++/WinRT-kód és a még nem portolt C++/CX kód között.

Note

A C++/CX és a Windows SDK is deklarál típusokat a gyökérnévtérben Windows. A C++/WinRT-be vetített Windows típus teljes neve megegyezik a Windows típuséval, de a C++ winrt névtérben van elhelyezve. Ezek a különböző névterek lehetővé teszik, hogy a C++/CX-ről a C++/WinRT-be a saját tempójában portoljon.

XAML-projekt portolása fokozatosan

Fontos

Az XAML-t használó projektek esetében az összes XAML-oldaltípusnak teljes egészében C++/CX vagy teljes egészében C++/WinRT típusúnak kell lennie. A C++/CX és a C++/WinRT egyazon projekten belül (a modellekben és a nézetmodellekben, illetve máshol) az XAML-oldaltípusokon kívül is keverhető.

Ebben a forgatókönyvben azt javasoljuk, hogy hozzon létre egy új C++/WinRT-projektet, és másolja át a forráskódot és a korrektúrát a C++/CX projektből. Ha az összes XAML-laptípus C++/WinRT, akkor új XAML-lapokat adhat hozzá Project>Új elem hozzáadása...>Visual C++>Üres oldal (C++/WinRT).

Alternatív megoldásként használhat egy Windows-futtatókörnyezet-összetevőt (WRC) a kód XAML C++/CX-projektből való kiemelésére a portolás során.

  • Létrehozhat egy új C++/CX WRC-projektet, áthelyezhet annyi C++/CX-kódot, amennyit csak tud, majd az XAML-projektet C++/WinRT-re módosíthatja.
  • Létrehozhat egy új C++/WinRT WRC-projektet, hagyhatja az XAML-projektet C++/CX néven, és megkezdheti a C++/CX portolását a C++/WinRT-be, és áthelyezheti az eredményül kapott kódot az XAML-projektből és az összetevőprojektbe.
  • C++/CX-összetevőprojekt is lehet egy C++/WinRT-összetevőprojekt mellett ugyanabban a megoldásban, mindkettőre hivatkozhat az alkalmazásprojektből, és fokozatosan portozhat az egyikről a másikra. A C++/WinRT és a C++/CX közötti együttműködés című témakörben további információt talál a két nyelvi kivetítés ugyanazon projektben való használatáról.

C++/CX-projekt C++/WinRT-be történő átvitelének első lépései

Függetlenül attól, hogy mi lesz a portálási stratégia (a portolás egy lépésben vagy fokozatosan történik), az első lépés a projekt előkészítése a portoláshoz. Íme egy összefoglaló arról, amit a Stratégiák a portoláshoz című részben leírtunk azzal kapcsolatban, hogy milyen típusú projekttel fog kezdeni, és hogyan kell azt beállítani.

  • Átültetés egyetlen menetben. Hozzon létre egy új projektet Visual Studio a C++/WinRT projektsablonok egyikével. Helyezze át a fájlokat a C++/CX projektből az új projektbe, és portozza a C++/CX forráskódot.
  • Egy nem XAML-alapú projekt fokozatos portolása. Dönthet úgy, hogy C++/WinRT-támogatást ad a C++/CX-projektjéhez (lásd: Egy C++/CX-projekt átalakítása C++/WinRT-támogatás hozzáadásával), majd fokozatosan átállhat. Vagy létrehozhat egy új C++/WinRT-projektet, és hozzáadhat ehhez C++/CX támogatást (lásd: C++/WinRT-projekt készítése és C++/CX-támogatás hozzáadása), fájlok áthelyezése és portok fokozatos áthelyezése.
  • XAML-projekt portolása fokozatosan. Hozzon létre egy új C++/WinRT-projektet, helyezze át a fájlokat és a portot fokozatosan. Az XAML-oldaltípusoknak adott időpontban vagy mind C++/WinRT-, vagy mind C++/CX-típusúnak kell lenniük.

A témakör többi része a választott portálási stratégiától függetlenül érvényes. A forráskód C+++/CX-ről C+++/WinRT-be történő portolásával kapcsolatos technikai részletek katalógusát tartalmazza. Ha fokozatosan állsz át, akkor valószínűleg ezeket is érdemes megnézned: Interoperabilitás a C++/WinRT és a C++/CX között és Aszinkronitás, valamint interoperabilitás a C++/WinRT és a C++/CX között.

Fájlelnevezési szabályok

XAML-jelölőfájlok

Fájl eredete C++/CX C++/WinRT
Fejlesztői XAML-fájlok MyPage.xaml
MyPage.xaml.h
MyPage.xaml.cpp
MyPage.xaml
MyPage.h
MyPage.cpp
MyPage.idl (lásd alább)
Létrehozott XAML-fájlok MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.g.h

Figyelje meg, hogy a C++/WinRT eltávolítja a(z) .xaml elemet a(z) *.h és *.cpp fájlnevekből.

A C++/WinRT hozzáad egy további fejlesztői fájlt, a Midl-fájlt (.idl). A C++/CX automatikusan létrehozza a fájlt belsőleg, és hozzáadja az összes nyilvános és védett taghoz. C++/WinRT-ben a fájlt ön adja hozzá és írja meg. További részletekért, kódpéldákért és az IDL szerkesztésének útmutatójáért lásd: XAML-vezérlők; kötés egy C++/WinRT tulajdonsághoz.

Lásd még: A futtatókörnyezeti osztályok MIDL-fájlokba (.idl) szervezése

Futásidejű osztályok

A C++/CX nem korlátozza a fejlécfájlok nevét; gyakori, hogy több futtatókörnyezeti osztálydefiníciót helyez el egyetlen fejlécfájlban, különösen a kis osztályok esetében. A C++/WinRT használatához azonban minden futtatókörnyezeti osztálynak saját fejlécfájlt kell elneveznie az osztály neve után.

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

A C++/CX-ben kevésbé gyakori (de még mindig legális) az, ha eltérő nevű fejlécfájlokat használ az XAML egyéni vezérlőihez. Az osztálynévnek megfelelően át kell neveznie ezeket a fejlécfájlokat.

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

Fejlécfájlra vonatkozó követelmények

A C++/CX nem igényel speciális fejlécfájlokat, mert belsőleg automatikusan generálja a fejlécfájlokat a fájlokból .winmd . A C++/CX-ben gyakori, hogy a név szerint használt névterekhez using direktívákat használnak.

using namespace Windows::Media::Playback;

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

Az using namespace Windows::Media::Playback irányelv lehetővé teszi, hogy névtér-előtag nélkül írjunk MediaPlaybackItem . A Windows.Media.Core névteret is érintettük, mert a item->VideoTracks->GetAt(0) egy Windows.Media.Core.VideoTrack típust ad vissza. De a VideoTrack nevet sehol nem kellett begépelnünk, ezért nem volt szükségünk irányelvre using Windows.Media.Core .

A C++/WinRT esetében azonban minden használt névtérhez be kell illesztenie a hozzá tartozó fejlécfájlt, még akkor is, ha a névteret nem nevezi meg.

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

Másrészt, annak ellenére, hogy a MediaPlaybackItem.AudioTracksChanged esemény typedEventHandler<MediaPlaybackItem típusú, Windows. Foundation.Collections.IVectorChangedEventArgs>, nem kell belefoglalnunkwinrt/Windows.Foundation.Collections.h, mert nem használtuk ezt az eseményt.

A C++/WinRT azt is megköveteli, hogy befoglalja a XAML-jelölőnyelv által használt névterek fejlécfájljait.

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

A Rectangle osztály használata azt jelenti, hogy fel kell vennie ezt az include-ot.

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

Ha elfelejt egy fejlécfájlt, akkor minden rendben lesz lefordítva, de linkerhibákat fog kapni, mert az consume_ osztályok hiányoznak.

Paraméterátadás

A C++/CX forráskód írása során a C++/CX típusokat függvényparaméterként hat (^) hivatkozásként adja át.

void LogPresenceRecord(PresenceRecord^ record);

A C++/WinRT-ben a szinkron függvényekhez alapértelmezés szerint paramétereket kell használnia const& . Így elkerülhetők a másolatok és az összekapcsolt többletterhelések. A korutinoknak azonban érték szerinti átadást kell használniuk, hogy biztosan érték szerint rögzítsenek, és elkerüljék az élettartamproblémákat (további részletekért lásd: Egyidejűség és aszinkron műveletek a C++/WinRT használatával).

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

A C++/WinRT objektum alapvetően olyan érték, amely a háttérrendszeri Windows-futtatókörnyezet objektumra mutató illesztőmutatót tartalmaz. C++/WinRT-objektum másolásakor a fordító átmásolja a beágyazott felület mutatóját, növelve annak referenciaszámát. A másolat végleges megsemmisítése magában foglalja a hivatkozás számának csökkenését. Tehát csak akkor merül fel a másolatok többletterhelése, ha szükséges.

Változó- és mezőhivatkozások

C++/CX forráskód írásakor a kalap (^) változókat használja a Windows-futtatókörnyezet-objektumokra való hivatkozáshoz, a nyíl (->) operátort pedig egy kalapváltozó dereferálásához.

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

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

Ha a megfelelő C++/WinRT-kódhoz portol, a kalapok eltávolításával és a nyíl operátor (->) pont operátorra (.) való módosításával hosszú utat tehet meg. A C++/WinRT előre jelzett típusok értékek, és nem mutatók.

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

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

A C++/CX „hat” referencia alapértelmezett konstruktora nullára inicializálja azt. Íme egy C++/CX-kódpéldát, amelyben létrehozunk egy megfelelő típusú változót/mezőt, de egy nem inicializáltat. Más szóval, kezdetben nem hivatkozik TextBlock-ra; később referenciát kívánunk hozzárendelni.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

A C++/WinRT megfelelőjéért lásd a késleltetett inicializálást.

Tulajdonságok

A C++/CX nyelvi bővítmények tartalmazzák a tulajdonságok fogalmát. C++/CX forráskód írásakor úgy érhet el egy tulajdonságot, mintha mező lenne. A standard C++ nem rendelkezik a tulajdonság fogalmával, ezért a C++/WinRT-ben a get és a set függvényeket hívhatja meg.

Az alábbi példákban az XboxUserId, a UserState, a PresenceDeviceRecords és a Size mind tulajdonságok.

Érték beolvasása egy tulajdonságból

A következő módon szerezhet be egy tulajdonságértéket a C++/CX fájlban.

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

Az ezzel egyenértékű C++/WinRT-forráskód egy olyan függvényt hív meg, amelynek neve megegyezik a tulajdonsággal, paraméterek nélkül.

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

Vegye figyelembe, hogy a PresenceDeviceRecords függvény egy Windows-futtatókörnyezet objektumot ad vissza, amely maga is rendelkezik Méret függvénnyel. Mivel a visszaadott objektum szintén C++/WinRT előrejelzett típus, a pont operátorral elhalasztjuk a Méret meghívását.

Tulajdonság beállítása új értékre

Egy tulajdonság új értékre állítása hasonló mintát követ. Először is C++/CX formátumban.

record->UserState = newValue;

A C++/WinRT függvény megfelelőjének eléréséhez meghívhat egy olyan függvényt, amelynek a neve megegyezik a tulajdonsággal, és egy argumentumot ad át.

record.UserState(newValue);

Osztálypéldány létrehozása

Egy C++/CX objektummal egy rá mutató hivatkozáson keresztül dolgozik, amelyet általában kalap (^) referenciának neveznek. A kulcsszón keresztül új objektumot hoz létre, amely viszont meghívja a ref newRoActivateInstance-et a futtatókörnyezeti osztály új példányának aktiválásához.

using namespace Windows::Storage::Streams;

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

A C++/WinRT-objektum értéktípusú, így elhelyezhető a veremen, illetve egy objektum mezőjeként is. C++/WinRT-objektum lefoglalására soha nem használ ref new (és new sem). A színfalak mögött a RoActivateInstance még mindig hívható.

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

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

Ha egy erőforrás inicializálása költséges, akkor gyakran késlelteti az inicializálását, amíg ténylegesen szükség nem lesz rá. Ahogy már említettük, a C++/CX hat referencia alapértelmezett konstruktora null értékűre inicializálja.

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

Ugyanaz a C++/WinRT-re átültetett kód. Figyelje meg az std::nullptr_t konstruktor használatát. A konstruktorról további információt a Késleltetett inicializálás című témakörben talál.

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

Az alapértelmezett konstruktor hatása a gyűjteményekre

A C++ gyűjteménytípusok az alapértelmezett konstruktort használják, ami nem szándékos objektumépítést eredményezhet.

Scenario C++/CX C++/WinRT (helytelen) C++/WinRT (helyes)
Helyi változó, kezdetben üres TextBox^ textBox; TextBox textBox; // Creates a TextBox! TextBox textBox{ nullptr };
Tagváltozó, kezdetben üres class C {
  TextBox^ textBox;
};
class C {
  TextBox textBox; // Creates a TextBox!
};
class C {
  TextBox textbox{ nullptr };
};
Kezdetben üres globális változó TextBox^ g_textBox; TextBox g_textBox; // Creates a TextBox! TextBox g_textBox{ nullptr };
Üres hivatkozások vektora std::vector<TextBox^> boxes(10); // Creates 10 TextBox objects!
std::vector<TextBox> boxes(10);
std::vector<TextBox> boxes(10, nullptr);
Érték beállítása térképen 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);
Üres hivatkozások tömbje TextBox^ boxes[2]; // Creates 2 TextBox objects!
TextBox boxes[2];
TextBox boxes[2] = { nullptr, nullptr };
Párosítás std::pair<TextBox^, String^> p; // Creates a TextBox!
std::pair<TextBox, String> p;
std::pair<TextBox, String> p{ nullptr, nullptr };

További információ az üres hivatkozások gyűjteményeiről

Amikor a C++/CX-ben egy Platform::Array^ típust használ (lásd: Port Platform::Array^), dönthet úgy, hogy azt a C++/WinRT-ben std::vector-ra (sőt, valójában bármilyen összefüggő tárolóra) ülteti át ahelyett, hogy tömbként hagyná meg. Az std::vector kiválasztása számos előnnyel jár.

Ha például az üres hivatkozások rögzített méretű vektorának létrehozására van rövidítés (lásd a fenti táblázatot), nincs ilyen rövidítés üres hivatkozások tömbjének létrehozásához. A(z) nullptr elemet meg kell ismételni a tömb minden eleméhez. Ha túl kevés, akkor az extrák alapértelmezés szerint létrejönnek.

Vektorok esetében üres hivatkozásokkal is kitöltheti az inicializáláskor (a fenti táblázathoz hasonlóan), vagy az inicializálás után üres hivatkozásokkal is kitöltheti az ilyen kóddal.

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

További információ az std::map példáról

Az []std::map alsó index operátora így viselkedik.

  • Ha a kulcs megtalálható a térképen, adjon vissza egy hivatkozást a meglévő értékre (amelyet felülírhat).
  • Ha a kulcs nem található a térképen, hozzon létre egy új bejegyzést a térképen, amely a kulcsból (áthelyezve, ha mozgatható) és egy alapértelmezetten létrehozott értékből áll, és visszaadja az értékre mutató hivatkozást (amelyet felülírhat).

Más szóval az [] operátor mindig létrehoz egy bejegyzést a térképen. Ez eltér a C#, a Java és a JavaScripttől.

Konvertálás alap futtatókörnyezeti osztályból származtatott osztálysá

Gyakran előfordul, hogy egy hivatkozott alap egy származtatott típusú objektumra hivatkozik. A C++/CX-ben a dynamic_cast használatával a bázisosztályra mutató referenciát származtatott osztályra mutató referenciává alakíthatja. Ez dynamic_cast valójában csak egy rejtett hívás a QueryInterface-ra. Íme egy tipikus példa : egy függőségi tulajdonság módosított eseményét kezeli, és vissza szeretné adni a DependencyObjectből a függőségi tulajdonságot birtokba venni kívánt tényleges típusra.

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

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

Az ezzel egyenértékű C++/WinRT-kód az dynamic_castIUnknown::try_as függvény hívását váltja fel, amely tartalmazza a QueryInterface függvényt. Lehetősége van arra is, hogy meghívja az IUnknown::as parancsot, amely kivételt eredményez, ha a szükséges felület lekérdezése (a kért típus alapértelmezett felülete) nem lesz visszaadva. Íme egy C++/WinRT-kód példa.

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

Származtatott osztályok

A futtatókörnyezeti osztályból való levezetéshez az alaposztálynak összeállíthatónak kell lennie. A C++/CX nem követeli meg, hogy speciális lépéseket tegyen az osztályok összeállításához, de a C++/WinRT igen. Az unsealed kulcsszó arra szolgál, hogy jelezze, hogy az osztály alaposztályként használható.

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

A implementálási fejlécosztályban az alaposztály fejlécfájlját kell tartalmaznia, mielőtt belefoglalja a származtatott osztály automatikusan létrehozott fejlécét. Ellenkező esetben olyan hibaüzenetek jelennek meg, mint például "Az ilyen típusú kifejezések tiltott használata".

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

Eseménykezelés meghatalmazottal

Íme egy tipikus példa egy C++/CX-esemény kezelésére, amely ebben az esetben egy lambda függvényt használ meghatalmazottként.

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

Ez a C++/WinRT megfelelője.

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

Lambda-függvény helyett dönthet úgy, hogy a delegáltat szabad függvényként vagy mutató–tag függvényként valósítja meg. További információ: Események kezelése meghatalmazottak használatával a C++/WinRT-ben.

Ha olyan C++/CX kódbázisból portoz, ahol az eseményeket és a meghatalmazottakat belsőleg használják (nem bináris fájlok között), akkor a winrt::d elegate segítségével replikálhatja ezt a mintát a C++/WinRT fájlban. Lásd még a projekten belüli paraméteres meghatalmazottakat, egyszerű jeleket és visszahívásokat.

Meghatalmazott visszavonása

A C++/CX-ben az operátorral -= visszavonhat egy korábbi eseményregisztrációt.

myButton->Click -= token;

Ez a C++/WinRT megfelelője.

myButton().Click(token);

További információkért és lehetőségekért lásd: Regisztrált meghatalmazott visszavonása.

Dobozolás és kicsomagolás

A C++/CX automatikusan objektumokká csomagolja be a skaláris értékeket. A C++/WinRT megköveteli a winrt::box_value függvény explicit meghívását. Mindkét nyelvben expliciten kell kicsomagolni. Lásd: Boxolás és kicsomagolás a C++/WinRT használatával.

Az alábbi táblázatokban ezeket a definíciókat fogjuk használni.

C++/CX C++/WinRT
int i; int i;
String^ s; winrt::hstring s;
Object^ o; IInspectable o;
Operation C++/CX C++/WinRT
Ökölvívás o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Kicsomagolás i = (int)o;
s = (String^)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

A C++/CX és a C# kivételt vált ki, ha nullértékű mutatót próbál értéktípussá kibontani. A C++/WinRT ezt programozási hibának tekinti, és összeomlik. A C++/WinRT-ben használja a winrt::unbox_value_or függvényt, ha azt az esetet szeretné kezelni, amelyben az objektum nem az ön által vélt típus.

Scenario C++/CX C++/WinRT
Ismert egész szám kicsomagolása i = (int)o; i = unbox_value<int>(o);
Ha o null Platform::NullReferenceException Lezuhan
Ha o nem dobozos int Platform::InvalidCastException Lezuhan
Az int kicsomagolása, null érték esetén tartalékérték használata; minden más esetben összeomlás i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Ha lehetséges, csomagolja ki az intet; minden más esetben használjon tartalék megoldást auto box = dynamic_cast<IBox<int>^>(o);
i = box ? box->Value : fallback;
i = unbox_value_or<int>(o, fallback);

Sztring dobozolása és kicsomagolása

A sztringek valamilyen módon értéktípust, más módon pedig referenciatípust jelentek. A C++/CX és a C++/WinRT eltérően kezeli a sztringeket.

Az ABI típusú HSTRING egy referenciaszámlált karakterláncra mutató mutató. De nem az IInspectable-ból származik, tehát technikailag nem objektum. Ezenkívül a null HSTRING az üres sztringet jelöli. Az IInspectable-ból nem származó dolgok dobozolása úgy történik, hogy egy IReference<T-be> burkolja őket, és a Windows-futtatókörnyezet egy standard implementációt biztosít a PropertyValue objektum formájában (az egyéni típusok tulajdonságtípusként vannak jelentve::OtherType).

A C++/CX egy Windows-futtatókörnyezet sztringet jelöl referenciatípusként, míg a C++/WinRT egy sztringet értéktípusként. Ez azt jelenti, hogy egy becsomagolt null sztring attól függően különbözőképpen ábrázolható, hogy hogyan jutottunk el idáig.

Ezenkívül a C++/CX lehetővé teszi egy null String^ dereferálását; ilyenkor úgy viselkedik, mint a "" üres karakterlánc.

Behavior C++/CX C++/WinRT
Nyilatkozatok Object^ o;
String^ s;
IInspectable o;
hstring s;
Sztringtípus kategóriája Hivatkozás típusa Érték típusa
null HSTRING formában jelenik meg (String^)nullptr hstring{}
Null és "" azonos? Yes Yes
Null érték érvényessége s = nullptr;
s->Length == 0 (érvényes)
s = hstring{};
s.size() == 0 (érvényes)
Ha null sztringet rendel az objektumhoz o = (String^)nullptr;
o == nullptr
o = box_value(hstring{});
o != nullptr
Ha objektumhoz rendel "" o = "";
o == nullptr
o = box_value(hstring{L""});
o != nullptr

Alapszintű ökölvívás és kicsomagolás.

Operation C++/CX C++/WinRT
Karakterlánc becsomagolása o = s;
Az üres sztring nullptr lesz.
o = box_value(s);
Az üres sztring nem null értékű objektummá válik.
Ismert karakterlánc kibontása s = (String^)o;
A null objektum üres sztring lesz.
InvalidCastException, ha az érték nem karakterlánc.
s = unbox_value<hstring>(o);
Null objektum összeomlik.
Összeomlik, ha nem karakterlánc.
Opcionális karakterlánc kibontása s = dynamic_cast<String^>(o);
A null érték vagy a nem karakterlánc üres karakterlánccá válik.
s = unbox_value_or<hstring>(o, fallback);
A null vagy nem karakterlánc típusú érték helyett az alapértelmezett érték lesz használva.
Üres sztring megőrzve.

Egyidejűség és aszinkron műveletek

A párhuzamos minták függvénytárát (PPL) (például a concurrency::task) frissítették, hogy támogassa a C++/CX kalap operátoros hivatkozásait.

C++/WinRT esetén ehelyett korutinokat és co_await kell használnia. További információkért és kódpéldákért lásd a C++/WinRT egyidejűségi és aszinkron műveleteit.

Objektumok használata XAML-jelölőnyelvből

Egy C++/CX-projektben a privát tagokat és az elnevezett elemeket is elérheti az XAML-jelölőnyelvből. A C++/WinRT rendszerben azonban az XAML {x:Bind} jelölőbővítmény használatával felhasznált összes entitást nyilvánosan közzé kell tenni az IDL-ben.

Emellett logikai értékhez kötéskor a C++/CX rendszerben true vagy false jelenik meg, a C++/WinRT rendszerben pedig Windows.Foundation.IReference`1<Boolean>.

További információkért és kódpéldákért lásd Objektumok felhasználása jelölőnyelvből.

C++/CX platformtípusok leképezése C++/WinRT-típusokhoz

A C++/CX számos adattípust biztosít a Platform névterében. Ezek a típusok nem szabványos C++, ezért csak akkor használhatja őket, ha engedélyezi Windows-futtatókörnyezet nyelvi bővítményeket (Visual Studio C/C++>General>Consume Windows-futtatókörnyezet Extension>Yes (/ZW) projekttulajdonság). Az alábbi táblázat segít a platformtípusokról a C++/WinRT-beli megfelelőikre történő portálásban. Ha ezt megtette, mivel a C++/WinRT standard C++, kikapcsolhatja a /ZW beállítást.

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

A(z) Platform::Agile^ átültetése erre: winrt::agile_ref

A Platform::Agile^ típus a C++/CX fájlban egy Windows-futtatókörnyezet osztályt jelöl, amely bármely szálról elérhető. A C++/WinRT-ekvivalens a winrt::agile_ref.

C++/CX formátumban.

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

A C++/WinRT-ben a WinUI 3 a Microsoft::UI::Xaml::Window típust használja a CoreWindow helyett.

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

Port Platform::Array^

Olyan esetekben, amikor a C++/CX tömb használatát igényli, a C++/WinRT lehetővé teszi az egybefüggő tárolók használatát. Tekintse meg , hogyan befolyásolja az alapértelmezett konstruktor a gyűjteményeket , mert az std::vector jó választás.

Tehát amikor C++/CX-ben van egy Platform::Array^ elemed, a portolási lehetőségek közé tartozik az inicializálólista, a std::array vagy a std::vector használata. További információ és példakód: Standard inicializáló listák , Standard tömbök és vektorok.

Portold a(z) Platform::Exception^ elemet erre: winrt::hresult_error

A platform::Exception^ típus akkor jön létre a C++/CX fájlban, ha egy Windows-futtatókörnyezet API nem S_OK HRESULT-ot ad vissza. A C++/WinRT megfelelője a winrt::hresult_error.

A C++/WinRT-re való átültetéshez módosítson minden, Platform::Exception^-t használó kódot úgy, hogy winrt::hresult_error-t használjon.

C++/CX formátumban.

catch (Platform::Exception^ ex)

C++/WinRT nyelven.

catch (winrt::hresult_error const& ex)

A C++/WinRT ezeket a kivételosztályokat biztosítja.

Kivétel típusa Alaposztály HRESULT
winrt::hresult_error hívja meg a hresult_error::to_abi függvényt
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

Vegye figyelembe, hogy minden osztály (a hresult_error alaposztályon keresztül) egy to_abi függvényt biztosít, amely a hiba HRESULT-ját adja vissza, valamint egy üzenetfüggvényt, amely az adott HRESULT sztring-ábrázolását adja vissza.

Íme egy példa egy kivétel C++/CX-ben való kivetésére.

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

És ennek megfelelője a C++/WinRT.

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

Platform::Object^ portolása ide: winrt::Windows::Foundation::IInspectable

Az összes C++/WinRT típushoz hasonlóan a winrt::Windows::Foundation::IInspectable értéktípus. Így inicializálhat egy ilyen típusú változót null értékre.

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

Portáld át a Platform::String^ típust erre: winrt::hstring

Platform::A karakterlánc^ egyenértékű a Windows-futtatókörnyezet HSTRING ABI típussal. A C++/WinRT esetén ennek megfelelője a winrt::hstring. A C++/WinRT használatával azonban meghívhatja Windows-futtatókörnyezet API-kat a C++ Standard Könyvtár széles sztringtípusaival, például std::wstring és/vagy széles sztringkonstansok használatával. További részletekért és kódpéldákért lásd a C++/WinRT sztringkezelését.

A C++/CX használatával elérheti a Platform::String::Data tulajdonságot, hogy a karakterláncot C stílusú const wchar_t* karaktertömbként lekérje (például az std::wcout-nak való átadáshoz).

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

Ugyanezt C++/WinRT használatával is megteheti: a hstring::c_str függvénnyel lekérheti a nullával lezárt, C stílusú karakterlánc-változatot, ahogyan a std::wstring esetében is.

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

Amikor karakterláncokat fogadó vagy visszaadó API-k implementálásáról van szó, általában minden olyan C++/CX-kódot módosítani szokott, amely Platform::String^-t használ, hogy helyette winrt::hstring-et használjon.

Íme egy példa egy sztringet használó C++/CX API-ra.

void LogWrapLine(Platform::String^ str);

A C++/WinRT esetében az API-t a MIDL 3.0-ban így deklarálhatja.

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

A C++/WinRT eszközlánc ezután létrehoz egy forráskódot, amely így néz ki.

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

ToString()

A C++/CX típusok biztosítják az Object::ToString metódust .

int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".

A C++/WinRT közvetlenül nem biztosítja ezt a létesítményt, de alternatív megoldásokhoz fordulhat.

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

A C++/WinRT a winrt::to_hstring is támogatja korlátozott számú típus esetén. Túlterheléseket kell hozzáadnia minden további sztringbefűzni kívánt típushoz.

Nyelv Stringify int Felsorolás karakterlánccá alakítása
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);

Enum sztringelése esetén meg kell adnia a winrt::to_hstring implementációját.

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

Ezeket a karakterlánccá alakított értékeket az adatkötés gyakran implicit módon használja fel.

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

Ezek a kötések végrehajtják a kötött tulajdonság winrt::to_hstring értékét. A második példa (a StatusEnum) esetében a winrt::to_hstring saját túlterhelését kell megadnia, ellenkező esetben fordítóhiba jelenik meg.

Sztringépítés

A C++/CX és a C++/WinRT a karakterláncok összeállításához a szabványos std::wstringstream osztályt használja.

Operation C++/CX C++/WinRT
Sztring hozzáfűzése, null értékek megőrzése stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
Karakterlánc hozzáfűzése, álljon meg az első null karakternél stream << s->Data(); stream << s.c_str();
Eredmény kinyerése ws = stream.str(); ws = stream.str();

További példák

Az alábbi példákban a ws egy std::wstring típusú változó. Emellett, míg a C++/CX képes egy Platform::String objektumot 8 bites karakterláncból létrehozni, a C++/WinRT erre nem képes.

Operation C++/CX C++/WinRT
Sztring létrehozása literálból String^ s = "hello";
String^ s = L"hello";
// winrt::hstring s{ "hello" }; // Doesn't compile
winrt::hstring s{ L"hello" };
Konvertálás std::wstring értékről, null értékek megőrzése 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
Konvertálás innen: std::wstring, megáll az első null karakternél 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
Konvertálás std::wstring értékre, null értékek megőrzése std::wstring ws{ s->Data(), s->Length };
ws = std::wstring(s>Data(), s->Length);
std::wstring ws{ s };
ws = s;
Konvertálás std::wstring típusra, leállás az első nullkarakternél std::wstring ws{ s->Data() };
ws = s->Data();
std::wstring ws{ s.c_str() };
ws = s.c_str();
Konstans átadása metódusnak Method("hello");
Method(L"hello");
// Method("hello"); // Doesn't compile
Method(L"hello");
Pass std::wstring to method Method(ref new String(ws.c_str(),
  (uint32_t)ws.size()); // Stops on first null
Method(ws);
// param::winrt::hstring accepts std::wstring_view

Fontos API-k

Note

Számos C++/WinRT-témakör áttelepítése folyamatban van az UWP dokumentációjából ebbe a szakaszba. A migrálás befejezéséig az alábbi listában szereplő hivatkozások az UWP-dokumentumok szakaszához vezethetnek. A C++/WinRT nyelvi kivetítés az UWP és a WinUI 3 alkalmazások esetében is megegyezik, így a tartalom mindkét környezetben alkalmazható. Ezek a cikkek kifejezetten feljegyzik az UWP-specifikus mintákat (például az alkalmazás életciklusát vagy Windows.UI a névtér API-kat).