Interoperabilitás a C++/WinRT és C++/CX között

A témakör elolvasása előtt szüksége lesz a A C++/CX-ről a C++/WinRT-re való áttérés című témakörben található információkra. Ez a témakör két fő stratégiai lehetőséget mutat be a C++/CX projekt C+++/WinRT-be történő átviteléhez.

  • Portolja a teljes projektet egyetlen lépésben. Egy nem túl nagy projekt legegyszerűbb lehetősége. Ha Windows-futtatókörnyezet összetevőprojektje van, akkor ez a stratégia az egyetlen lehetőség.
  • A projektet fokozatosan portolhatja (a kódbázis mérete vagy összetettsége ezt szükségessé teheti). Ez a stratégia azonban arra kéri, hogy kövessen egy portálási folyamatot, amelyben egy ideig a C++/CX és a C++/WinRT kód egymás mellett létezik ugyanabban a projektben. XAML-projekt esetén egy adott időpontban a XAML-oldaltípusoknak vagy mind C++/WinRT-, vagy mind C++/CX-típusúnak kell lenniük.

Ez az interoperabilitási témakör ehhez a második stratégiához kapcsolódik – azokban az esetekben, amikor fokozatosan kell átültetnie a projektjét. Ez a témakör a C++/CX objektumok (és más típusok) C++/WinRT objektummá alakításához használható segédfüggvények különböző formáit mutatja be (és fordítva).

Ezek a segédfüggvények nagyon hasznosak lesznek, mivel a kódot fokozatosan portozza C++/CX-ről C++/WinRT-be. Vagy dönthet úgy is, hogy ugyanabban a projektben a C++/WinRT és a C++/CX nyelvi projekciót is használja, akár éppen portol, akár nem, és ezekkel a segédfüggvényekkel interoperabilitást biztosít a kettő között.

A témakör elolvasása után további információ- és kódpéldákat találhat, amelyek bemutatják, hogyan támogathatja a PPL-feladatokat és a coroutine-okat egymás mellett ugyanabban a projektben (például a coroutinest a tevékenységláncokból hívhatja meg), tekintse meg a fejlettebb Aszinkron témakört, valamint a C++/WinRT és a C+++/CX közötti együttműködéseket.

A from_cx és to_cx függvények

Íme egy interop_helpers.h nevű fejlécfájl forráskódlistája, amely különféle konverziós segédfüggvényeket tartalmaz. Ahogy fokozatosan portozza a projektet, a C++/CX- és a C++/WinRT-be portolt részek is megmaradnak. Ezeket a segédfüggvényeket arra használhatja, hogy objektumokat (és más típusokat) oda-vissza konvertáljon a projektben a C++/CX és a C++/WinRT között, e két rész közötti illesztési pontokon.

A kódlistát követő szakaszok ismertetik a segédfüggvényeket, valamint a projekt fejlécfájljának létrehozását és használatát.

// interop_helpers.h
#pragma once

template <typename T>
T from_cx(Platform::Object^ from)
{
    T to{ nullptr };

    if (from != nullptr)
    {
        winrt::check_hresult(reinterpret_cast<::IUnknown*>(from)
            ->QueryInterface(winrt::guid_of<T>(), winrt::put_abi(to)));
    }

    return to;
}

template <typename T>
T^ to_cx(winrt::Windows::Foundation::IUnknown const& from)
{
    return safe_cast<T^>(reinterpret_cast<Platform::Object^>(winrt::get_abi(from)));
}

inline winrt::hstring from_cx(Platform::String^ const& from)
{
    return reinterpret_cast<winrt::hstring&>(const_cast<Platform::String^&>(from));
}

inline Platform::String^ to_cx(winrt::hstring const& from)
{
    return reinterpret_cast<Platform::String^&>(const_cast<winrt::hstring&>(from));
}

inline winrt::guid from_cx(Platform::Guid const& from)
{
    return reinterpret_cast<winrt::guid&>(const_cast<Platform::Guid&>(from));
}

inline Platform::Guid to_cx(winrt::guid const& from)
{
    return reinterpret_cast<Platform::Guid&>(const_cast<winrt::guid&>(from));
}

A from_cx függvény

