Přechod na C++/WinRT z C++/CX

Toto téma je první v řadě popisující, jak můžete zdrojový kód v projektu C++/CX přenést na jeho ekvivalent v jazyce C++/WinRT.

Pokud váš projekt také používá typy knihovny prostředí Windows Runtime C++ Template Library (WRL), viz Přechod z knihovny WRL na C++/WinRT.

Strategie přenosu

Je dobré vědět, že portování z C++/CX do C++/WinRT je obecně přímočaré, s jedinou výjimkou: přechodem z úloh Parallel Patterns Library (PPL) na korutiny. Modely se liší. Neexistuje přirozené mapování jedna ku jedné mezi úlohami PPL a korutinami ani žádný jednoduchý způsob, jak kód mechanicky převést tak, aby to fungovalo ve všech případech. Nápovědu k tomuto konkrétnímu aspektu přenosu a možnostem spolupráce mezi těmito dvěma modely najdete v tématu Asynchrony a spolupráce mezi C++/WinRT a C++/CX.

Vývojové týmy rutinně hlásí, že jakmile přejdou přes překážku přenosu asynchronního kódu, zbytek práce přenosu je z velké části mechanický.

Přenos v jednom kroku

Pokud můžete portovat celý projekt najednou, stačí vám pouze toto téma, které obsahuje všechny potřebné informace (a nebudete potřebovat témata interop, která následují po tomto tématu). Doporučujeme začít vytvořením nového projektu v Visual Studio pomocí jedné ze šablon projektů C++/WinRT (viz Visual Studio podpora C++/WinRT). Potom přesuňte soubory zdrojového kódu do tohoto nového projektu a přepněte veškerý zdrojový kód C++/CX do C++/WinRT.

Pokud byste chtěli raději provést práci přenosu ve stávajícím projektu C++/CX, budete do něj muset přidat podporu C++/WinRT. Postup, jak to provést, je popsán v tématu Převzetí projektu C++/CX a přidání podpory C++/WinRT. V době, kdy jste dokončili přenos, změníte, co byl čistý projekt C++/CX, na čistý projekt C++/WinRT.

Note

Pokud máte projekt komponenty prostředí Windows Runtime, pak je vaší jedinou možností převést ho v jednom kroku. Projekt komponenty prostředí Windows Runtime napsaný v jazyce C++ musí obsahovat buď veškerý zdrojový kód C++/CX, nebo veškerý zdrojový kód C++/WinRT. Nemůžou existovat společně v tomto typu projektu.

Postupné přenosy projektu

S výjimkou projektů komponent prostředí Windows Runtime, jak je uvedeno v předchozí části, pokud velikost nebo složitost základu kódu vyžaduje postupné přenesení projektu, budete potřebovat proces přenosu, ve kterém po dobu, kdy kód C++/CX a C++/WinRT existuje vedle sebe ve stejném projektu. Kromě tohoto tématu si také přečtěte Interoperabilita mezi C++/WinRT a C++/CX a Asynchronie a interoperabilita mezi C++/WinRT a C++/CX. Tato témata poskytují informace a příklady kódu, které ukazují, jak interoperovat mezi těmito dvěma projekcemi jazyka.

Pokud chcete projekt připravit na proces postupného přenosu, je jednou z možností přidání podpory C++/WinRT do projektu C++/CX. Postup, jak to provést, je popsán v tématu Převzetí projektu C++/CX a přidání podpory C++/WinRT. Pak můžete portovat postupně odsud.

Další možností je vytvořit nový projekt v Visual Studio pomocí jedné ze šablon projektů C++/WinRT (viz Visual Studio podpora C++/WinRT). A pak do projektu přidejte podporu C++/CX. Kroky, které provedete, jsou popsány v části Pořízení projektu C++/WinRT a přidání podpory jazyka C++/CX. Potom můžete začít přesouvat zdrojový kód do něj a přitom portovat část zdrojového kódu C++/CX do C++/WinRT.

V obou případech budete spolupracovat (oběma způsoby) mezi kódem C++/WinRT a veškerým kódem C++/CX, který jste ještě nepřevedli.

Note

