Sdílet prostřednictvím


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

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

Pokud váš projekt používá také typy knihovny šablon C++ prostředí Windows Runtime C++, přečtěte si téma Přesunout do C++/WinRT z knihovny WRL.

Strategie přenosu

Je vhodné vědět, že přenosy z C++/CX do C++/WinRT jsou obecně jednoduché, s výjimkou přechodu z knihovny PPL (Parallel Patterns Library) úloh na korutiny. Modely se liší. Neexistuje přirozené mapování 1:1 z úkolů PPL na korutiny a neexistuje žádný jednoduchý způsob, jak mechanicky přenést kód, který funguje pro všechny případy. 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 vzájemné 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ý.

Portování v jednom průchodu

Pokud máte možnost portovat celý projekt jedním průchodem, budete potřebovat jenom toto téma pro informace, které potřebujete (a nebudete potřebovat zprostředkovatele komunikace témata, která následují). Doporučujeme začít vytvořením nového projektu v sadě Visual Studio pomocí jedné ze šablon projektů C++/WinRT (viz podpora sady Visual Studio pro 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. Kroky, které musíte následovat, jsou popsány v Zpracování 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.

Poznámka:

Pokud máte projekt komponent 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é portování 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ě čtení tohoto tématu se také podívejte na interoperabilitu mezi C++/WinRT a C++/CX a asynchronii a interoperabilitu 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. Kroky, které musíte následovat, jsou popsány v Zpracování 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 sadě Visual Studio pomocí jedné ze šablon projektů C++/WinRT (viz podpora sady Visual Studio pro C++/WinRT). A pak do projektu přidejte podporu C++/CX. Kroky, které následujete, jsou popsány v tématu Provedení projektu C++/WinRT a přidání podpory C++/CX. Potom můžete zdrojový kód přesunout do tohoto kódu a přenést některé 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.

Poznámka:

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

Postupné přenášení 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. Stále můžete kombinovat C++/CX a C++/WinRT mimo typů stránek XAML ve stejném projektu (v modelech a modelech zobrazení a 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 pomocí Project>Přidat Novou Položku...>Visual C++>Prázdná Stránka (C++/WinRT).

Alternativně můžete použít komponentu Windows Runtime (WRC) k oddělení kódu z projektu XAML C++/CX během jeho portování.

  • 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. Podívejte se znovu na interoperabilitu mezi C++/WinRT a C++/CX pro podrobnosti o tom, jak používat obě jazykové projekce ve stejném projektu.

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. Připomeňme si, co jsme popsali ve strategiích pro adaptaci z hlediska typu projektu, se kterým začínáte, a jak ho nastavit.

  • Přenášení v jednom průchodu. Vytvořte nový projekt v sadě 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 než XAML. Do projektu C++/CX můžete přidat podporu jazyka C++/WinRT (viz Převod projektu C++/CX s přidáním podpory C++/WinRT) a postupně projekt převést. Můžete také vytvořit nový projekt C++/WinRT a přidat do ní podporu C++/CX (viz Převzetí projektu C++/WinRT a přidání podpory C++/CX), přesunutí souborů a postupného přenosu.
  • Postupné portování projektu XAML. Vytvořte nový projekt C++/WinRT, přesuňte soubory postupně a portujte. V každém okamžiku musí být typy stránek XAML 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ě, budete pravděpodobně chtít vidět také interoperabilitu mezi C++/WinRT a C++/CX a Asynchrony a interoperabilitou mezi C++/WinRT a C++/CX.

Zásady vytváření názvů 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 a *.cpp názvů 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 přehled vytváření IDL najdete v tématu ovládacích prvků XAML, jak připojit k vlastnosti C++/WinRT.

Viz také třídy modulu runtime Factoring 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 z .winmd souborů. V jazyce C++/CX se běžně používají direktivy using pro obory názvů, které používáte podle názvu.

using namespace Windows::Media::Playback;

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

Direktiva using namespace Windows::Media::Playback vám umožňuje psát MediaPlaybackItem bez použití předpony oboru názvů. Také jsme se dotkli jmenného prostoru Windows.Media.Core, protože item->VideoTracks->GetAt(0) vrací Windows.Media.Core.VideoTrack. Nemuseli jsme ale zadávat název VideoTrack kamkoliv, takže jsme nepotřebovali direktivu using Windows.Media.Core.

C++/WinRT ale vyžaduje, abyste zahrnuli soubor hlaviček odpovídající každému použitelnému oboru názvů, 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, přestože je událost MediaPlaybackItem.AudioTracksChanged typu TypedEventHandler<MediaPlaybackItem, Windows.Foundation.Collections.IVectorChangedEventArgs>, nemusíme zahrnout winrt/Windows.Foundation.Collections.h, protože jsme tuto událost nepoužili.

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"/>

Použití třídy Rectangle znamená, že musíte přidat toto include.

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

Pokud zapomenete hlavičkový soubor, všechno se zkompiluje, ale zobrazí se chyby při linkování, protože chybí třídy consume_.

Předávání parametrů

Při psaní zdrojového kódu v C++/CX předáváte typy C++/CX jako parametry funkce ve formě odkazů „hat“ (^).

void LogPresenceRecord(PresenceRecord^ record);

V jazyce C++/WinRT byste pro synchronní funkce měli ve výchozím nastavení použít parametry const&. Tím se zabrání kopiím a vzájemnému překrývání režie. Korutiny by však měly používat předávání hodnotou, aby se zajistilo, že zachytávají podle hodnoty a vyhýbají se 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 použijete proměnné hat (^) k odkazování na objekty prostředí Windows Runtime a operátor šipky (->) pro dereference proměnné hat.

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

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

Při přenosu do ekvivalentního kódu C++/WinRT můžete získat dlouhou cestu odebráním klobouků a změnou operátoru šipky (->) na operátor tečky (.). 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 pro odkaz C++/CX hat inicializuje 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 to neodkazuje na TextBlock; chceme později přiřadit odkaz.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

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

Vlastnosti

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 XboxUserId, UserState, PresenceDeviceRecordsa Size jsou všechny vlastnosti.

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 vrátí objekt prostředí Windows Runtime, který má funkci Size. Jelikož vrácený objekt je také projektovaný typ C++/WinRT, používáme operátor tečky k volání metody 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 tzv. hat (^) reference. Pomocí klíčového slova ref new vytvoříte nový objekt, který pak volá RoActivateInstance aktivovat novou instanci 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 přidělit v zásobníku nebo jako pole objektu. K přidělení objektu C++/WinRT nikdy použít ref new (ani new). 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 odkaz C++/CX hat inicializuje ho na hodnotu 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 Opož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ů.

Scénář 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 };
Dvojice 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 Platform::Array^ (viz Port Platform::Array^) v jazyce C++/CX, máte možnost převést ji na std::vector v C++/WinRT (nebo na jakýkoli jiný souvislý kontejner) místo ponechání jako pole. Existují výhody výběru std::vector.

Pokud například 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ů. Musíte 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 [] dolního indexu (subscript) pro `std::map` se chová takto.

  • Pokud se klíč nachází v mapě, vraťte odkaz na existující hodnotu (kterou můžete přepsat).
  • Pokud klíč v mapě nenajdete, 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í konstruovanou hodnotua vrátí odkaz na hodnotu (kterou pak můžete přepsat).

Jinými slovy, operátor [] vždy vytvoří položku v mapě. Liší se 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í typ, o kterém víte, že odkazuje na objekt odvozeného typu. V jazyce C++/CX použijete dynamic_cast k přetypování odkaz na základ do odvozeného odkazu. dynamic_cast je skutečně jen skryté volání QueryInterface. Tady je typický příklad – zpracováváte změnu události vlastnosti závislosti a chcete přetypovat objekt DependencyObject zpět na původní typ, který vlastní tuto vlastnost závislosti.

void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject^ d, Windows::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 metody IUnknown::try_as, která zapouzdřuje QueryInterface. Máte také možnost volat IUnknown::as, místo toho vyvolá výjimku, pokud dotazování na požadované rozhraní (výchozí rozhraní typu, který požadujete) se nevrátí. Tady je příklad kódu C++/WinRT.

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

Odvozené třídy

Aby bylo možné odvodit běhovou třídu, musí být základní třída kompozitelná. C++/CX nevyžaduje provedení žádných speciálních kroků, aby vaše třídy byly kompozibilní, ale C++/WinRT dělá. Pomocí nezapečetěného klíčového slova označíte, že chcete, aby vaše třída byla použitelná jako základní třída.

unsealed runtimeclass BasePage : Windows.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 funkce lambda se můžete rozhodnout implementovat svého delegáta jako samostatnou funkci nebo jako ukazatel na členovou funkci. Další informace najdete v tématu Zpracování událostí pomocí delegátů v jazyce C++/WinRT.

Pokud přenášíte ze základu kódu C++/CX, kde se události a delegáty používají interně (ne mezi binárními soubory), winrt::d elegate vám pomůže tento vzor replikovat v jazyce C++/WinRT. Viz také parametrizované delegáty, jednoduché signály a zpětná volání v 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í. Vizte boxování a unboxování s 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;
Operace 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 programátorskou 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 je.

Scénář 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í objektový int Platform::InvalidCastException Havárie
Rozbalit int, použít záložní možnost, pokud je hodnota null; ukončit aplikaci, pokud je to něco jiného. i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Pokud je to možné, rozbalte int; použijte náhradní možnost pro cokoli jiného auto box = dynamic_cast<IBox<int>^>(o);
i = box ? box->Value : fallback;
i = unbox_value_or<int>(o, fallback);

Zabalování a rozbalování ř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 z IInspectable, takže technicky to není objekt. Kromě toho nulový HSTRING představuje prázdný řetězec. Zabalení objektů, které nejsou odvozeny z IInspectable, se provádí jejich vložením do IReference<T>. Windows Runtime poskytuje standardní implementaci ve formě objektu PropertyValue (vlastní typy se hlásí jako PropertyType::OtherType).

C++/CX představuje řetězec modulu Windows Runtime jako referenční typ; zatímco C++/WinRT projektuje řetězec jako typ hodnoty. To znamená, že obalený nulový řetězec může mít různé reprezentace v závislosti na tom, jak jste se k němu dostali.

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

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

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

Operace C++/CX C++/WinRT
Zabalit řetězec do krabice 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 není řetězec.
s = unbox_value<hstring>(o);
Pád způsobený objektem null.
Selže, pokud není řetězec.
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);
Null nebo jiná než textová hodnota se změní na záložní hodnotu.
Prázdný řetězec se zachová.