A from_cx segédfüggvény C++/CX objektumot konvertál egyenértékű C++/WinRT objektummá. A függvény egy C++/CX objektumot vet a mögöttes IUnknown felület mutatójára. Ezután meghívja a QueryInterface-t arra a mutatóra, hogy lekérdezhesse a C++/WinRT objektum alapértelmezett felületét. A QueryInterface a C++/CX safe_cast kiterjesztés Windows-futtatókörnyezet alkalmazásbináris felületi (ABI) megfelelője. A winrt::p ut_abi függvény pedig lekéri egy C++/WinRT-objektum mögöttes IUnknown felületi mutatójának címét, hogy az másik értékre legyen állítva.

A to_cx függvény

A to_cx segédfüggvény egy C++/WinRT objektumot egyenértékű C++/CX objektummá konvertál. A winrt::get_abi függvény egy C++/WinRT-objektum mögöttes IUnknown felületére mutató mutatót kér le. A függvény először C++/CX objektummá alakítja a mutatót, majd a C++/CX safe_cast kiterjesztés használatával lekérdezi a kért C++/CX típust.

A interop_helpers.h fejlécfájl

A projekt segédfüggvényeinek használatához kövesse az alábbi lépéseket.

  • Adjon hozzá egy új fejlécfájlt (.h) a projekthez, és nevezze el.interop_helpers.h
  • Cserélje le a(z) interop_helpers.h tartalmát a fenti kódlistában szereplő kódra.
  • Adja hozzá ezeket a bővítményeket a következőhöz pch.h:
// pch.h
...
#include <unknwn.h>
// Include C++/WinRT projected Windows API headers here.
...
#include <interop_helpers.h>

C++/CX projekt készítése és C++/WinRT-támogatás hozzáadása

Ez a szakasz azt ismerteti, hogy mi a teendő, ha úgy döntött, hogy a meglévő C++/CX projektet használja, hozzáadja a C++/WinRT-támogatást, és ott végzi a portolást. Lásd még Visual Studio C++/WinRT támogatását.

A C++/CX és a C++/WinRT C++/CX projektben való kombinálásához – beleértve a projekt from_cx és to_cx segédfüggvényeinek használatát is – manuálisan kell hozzáadnia a C++/WinRT-támogatást a projekthez.

Először nyissa meg a C++/CX projektet a Visual Studio, és győződjön meg arról, hogy a Project Property General>Target Platform Version értéke 10.0.17134.0 (Windows 10, 1803-es verzió) vagy újabb.

A C++/WinRT NuGet csomag telepítése

A Microsoft.Windows. A CppWinRT NuGet-csomag C++/WinRT buildtámogatást biztosít (MSBuild tulajdonságok és célok). A telepítéshez kattintson a Project>Manage NuGet Packages...>Browse menüpontra, írja be vagy illessze be a(z) Microsoft.Windows.CppWinRT nevet a keresőmezőbe, válassza ki az elemet a keresési eredmények közül, majd kattintson a Install gombra a csomag adott projekthez való telepítéséhez.

Fontos

A C++/WinRT NuGet csomag telepítése miatt a C++/CX támogatása ki van kapcsolva a projektben. Ha egy menetben szeretné elvégezni a portolást, akkor érdemes kikapcsolva hagyni ezt a támogatást, hogy az összeállítási üzenetek segítsenek megtalálni (és átportolni) a C++/CX-re támaszkodó összes függőséget (így a korábban tisztán C++/CX-projekt végül tisztán C++/WinRT-projektté válik). A visszakapcsolásról a következő szakaszban olvashat.

A C++/CX támogatás visszakapcsolása

Ha egyetlen lépésben portolsz, akkor ezt nem kell megtenned. Ha azonban fokozatosan kell portolnia, akkor ezen a ponton újra be kell kapcsolnia a C++/CX támogatást a projektben. A projekt tulajdonságainál a C/C++>Általános>Windows-futtatókörnyezet-bővítmény használata>Igen (/ZW)).

Másik lehetőségként (vagy XAML-projekt esetén) C++/CX-támogatást is hozzáadhat a C++/WinRT projekttulajdonságok lapján a Visual Studio. A projekt tulajdonságainál, Általános tulajdonságok>C++/WinRT>Projekt nyelve>C++/CX. Ezzel hozzáadja a következő tulajdonságot a .vcxproj fájlhoz.

  <PropertyGroup Label="Globals">
    <CppWinRTProjectLanguage>C++/CX</CppWinRTProjectLanguage>
  </PropertyGroup>

Fontos