C++/CX i sada Windows SDK deklarují typy v kořenovém oboru názvů Windows. Typ Windows projektovaný do C++/WinRT má stejný plně kvalifikovaný název jako typ Windows, ale je umístěný v oboru názvů winrt jazyka C++. Tyto odlišné obory názvů umožňují portovat z C++/CX do C++/WinRT vlastním tempem.

Postupný převod projektu XAML

Důležité

Pro projekt, který používá XAML, musí být všechny typy stránek XAML v daném okamžiku zcela C++/CX nebo zcela C++/WinRT. C++/CX a C++/WinRT můžete stále kombinovat i mimo typy stránek XAML v rámci stejného projektu (ve svých modelech a viewmodelech i jinde).

Pro tento scénář doporučujeme vytvořit nový projekt C++/WinRT a zkopírovat zdrojový kód a revize z projektu C++/CX. Pokud jsou všechny typy stránek XAML C++/WinRT, můžete přidat nové stránky XAML s Project>Přidat novou položku...>Visual C++>Prázdná stránka (C++/WinRT).

Alternativně můžete při portování použít komponentu prostředí Windows Runtime (WRC) k vyčlenění kódu z projektu XAML C++/CX.

  • Můžete vytvořit nový projekt C++/CX WRC, přesunout do projektu co nejvíce kódu C++/CX a pak změnit projekt XAML na C++/WinRT.
  • Nebo můžete vytvořit nový projekt C++/WinRT WRC, ponechat projekt XAML jako C++/CX a začít portovat C++/CX do C++/WinRT a přesunout výsledný kód z projektu XAML a do projektu komponent.
  • Můžete také mít projekt komponent C++/CX společně s projektem komponent C++/WinRT v rámci stejného řešení, odkazovat na oba z projektu aplikace a postupně portovat z jednoho do druhého. Další podrobnosti o použití dvou projekcí jazyka ve stejném projektu najdete v tématu Spolupráce mezi C++/WinRT a C++/CX .

První kroky při přenosu projektu C++/CX do C++/WinRT

Bez ohledu na to, jaká bude vaše strategie přenosu (portování v jednom průchodu nebo postupné přenosy), je vaším prvním krokem příprava projektu na přenos. Tady je rekapitulace toho, co jsme popsali v strategiích pro přenos z hlediska typu projektu, se kterým začnete, a jak ho nastavit.

  • Přenos v jednom kroku. Vytvořte nový projekt v Visual Studio pomocí jedné ze šablon projektů C++/WinRT. Přesuňte soubory z projektu C++/CX do nového projektu a portujte zdrojový kód C++/CX.
  • Postupné přenosování projektu jiného typu než XAML Do projektu C++/CX můžete přidat podporu pro C++/WinRT (viz Převzetí projektu C++/CX a přidání podpory pro C++/WinRT) a postupně ho migrovat. Můžete také vytvořit nový projekt C++/WinRT a přidat do ní podporu C++/CX (viz Pořízení projektu C++/WinRT a přidání podpory C++/CX), postupné přesouvání souborů a portů.
  • Postupné přenosování projektu XAML Vytvořte nový projekt C++/WinRT, přesuňte soubory postupně a portujte. V daném okamžiku musí být typy vašich stránek XAML buď všechny C++/WinRT nebo všechny C++/CX.

Zbytek tohoto tématu platí bez ohledu na to, jakou strategii přenosu zvolíte. Obsahuje katalog technických podrobností, které jsou součástí přenosu zdrojového kódu z C++/CX do C++/WinRT. Pokud portujete postupně, pravděpodobně se také budete chtít podívat na Interoperabilitu mezi C++/WinRT a C++/CX a Asynchronitu a interoperabilitu mezi C++/WinRT a C++/CX.

Pravidla pojmenování souborů

Soubory značek XAML

Původ souboru C++/CX C++/WinRT
Soubory XAML pro vývojáře MyPage.xaml
MyPage.xaml.h
MyPage.xaml.cpp
MyPage.xaml
MyPage.h
MyPage.cpp
MyPage.idl (viz níže)
Vygenerované soubory XAML MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.g.h

Všimněte si, že C++/WinRT odebere .xaml z *.h názvů souborů a *.cpp souborů.