Souběžnost a asynchronní operace

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

Pro C++/WinRT byste měli místo toho 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 XAML značek

V projektu C++/CX můžete využívat soukromé členy a pojmenované elementy z kódu XAML. V jazyce C++/WinRT musí být ale všechny entity používané pomocí rozšíření XAML {x:Bind} veřejně zpřístupněny v IDL.

Vazba na logickou hodnotu také zobrazuje true nebo false v C++/CX, ale zobrazuje Windows.Foundation.IReference`1<logická hodnota> v C++/WinRT.

Další informace a příklady kódu viz Využívání objektů z markupu.

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

C++/CX poskytuje několik datových typů v oboru názvů Platform. Tyto typy nejsou součástí standardu C++, takže je můžete použít pouze, když povolíte rozšíření jazyka Windows Runtime (vlastnost projektu sady Visual Studio C/C++>Obecné>Použití rozšíření Windows Runtime>Ano (/ZW)). Následující tabulka vám pomůže portovat z typů platformy na jejich ekvivalenty v C++/WinRT. Jakmile to uděláte, protože C++/WinRT je standardní jazyk C++, můžete vypnout možnost /ZW.

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

Port Platforma::Agile^ na winrt::agile_ref

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

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

Platforma portu::Array^

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

Kdykoli tedy máte Platform::Array^ v jazyce C++/CX, vaše možnosti přenosu zahrnují použití seznamu inicializátorů, std::arraynebo std::vector. Další informace a příklady kódu najdete v seznamu inicializátorů standardu a standardních polí a vektorů.

Port Platform::Exception^ na winrt::hresult_error

Typ Platform::Exception^ je vytvořen v jazyce C++/CX, pokud rozhraní API prostředí Windows Runtime vrátí jiný než S_OK HRESULT. Ekvivalent C++/WinRT je winrt::hresult_error.

Pokud chcete portovat do C++/WinRT, změňte veškerý kód, který používá Platform::Exception^ na použití 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 volání hresult_error::to_abi
winrt::hresult_access_denied Chyba WinRT::HResult E_ACCESSDENIED
winrt::hresult_canceled Chyba WinRT::HResult ERROR_ZRUŠENO
winrt::hresult_changed_state Chyba WinRT::HResult E_CHANGED_STATE
winrt::hresult_class_not_available Chyba WinRT::HResult CLASS_E_CLASSNOTAVAILABLE (Třída není k dispozici)
winrt::hresult_illegal_delegate_assignment Chyba WinRT::HResult E_NELEGÁLNÍ_PŘIDĚLENÍ_DELEGÁTA
winrt::hresult_illegal_method_call Chyba WinRT::HResult E_NEPOVOLANÉ_VOLÁNÍ_METODY
winrt::hresult_illegal_state_change Chyba WinRT::HResult E_ILLEGAL_STATE_CHANGE (Nelegální změna stavu)
winrt::hresult_invalid_argument Chyba WinRT::HResult Chyba: Neplatný argument
winrt::hresult_no_interface Chyba WinRT::HResult E_NOINTERFACE
winrt::hresult_not_implemented Chyba WinRT::HResult E_NOTIMPL (Není implementováno)
winrt::hresult_out_of_bounds Chyba WinRT::HResult E_BOUNDS
winrt::hresult_wrong_thread Chyba WinRT::HResult 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 zprávu funkce, která vrací řetězcovou reprezentaci této funkce 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" };

Port Platform::Object^ do winrt::Windows::Foundation::IInspectable

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

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

Port Platform::String^ na winrt::hstring

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

V C++/CX můžete získat přístup k vlastnosti Platform::String::Data, abyste načetli řetězec jako C-style const wchar_t* pole (například pro předání do std::wcout).

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

Pokud chcete stejný postup 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řebírají nebo vracejí řetězce, obvykle změníte jakýkoli kód C++/CX, který používá Platform::String^, aby používal místo toho winrt::hstring.

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

void LogWrapLine(Platform::String^ str);

Pro C++/WinRT můžete deklarovat toto rozhraní API 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řeveď int na řetězec Stringifikace výčtového typu
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);

V případě převedení 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 znázornění řetězců jsou často implicitně zpracovávány datovými vazbami.

<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 používají standardní std::wstringstream pro vytváření řetězců.

Operace C++/CX C++/WinRT
Připojit řetězec se zachováním nulových hodnot 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 nedělá.

Operace C++/CX C++/WinRT
Vytvoření řetězce 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 při prvním nulu 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řevést na std::wstring, zachování nulových hodnot 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 při prvním „null“ 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ředání std::wstring do metody 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