Amikor a Midl-fájl (.idl) tartalmát csonkfájlokká való feldolgozáshoz fordítania kell, vissza kell állítania a Projekt nyelve beállítást C++/WinRT értékre. Miután a fordítás létrehozta ezeket a stubokat, módosítsa a Projekt nyelve beállítást vissza C++/CX értékre.

A cppwinrt.exe eszköz működését finomhangoló hasonló testreszabási lehetőségek listáját lásd a Microsoft.Windows.CppWinRT NuGet-csomag readme fájljában.

C++/WinRT-fejlécfájlok belefoglalása

Legalább annyit tegyen meg, hogy a precompiled header fájlba (általában pch.h) felveszi a winrt/base.h elemet, az alábbiak szerint.

// pch.h
...
#include <winrt/base.h>
...

De szinte biztosan szüksége lesz a winrt::Windows::Foundation névtér típusára. És lehet, hogy már tud más névterekről, amelyekre szüksége lesz. Tehát a következőképpen foglalja bele az ezeknek a névtereknek megfelelő, a C++/WinRT által leképezett Windows API-fejléceket (a winrt/base.h elemet most már nem kell külön belefoglalnia, mert a rendszer automatikusan belefoglalja).

// pch.h
...
#include <winrt/Windows.Foundation.h>
// Include any other C++/WinRT projected Windows API headers here.
...

Lásd még a következő szakaszban található kódpéldát (Egy C++/WinRT-projekt átalakítása és a C++/CX támogatás hozzáadása), amely a namespace cx és namespace winrt névtéraliasokat használó technikát mutat be. Ez a technika lehetővé teszi a C++/WinRT-vetítés és a C++/CX-vetítés közötti esetleges névtérütközések kezelését.

Adja hozzá interop_helpers.h a projekthez

Mostantól hozzáadhatja a from_cx és to_cx függvényeket a C++/CX projekthez. Ehhez útmutatást a fenti from_cx és to_cx függvények szakaszában talál.

C++/WinRT-projekt készítése és C++/CX-támogatás hozzáadása

Ez a szakasz azt ismerteti, hogy mi a teendő, ha úgy döntött, hogy létrehoz egy új C++/WinRT-projektet, és ott végzi a portolást.

A C++/WinRT és a C++/CX egy C++/WinRT-projektben való kombinálásához – beleértve a projekt from_cx és to_cx segédfüggvényeinek használatát is – manuálisan kell hozzáadnia a C++/CX támogatást a projekthez.

  • Hozzon létre egy új C++/WinRT-projektet Visual Studio a C++/WinRT projektsablonok egyikével (lásd Visual Studio C++/WinRT támogatásával).
  • A C++/CX projekttámogatásának bekapcsolása. A projekt tulajdonságainál a C/C++>Általános>Windows-futtatókörnyezet bővítmény használata>Igen (/ZW).

Példa C++/WinRT projektre, amely a két használt segédfüggvényt mutatja be

Ebben a szakaszban létrehozhat egy C++/WinRT-projektet, amely bemutatja, hogyan használható from_cx és to_cx. Azt is bemutatja, hogyan használhat névtér-aliasokat a kód különböző szigeteihez, hogy kezelni tudja a C++/WinRT-vetítés és a C++/CX-előrejelzés közötti esetleges névtér-ütközéseket.

  • Hozzon létre egy Visual C++>Windows Universal>Core App (C++/WinRT) projektet.
  • A projekt tulajdonságainál: C/C++>General>Consume Windows-futtatókörnyezet Extension>Yes (/ZW).
  • Adja hozzá a(z) interop_helpers.h elemet a projekthez. Ehhez útmutatást a fenti from_cx és to_cx függvények szakaszában talál.
  • Cserélje le a App.cpp tartalmát az alábbi kódra.
  • Építés és futtatás.

WINRT_ASSERT egy makródefiníció, és _ASSERTE elemre bővül.

// App.cpp
#include "pch.h"
#include <sstream>

namespace cx
{
    using namespace Windows::Foundation;
}

namespace winrt
{
    using namespace Windows;
    using namespace Windows::ApplicationModel::Core;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Numerics;
    using namespace Windows::UI;
    using namespace Windows::UI::Core;
    using namespace Windows::UI::Composition;
}

struct App : winrt::implements<App, winrt::IFrameworkViewSource, winrt::IFrameworkView>
{
    winrt::CompositionTarget m_target{ nullptr };
    winrt::VisualCollection m_visuals{ nullptr };
    winrt::Visual m_selected{ nullptr };
    winrt::float2 m_offset{};