C++/WinRT přidá další vývojářský soubor, soubor Midl (.idl). C++/CX tento soubor interně vygeneruje a přidá do něj každý veřejný a chráněný člen. V jazyce C++/WinRT přidáte a vytvoříte soubor sami. Další podrobnosti, příklady kódu a názorný postup při vytváření IDL najdete v tématu Ovládací prvky XAML, vazba na vlastnost C++/WinRT.

Viz také Vyčlenění tříd modulu runtime do souborů MIDL (.idl)

Třídy modulu runtime

C++/CX neukládá omezení názvů hlaviček souborů; Běžně se do jednoho souboru hlaviček vkládá několik definic tříd modulu runtime, zejména u malých tříd. Ale C++/WinRT vyžaduje, aby každá třída modulu runtime má vlastní hlavičkový soubor pojmenovaný za názvem třídy.

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

Méně běžné (ale přesto právní) v jazyce C++/CX je použití jinak pojmenovaných souborů hlaviček pro vlastní ovládací prvky XAML. Tento soubor hlaviček budete muset přejmenovat tak, aby odpovídal názvu třídy.

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

Požadavky na soubor hlaviček

C++/CX nevyžaduje zahrnutí žádných speciálních hlavičkových souborů, protože interně automaticky generuje soubory hlaviček ze .winmd souborů. V jazyce C++/CX je běžné používat direktivy using pro oblasti názvů, které používáte pod jejich názvem.

using namespace Windows::Media::Playback;

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

Direktiva using namespace Windows::Media::Playback nám umožňuje psát MediaPlaybackItem bez předpony oboru názvů. Zmínili jsme také obor názvů Windows.Media.Core, protože item->VideoTracks->GetAt(0) vrací Windows.Media.Core.VideoTrack. Ale nemuseli jsme nikam zadávat název VideoTrack , takže jsme nepotřebovali direktivu using Windows.Media.Core .

C++/WinRT ale vyžaduje, abyste zahrnuli hlavičkový soubor odpovídající každému oboru názvů, který používáte, i když ho nepojmenujete.

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

Na druhou stranu, i když MediaPlaybackItem.AudioTracksChanged událost je typu TypedEventHandler<MediaPlaybackItem, Windows. Foundation.Collections.IVectorChangedEventArgs>, nemusíme zahrnoutwinrt/Windows.Foundation.Collections.h, protože jsme tuto událost nepoužívala.

C++/WinRT také vyžaduje, abyste zahrnuli soubory hlaviček pro obory názvů, které využívají kód XAML.

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

Pokud používáte třídu Rectangle, musíte přidat tuto direktivu include.

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

Pokud zapomenete připojit hlavičkový soubor, všechno se sice zkompiluje bez problémů, ale dostanete chyby linkeru, protože chybí třídy consume_.

Předávání parametrů

Při psaní zdrojového kódu C++/CX předáváte typy C++/CX jako parametry funkcí ve formě referencí se stříškou (^).

void LogPresenceRecord(PresenceRecord^ record);

V jazyce C++/WinRT byste pro synchronní funkce měli ve výchozím nastavení používat const& parametry. Tím se zabráníte kopírováním a vzájemné režii. Korutany by ale měly používat předávací hodnotu, aby se zajistilo, že zachytí podle hodnoty a zabrání problémům s životností (další podrobnosti najdete v tématu Souběžnost a asynchronní operace s C++/WinRT).

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

Objekt C++/WinRT je v podstatě hodnota, která obsahuje ukazatel rozhraní na záložní objekt prostředí Windows Runtime. Při kopírování objektu C++/WinRT zkopíruje kompilátor zapouzdřený ukazatel rozhraní a zvýší jeho počet odkazů. Při případném zničení kopie se sníží počet odkazů. V případě potřeby se tedy účtují pouze režijní náklady na kopii.

Odkazy na proměnné a pole

Při psaní zdrojového kódu C++/CX se proměnné hat (^) používají k odkazování na objekty prostředí prostředí Windows Runtime a operátor šipky (->) k dereferencování proměnné hat.

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

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

Při převodu na ekvivalentní kód C++/WinRT si z velké části vystačíte s odebráním stříšek a nahrazením operátoru šipky (->) operátorem tečka (.). Projektované typy C++/WinRT jsou hodnoty, nikoli ukazatele.

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

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

Výchozí konstruktor reference hat v jazyce C++/CX ji inicializuje na hodnotu null. Tady je příklad kódu C++/CX, ve kterém vytvoříme proměnnou nebo pole správného typu, ale ten, který není inicializovaný. Jinými slovy, zpočátku neodkazuje na TextBlock; hodláme později přiřadit odkaz.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

Ekvivalent v jazyce C++/WinRT najdete v tématu Zpožděná inicializace.

Properties

Rozšíření jazyka C++/CX zahrnují koncept vlastností. Při psaní zdrojového kódu C++/CX máte přístup k vlastnosti, jako by šlo o pole. Standardní C++ nemá koncept vlastnosti, takže v jazyce C++/WinRT voláte funkce get a set.

V následujících příkladech jsou všechny vlastnosti XboxUserId, UserState, PresenceDeviceRecords a Size .

Načtení hodnoty z vlastnosti

Takto získáte hodnotu vlastnosti v jazyce C++/CX.

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

Ekvivalentní zdrojový kód C++/WinRT volá funkci se stejným názvem jako vlastnost, ale bez parametrů.

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

Všimněte si, že funkce PresenceDeviceRecords vrací prostředí Windows Runtime objekt, který sám má funkci Size. Vzhledem k tomu, že vrácený objekt je také projektovaný typ C++/WinRT, dereferencujeme jej pomocí operátoru tečky, abychom zavolali Size.

Nastavení vlastnosti na novou hodnotu

Nastavení vlastnosti na novou hodnotu se řídí podobným vzorem. Nejprve v C++/CX.

record->UserState = newValue;

Chcete-li provést ekvivalent v jazyce C++/WinRT, zavoláte funkci se stejným názvem jako vlastnost a předáte argument.

record.UserState(newValue);

Vytvoření instance třídy

Pracujete s objektem C++/CX prostřednictvím popisovače, který se běžně označuje jako odkaz na klobouk (^). Vytvoříte nový objekt prostřednictvím klíčového ref new slova, které pak volá RoActivateInstance k aktivaci nové instance třídy runtime.

using namespace Windows::Storage::Streams;

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

Objekt C++/WinRT je hodnota, takže ho můžete alokovat na zásobníku nebo jako člen objektu. Nikdy nepoužíváteref new (aninew) k přidělení objektu C++/WinRT. Na pozadí se stále volá RoActivateInstance .

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

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

Pokud je inicializace prostředku náročná, je běžné, že její inicializaci zpozdíte, dokud ho skutečně nepotřebujete. Jak už bylo zmíněno, výchozí konstruktor pro referenci hat v jazyce C++/CX ji inicializuje na 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;
};

Stejný kód byl portován do C++/WinRT. Všimněte si použití konstruktoru std::nullptr_t . Další informace o tomto konstruktoru naleznete v tématu Zpožděná inicializace.

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

Vliv výchozího konstruktoru na kolekce

Typy kolekcí jazyka C++ používají výchozí konstruktor, který může vést k nezamýšlené konstrukci objektů.

Scenario C++/CX C++/WinRT (nesprávná) C++/WinRT (správně)
Místní proměnná, zpočátku prázdná TextBox^ textBox; TextBox textBox; // Creates a TextBox! TextBox textBox{ nullptr };
Členská proměnná, zpočátku prázdná class C {
  TextBox^ textBox;
};
class C {
  TextBox textBox; // Creates a TextBox!
};
class C {
  TextBox textbox{ nullptr };
};
Globální proměnná, zpočátku prázdná TextBox^ g_textBox; TextBox g_textBox; // Creates a TextBox! TextBox g_textBox{ nullptr };
Vektor prázdných odkazů std::vector<TextBox^> boxes(10); // Creates 10 TextBox objects!
std::vector<TextBox> boxes(10);
std::vector<TextBox> boxes(10, nullptr);
Nastavení hodnoty v mapě 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);
Matice prázdných odkazů TextBox^ boxes[2]; // Creates 2 TextBox objects!
TextBox boxes[2];
TextBox boxes[2] = { nullptr, nullptr };
Spárovat std::pair<TextBox^, String^> p; // Creates a TextBox!
std::pair<TextBox, String> p;
std::pair<TextBox, String> p{ nullptr, nullptr };

Další informace o kolekcích prázdných odkazů