    winrt::IFrameworkView CreateView()
    {
        return *this;
    }

    void Initialize(winrt::CoreApplicationView const &)
    {
    }

    void Load(winrt::hstring const&)
    {
    }

    void Uninitialize()
    {
    }

    void Run()
    {
        winrt::CoreWindow window = winrt::CoreWindow::GetForCurrentThread();
        window.Activate();

        winrt::CoreDispatcher dispatcher = window.Dispatcher();
        dispatcher.ProcessEvents(winrt::CoreProcessEventsOption::ProcessUntilQuit);
    }

    void SetWindow(winrt::CoreWindow const & window)
    {
        winrt::Compositor compositor;
        winrt::ContainerVisual root = compositor.CreateContainerVisual();
        m_target = compositor.CreateTargetForCurrentView();
        m_target.Root(root);
        m_visuals = root.Children();

        window.PointerPressed({ this, &App::OnPointerPressed });
        window.PointerMoved({ this, &App::OnPointerMoved });

        window.PointerReleased([&](auto && ...)
        {
            m_selected = nullptr;
        });
    }

    void OnPointerPressed(IInspectable const &, winrt::PointerEventArgs const & args)
    {
        winrt::float2 const point = args.CurrentPoint().Position();

        for (winrt::Visual visual : m_visuals)
        {
            winrt::float3 const offset = visual.Offset();
            winrt::float2 const size = visual.Size();

            if (point.x >= offset.x &&
                point.x < offset.x + size.x &&
                point.y >= offset.y &&
                point.y < offset.y + size.y)
            {
                m_selected = visual;
                m_offset.x = offset.x - point.x;
                m_offset.y = offset.y - point.y;
            }
        }

        if (m_selected)
        {
            m_visuals.Remove(m_selected);
            m_visuals.InsertAtTop(m_selected);
        }
        else
        {
            AddVisual(point);
        }
    }

    void OnPointerMoved(IInspectable const &, winrt::PointerEventArgs const & args)
    {
        if (m_selected)
        {
            winrt::float2 const point = args.CurrentPoint().Position();

            m_selected.Offset(
            {
                point.x + m_offset.x,
                point.y + m_offset.y,
                0.0f
            });
        }
    }

    void AddVisual(winrt::float2 const point)
    {
        winrt::Compositor compositor = m_visuals.Compositor();
        winrt::SpriteVisual visual = compositor.CreateSpriteVisual();

        static winrt::Color colors[] =
        {
            { 0xDC, 0x5B, 0x9B, 0xD5 },
            { 0xDC, 0xED, 0x7D, 0x31 },
            { 0xDC, 0x70, 0xAD, 0x47 },
            { 0xDC, 0xFF, 0xC0, 0x00 }
        };

        static unsigned last = 0;
        unsigned const next = ++last % _countof(colors);
        visual.Brush(compositor.CreateColorBrush(colors[next]));

        float const BlockSize = 100.0f;

        visual.Size(
        {
            BlockSize,
            BlockSize
        });

        visual.Offset(
        {
            point.x - BlockSize / 2.0f,
            point.y - BlockSize / 2.0f,
            0.0f,
        });

        m_visuals.InsertAtTop(visual);

        m_selected = visual;
        m_offset.x = -BlockSize / 2.0f;
        m_offset.y = -BlockSize / 2.0f;
    }
};

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    winrt::init_apartment();

    winrt::Uri uri(L"http://aka.ms/cppwinrt");
    std::wstringstream wstringstream;
    wstringstream << L"C++/WinRT: " << uri.Domain().c_str() << std::endl;

    // Convert from a C++/WinRT type to a C++/CX type.
    cx::Uri^ cx = to_cx<cx::Uri>(uri);
    wstringstream << L"C++/CX: " << cx->Domain->Data() << std::endl;
    ::OutputDebugString(wstringstream.str().c_str());

    // Convert from a C++/CX type to a C++/WinRT type.
    winrt::Uri uri_from_cx = from_cx<winrt::Uri>(cx);
    WINRT_ASSERT(uri.Domain() == uri_from_cx.Domain());
    WINRT_ASSERT(uri == uri_from_cx);

    winrt::CoreApplication::Run(winrt::make<App>());
}

Fontos API-k