Kdykoli máte platformu::Array^ (viz Port Platform::Array^) v C++/CX, máte možnost portovat ji na std::vector v jazyce C++/WinRT (ve skutečnosti jakýkoli souvislý kontejner) a ponechat ho jako pole. Existují výhody výběru std::vector.

Například když existuje zkratka pro vytvoření vektoru prázdných odkazů s pevnou velikostí (viz tabulka výše), neexistuje taková zkratka pro vytvoření pole prázdných odkazů. Je nutné opakovat nullptr pro každý prvek v poli. Pokud jich máte příliš málo, budou tyto doplňky vytvořené jako výchozí.

U vektoru ho můžete vyplnit prázdnými odkazy při inicializaci (jako v tabulce výše), nebo ho můžete vyplnit prázdnými odkazy po inicializaci kódem, jako je tento.

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

Další informace o příkladu std::map

Operátor [] [] pro std::map funguje takto.

  • Pokud se klíč nachází v mapě, vraťte odkaz na existující hodnotu (kterou můžete přepsat).
  • Pokud se klíč v mapě nenajde, vytvořte v mapě novou položku, která se skládá z klíče (přesunutého, pokud se dá přesunout) a výchozí konstruované hodnoty a vrátí odkaz na hodnotu (kterou pak můžete přepsat).

Jinými slovy, [] operátor vždy vytvoří položku v mapě. To se liší od jazyka C#, Java a JavaScriptu.

Převod ze základní třídy modulu runtime na odvozenou třídu

Je běžné mít odkaz na základní třídu, o kterém víte, že odkazuje na objekt odvozeného typu. V jazyce C++/CX použijete dynamic_cast k přetypování odkazu na základní třídu na odkaz na odvozenou třídu. dynamic_cast je ve skutečnosti jen skryté volání QueryInterface. Tady je typický příklad – zpracováváte událost změny vlastnosti závislosti a chcete přetypovat z DependencyObject zpět na konkrétní typ, který vlastní vlastnost závislosti.

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

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

Ekvivalentní kód C++/WinRT nahrazuje dynamic_cast voláním funkce IUnknown::try_as , která zapouzdřuje QueryInterface. Máte také možnost místo toho volat IUnknown::as, což vyvolá výjimku, pokud dotaz na požadované rozhraní (výchozí rozhraní požadovaného typu) nevrátí výsledek. Tady je příklad kódu C++/WinRT.

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

Odvozené třídy

Aby bylo možné dědit z běhové třídy, musí být základní třída komponovatelná. C++/CX nevyžaduje provedení žádných speciálních kroků, aby vaše třídy byly kompozibilní, ale C++/WinRT dělá. Nezapečetěné klíčové slovo použijete k označení, že chcete, aby vaše třída byla použitelná jako základní třída.

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

Do třídy hlavičky implementace musíte zahrnout soubor hlaviček základní třídy před zahrnutím automaticky generované hlavičky pro odvozenou třídu. V opačném případě se zobrazí chyby typu Neplatné použití tohoto typu jako výrazu.

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

Zpracování událostí s delegátem

Tady je typický příklad zpracování události v jazyce C++/CX pomocí funkce lambda jako delegát v tomto případě.

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

Toto je ekvivalent v jazyce 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.
});

Místo lambda funkce můžete zvolit implementaci delegáta jako volnou funkci nebo jako ukazatel na členskou funkci. Další informace najdete v tématu Zpracování událostí pomocí delegátů v C++/WinRT.

Pokud portujete ze základu kódu C++/CX, kde se události a delegáty používají interně (ne napříč binárními soubory), pomůže vám winrt::d elegate tento vzor replikovat v jazyce C++/WinRT. Viz také parametrizované delegáty, jednoduché signály a zpětná volání v rámci projektu.

Odvolání delegáta

V jazyce C++/CX použijete -= operátor k odvolání předchozí registrace události.

myButton->Click -= token;

Toto je ekvivalent v jazyce C++/WinRT.

myButton().Click(token);

Další informace a možnosti najdete v tématu Odvolání registrovaného delegáta.

Boxování a rozbalování

C++/CX automaticky rozdělí skaláry do objektů. C++/WinRT vyžaduje, abyste explicitně volali funkci winrt::box_value . Oba jazyky vyžadují explicitní rozbalení. Viz Boxing and unboxing with C++/WinRT.

V následujících tabulkách použijeme tyto definice.

C++/CX C++/WinRT
int i; int i;
String^ s; winrt::hstring s;
Object^ o; IInspectable o;
Operation C++/CX C++/WinRT
Boxování o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Rozbalení i = (int)o;
s = (String^)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX a C# vyvolá výjimky, pokud se pokusíte rozbalit ukazatel null na typ hodnoty. C++/WinRT to považuje za programovací chybu a zhroutí se. V jazyce C++/WinRT použijte funkci winrt::unbox_value_or , pokud chcete zpracovat případ, kdy objekt není typu, o kterém jste si mysleli, že se jedná.

Scenario C++/CX C++/WinRT
Rozbalte známé celé číslo i = (int)o; i = unbox_value<int>(o);
Pokud má o hodnotu null Platform::NullReferenceException Havárie
Pokud o není zabalený int Platform::InvalidCastException Havárie
Rozbalte int, použijte záložní hodnotu, pokud je null; jinak havarujte i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Pokud je to možné, převeďte na typ int; ve všech ostatních případech použijte záložní variantu auto box = dynamic_cast<IBox<int>^>(o);
i = box ? box->Value : fallback;
i = unbox_value_or<int>(o, fallback);

Boxing a unboxing řetězce

Řetězec je nějakým způsobem typ hodnoty a jiným způsobem typ odkazu. C++/CX a C++/WinRT zachází s řetězci jinak.

Typ ABI HSTRING je ukazatel na řetězec s počítáním referencí. Ale neodvozuje se od IInspectable, takže to není technicky objekt. Navíc hodnota HSTRING s hodnotou null představuje prázdný řetězec. Zabalení věcí, které nejsou odvozeny z IInspectable je provádí zabalením uvnitř IReference<T> a prostředí Windows Runtime poskytuje standardní implementaci ve formě PropertyValue objektu (vlastní typy jsou hlášeny jako PropertyType::OtherType).

C++/CX představuje prostředí Windows Runtime řetězec jako typ odkazu; zatímco C++/WinRT projektuje řetězec jako typ hodnoty. To znamená, že řetězec s hodnotou null s rámečkem může mít různé reprezentace v závislosti na tom, jak jste se tam dostali.

Kromě toho C++/CX umožňuje dereferencovat hodnotu null typu String^, v takovém případě se chová jako řetězec "".

Behavior C++/CX C++/WinRT
Prohlášení Object^ o;
String^ s;
IInspectable o;
hstring s;
Kategorie typu řetězce Typ odkazu Typ hodnoty
null se mapuje jako HSTRING (String^)nullptr hstring{}
Jsou hodnoty null a "" identické? Yes Yes
Platnost hodnoty null s = nullptr;
s->Length == 0 (platné)
s = hstring{};
s.size() == 0 (platné)
Pokud k objektu přiřadíte řetězec null. o = (String^)nullptr;
o == nullptr
o = box_value(hstring{});
o != nullptr
Pokud k objektu přiřadíte "" o = "";
o == nullptr
o = box_value(hstring{L""});
o != nullptr

Základní boxování a rozbalení.

Operation C++/CX C++/WinRT
Zabalit řetězec o = s;
Prázdný řetězec se změní na hodnotu nullptr.
o = box_value(s);
Prázdný řetězec se stane objektem, který není null.
Rozbalení známého řetězce s = (String^)o;
Objekt Null se stane prázdným řetězcem.
InvalidCastException, pokud nejde o řetězec.
s = unbox_value<hstring>(o);
Objekt null selže.
Pokud to není řetězec, aplikace spadne.
Rozbalení možného řetězce s = dynamic_cast<String^>(o);
Objekt null nebo ne-řetězec se stane prázdným řetězcem.
s = unbox_value_or<hstring>(o, fallback);
Hodnota null nebo hodnota, která není řetězec, použije záložní hodnotu.
Prázdný řetězec je zachován.

Souběžnost a asynchronní operace

Knihovna PPL (Parallel Patterns Library) (concurrency::task) byla například aktualizována tak, aby podporovala odkazy na C++/CX hat.

V případě C++/WinRT byste měli místo něj použít korutiny a co_await. Další informace a příklady kódu najdete v tématu Souběžnost a asynchronní operace s C++/WinRT.

Využívání objektů z revizí XAML

V projektu C++/CX můžete přistupovat k soukromým členům a pojmenovaným prvkům v kódu XAML. V jazyce C++/WinRT se ale všechny entity spotřebované pomocí rozšíření značek XAML {x:Bind} musí veřejně zveřejnit v IDL.

Také se vazba na hodnotu typu Boolean v jazyce C++/CX zobrazuje jako true nebo false, ale v jazyce C++/WinRT se zobrazuje jako Windows.Foundation.IReference`1<Boolean>.

Další informace a příklady kódu najdete v tématu Využívání objektů z revizí.

Mapování typů C++/CX Platform na typy C++/WinRT

C++/CX poskytuje několik datových typů v oboru názvů Platform. Tyto typy nejsou standardní jazyk C++, takže je můžete použít pouze při povolení jazykových rozšíření prostředí Windows Runtime (Visual Studio vlastnost projektu C/C++>General>Consume prostředí Windows Runtime Extension>Yes (/ZW)). Následující tabulka vám pomůže portovat z typů platforem na jejich ekvivalenty v jazyce C++/WinRT. Jakmile to uděláte, protože C++/WinRT je standardní C++, můžete tuto možnost vypnout /ZW .

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

Přenést Platform::Agile^ na winrt::agile_ref

Typ Platform::Agile^ v jazyce C++/CX představuje třídu prostředí Windows Runtime, ke které lze přistupovat z libovolného vlákna. Ekvivalent C++/WinRT je winrt::agile_ref.

V C++/CX.

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

V C++/WinRT (WinUI 3 používá Microsoft::UI::Xaml::Window místo CoreWindow).

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

Port Platform::Array^

V případech, kdy C++/CX vyžaduje použití pole, C++/WinRT umožňuje použít jakýkoli souvislý kontejner. Podívejte se, jak výchozí konstruktor ovlivňuje kolekce z důvodu, proč std::vector je dobrou volbou.

Kdykoli tedy máte platformu::Array^ v jazyce C++/CX, mezi možnosti přenosu patří použití seznamu inicializátorů, std::array nebo std::vector. Další informace a příklady kódu naleznete v tématu Standardní inicializátor seznamy a standardní pole a vektory.

Přenést Platform::Exception^ na winrt::hresult_error

Typ Platform::Exception^ je v jazyce C++/CX generován, když rozhraní API prostředí Windows Runtime vrátí hodnotu HRESULT jinou než S_OK. Ekvivalent C++/WinRT je winrt::hresult_error.

Pokud chcete portovat do C++/WinRT, změňte veškerý kód, který používá platformu::Exception^ tak, aby používal winrt::hresult_error.

V C++/CX.

catch (Platform::Exception^ ex)

V C++/WinRT.

catch (winrt::hresult_error const& ex)

C++/WinRT poskytuje tyto třídy výjimek.

Typ výjimky Základní třída HRESULT
winrt::hresult_error zavolejte hresult_error::to_abi
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 (Není implementováno)
winrt::hresult_out_of_bounds winrt::hresult_error E_BOUNDS
winrt::hresult_wrong_thread winrt::hresult_error RPC_E_WRONG_THREAD

Všimněte si, že každá třída (prostřednictvím základní třídy hresult_error ) poskytuje funkci to_abi , která vrací hodnotu HRESULT chyby, a funkci zprávy , která vrací řetězcovou reprezentaci tohoto HRESULT.

Tady je příklad vyvolání výjimky v jazyce C++/CX.

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

A ekvivalent v C++/WinRT.

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

Přeneste Platform::Object^ na winrt::Windows::Foundation::IInspectable

Stejně jako všechny typy C++/WinRT je i winrt::Windows::Foundation::IInspectable hodnotový typ. Takto inicializujete proměnnou tohoto typu na hodnotu null.

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

Migrujte Platform::String^ na winrt::hstring

Platform::String^ je ekvivalentní typu prostředí Windows Runtime HSTRING ABI. Pro C++/WinRT je ekvivalent winrt::hstring. Pomocí jazyka C++/WinRT však můžete volat rozhraní API prostředí Windows Runtime pomocí širokých typů řetězců standardní knihovny C++, jako jsou std::wstring a/nebo široké řetězcové literály. Další podrobnosti a příklady kódu najdete v tématu Zpracování řetězců v jazyce C++/WinRT.

Pomocí C++/CX můžete přistupovat k vlastnosti Platform::String::Data a získat řetězec jako pole const wchar_t* ve stylu jazyka C (například pro jeho předání do std::wcout).

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

Pokud chcete to samé provést s jazykem C++/WinRT, můžete použít funkci hstring::c_str k získání verze řetězce ve stylu jazyka C s hodnotou null, stejně jako můžete použít std::wstring.

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

Pokud jde o implementaci rozhraní API, která přijímají nebo vracejí řetězce, obvykle změníte veškerý kód C++/CX, který používá Platform::String^, tak, aby místo něj používal winrt::hstring.

Tady je příklad rozhraní API C++/CX, které přebírá řetězec.

void LogWrapLine(Platform::String^ str);

V případě C++/WinRT můžete toto rozhraní API deklarovat v MIDL 3.0 takto.

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

Sada nástrojů C++/WinRT pak vygeneruje zdrojový kód, který vypadá takto.

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

ToString()

Typy C++/CX poskytují metodu Object::ToString .

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

C++/WinRT přímo neposkytuje toto zařízení, ale můžete se obrátit na alternativy.

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

C++/WinRT také podporuje winrt::to_hstring pro omezený počet typů. Budete muset přidat přetížení pro všechny další typy, které chcete převést na řetězec.

Jazyk Převést int na řetězec Stringify – výčet
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);

Při převodu výčtu na řetězec budete muset poskytnout implementaci 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));
        }
    }
}

Tyto řetězcové reprezentace jsou často implicitně využívány při datové vazbě.

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

Tyto vazby budou provádět winrt::to_hstring vázané vlastnosti. V případě druhého příkladu ( StatusEnum) musíte zadat vlastní přetížení winrt::to_hstring, jinak se zobrazí chyba kompilátoru.

Vytváření řetězců

C++/CX a C++/WinRT pro vytváření řetězců spoléhají na standardní třídu std::wstringstream.

Operation C++/CX C++/WinRT
Připojit řetězec se zachováním hodnot null stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
Připojte řetězec, zastavte při první hodnotě null. stream << s->Data(); stream << s.c_str();
Extrahování výsledku ws = stream.str(); ws = stream.str();

Další příklady

V následujících příkladech je ws proměnnou typu std::wstring. I když C++/CX může vytvořit Platform::String z 8bitového řetězce, C++/WinRT to neudělá.

Operation C++/CX C++/WinRT
Vytvořit řetězec z literálu String^ s = "hello";
String^ s = L"hello";
// winrt::hstring s{ "hello" }; // Doesn't compile
winrt::hstring s{ L"hello" };
Převod z std::wstring, zachování hodnot null 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
Převést z std::wstring, zastavit na prvním znaku null String^ s = ref new String(ws.c_str()); winrt::hstring s{ ws.c_str() };
s = winrt::hstring(ws.c_str());
// s = ws.c_str(); // Doesn't compile
Převod na std::wstring, zachování hodnot null std::wstring ws{ s->Data(), s->Length };
ws = std::wstring(s>Data(), s->Length);
std::wstring ws{ s };
ws = s;
Převést na std::wstring, zastavit se na prvním nulovém znaku std::wstring ws{ s->Data() };
ws = s->Data();
std::wstring ws{ s.c_str() };
ws = s.c_str();
Předání literálu metodě Method("hello");
Method(L"hello");
// Method("hello"); // Doesn't compile
Method(L"hello");
Předat std::wstring 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

Důležitá rozhraní API

Note

V procesu migrace z dokumentace upW do této části probíhá řada témat C++/WinRT. Až do dokončení migrace můžou odkazy v následujícím seznamu vést k části Dokumentace k UPW. Projekce jazyka C++/WinRT je stejná pro aplikace pro UPW i WinUI 3, takže obsah je použitelný v obou kontextech. Jakékoli vzory specifické pro UWP (například životní cyklus aplikace nebo rozhraní API v oboru názvů Windows.UI) jsou v těchto článcích výslovně uvedeny.