Interoperabiliteit tussen C++/WinRT en C++/CX

Voordat u dit onderwerp leest, hebt u de informatie in het onderwerp Verplaatsen naar C++/WinRT van C++/CX nodig. In dit onderwerp worden twee belangrijke strategieopties geïntroduceerd voor het overzetten van uw C++/CX-project naar C++/WinRT.

  • Het hele project in één pas overzetten. De eenvoudigste optie voor een project dat niet te groot is. Als u een Windows Runtime onderdeelproject hebt, is deze strategie uw enige optie.
  • Het project geleidelijk overzetten (de grootte of complexiteit van uw codebasis kan dit nodig maken). Maar deze strategie vraagt u om een overdrachtsproces te volgen waarin C++/CX- en C++/WinRT-code naast elkaar in hetzelfde project bestaan. Voor een XAML-project moeten uw XAML-paginatypen op een bepaald moment ofwel allemaal van het type C++/WinRT of allemaal van het type C++/CX zijn.

Dit interoperabiliteitsonderwerp is relevant voor die tweede strategie, voor gevallen waarin u uw project geleidelijk moet overzetten. In dit onderwerp ziet u verschillende vormen van helperfuncties die u kunt gebruiken om een C++/CX-object (en andere typen) te converteren naar een C++/WinRT-object (en omgekeerd) binnen hetzelfde project.

Deze helperfuncties zijn erg nuttig wanneer u uw code geleidelijk van C++/CX naar C++/WinRT overdrat. U kunt er ook voor kiezen om zowel de C++/WinRT- als C++/CX-taalprojecties in hetzelfde project te gebruiken, ongeacht of u nu porteert of niet, en deze helperfuncties gebruiken om tussen de twee te werken.

Na het lezen van dit onderwerp, voor informatie- en codevoorbeelden die laten zien hoe U PPL-taken en coroutines naast elkaar in hetzelfde project kunt ondersteunen (bijvoorbeeld het aanroepen van coroutines uit taakketens), raadpleegt u het geavanceerdere onderwerp Asynchroon en interop tussen C++/WinRT en C++/CX.

De functies from_cx en to_cx

Hier volgt een lijst met broncode van een headerbestand met de naam interop_helpers.h, met verschillende helperfuncties voor conversie. Wanneer u uw project geleidelijk overdrat, zijn er onderdelen nog steeds in C++/CX en onderdelen die u hebt overgezet naar C++/WinRT. U kunt deze helperfuncties gebruiken om objecten (en andere typen) te converteren naar en van C++/CX en C++/WinRT in uw project op de grenspunten tussen deze twee delen.

In de secties die de codevermelding volgen, worden de helperfuncties uitgelegd en wordt uitgelegd hoe u het headerbestand in uw project maakt en gebruikt.

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

De functie from_cx

De from_cx helperfunctie converteert een C++/CX-object naar een equivalent C++/WinRT-object. De functie cast een C++/CX-object naar de onderliggende IUnknown interface pointer. Vervolgens wordt QueryInterface op die aanwijzer aangeroepen om een query uit te voeren op de standaardinterface van het C++/WinRT-object. QueryInterface is het Windows Runtime ABI-equivalent (Application Binary Interface) van de C++/CX-extensiesafe_cast. En met de functie winrt::put_abi wordt het adres van de onderliggende IUnknown-interfacepointer van een C++/WinRT-object opgehaald, zodat deze op een andere waarde kan worden ingesteld.

De functie to_cx

De to_cx helperfunctie converteert een C++/WinRT-object naar een equivalent C++/CX-object. De functie winrt::get_abi haalt een aanwijzer op naar de onderliggende IUnknown-interface van een C++/WinRT-object. De functie cast die aanwijzer naar een C++/CX-object voordat die de C++/CX-extensie safe_cast gebruikt om het gevraagde C++/CX-type op te vragen.

Het interop_helpers.h headerbestand

Volg deze stappen om de helperfuncties in uw project te gebruiken.

  • Voeg een nieuw koptekstbestand (.h) toe aan uw project en geef het een interop_helpers.hnaam.
  • Vervang de inhoud van interop_helpers.h door het bovenstaande codefragment.
  • Voeg deze insluitingen toe aan pch.h.
// pch.h
...
#include <unknwn.h>
// Include C++/WinRT projected Windows API headers here.
...
#include <interop_helpers.h>

Een C++/CX-project maken en C++/WinRT-ondersteuning toevoegen

In deze sectie wordt beschreven wat u moet doen als u hebt besloten om uw bestaande C++/CX-project te gebruiken, C++/WinRT-ondersteuning toe te voegen en daar uw overdrachtswerk uit te voeren. Zie ook Visual Studio ondersteuning voor C++/WinRT.

Als u C++/CX en C++/WinRT wilt combineren in een C++/CX-project, inclusief het gebruik van de from_cx - en to_cx helperfuncties in het project, moet u handmatig C++/WinRT-ondersteuning toevoegen aan het project.

Open eerst uw C++/CX-project in Visual Studio en controleer of de projecteigenschap General>Target Platform Version is ingesteld op 10.0.17134.0 (Windows 10, versie 1803) of hoger.

Het C++/WinRT NuGet-pakket installeren

De Microsoft.Windows. CppWinRT NuGet-pakket biedt C++/WinRT-buildondersteuning (MSBuild-eigenschappen en -doelen). Om het te installeren, klikt u op de menuoptie Project>Manage NuGet Packages...>Browse, typt of plakt u Microsoft.Windows.CppWinRT in het zoekvak, selecteert u het item in de zoekresultaten en klikt u vervolgens op Install om het pakket voor dat project te installeren.

Belangrijk

Als u het C++/WinRT NuGet-pakket installeert, wordt ondersteuning voor C++/CX uitgeschakeld in het project. Als u in één keer gaat migreren, is het een goed idee om die ondersteuning uitgeschakeld te laten, zodat buildmeldingen u helpen al uw afhankelijkheden van C++/CX te vinden (en over te zetten), waardoor wat eerst een puur C++/CX-project was uiteindelijk een puur C++/WinRT-project wordt. Maar zie de volgende paragraaf voor informatie over hoe u dit weer inschakelt.

C++/CX-ondersteuning weer inschakelen

Als u in één pas porteert, hoeft u dit niet te doen. Maar als u geleidelijk moet overzetten, moet u op dit moment C++/CX-ondersteuning weer inschakelen in uw project. Ga in projecteigenschappen naar C/C++>Algemeen>Windows Runtime-extensie gebruiken>Ja (/ZW)).

U kunt ook (of voor een XAML-project) C++/CX-ondersteuning toevoegen met behulp van de eigenschappenpagina C++/WinRT-project in Visual Studio. In projecteigenschappen, Algemene eigenschappen>C++/WinRT>Projecttaal>C++/CX. Hiermee voegt u de volgende eigenschap toe aan uw .vcxproj bestand.

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

Belangrijk

Wanneer u moet bouwen om de inhoud van een Midl-bestand (.idl) te verwerken in stub-bestanden, moet u Project Taal weer wijzigen in C++/WinRT. Nadat de build deze stubs heeft gegenereerd, wijzig Project Language terug naar C++/CX.

Zie de readme van het Microsoft.Windows.CppWinRT NuGet-pakket voor een lijst met vergelijkbare aanpassingsopties (die het gedrag van het hulpprogramma cppwinrt.exe nauwkeuriger afstemmen).

C++/WinRT-headerbestanden opnemen

Het minste wat u moet doen, is in uw precompiled-headerbestand (meestal pch.h) winrt/base.h opnemen, zoals hieronder weergegeven.

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

Maar u hebt vrijwel zeker de typen in de winrt::Windows::Foundation-naamruimte nodig. En misschien weet u al van andere naamruimten die u nodig hebt. Neem dus de C++/WinRT projected Windows API-headers op die overeenkomen met die naamruimten zoals deze (u hoeft nu niet expliciet op te nemen winrt/base.h omdat deze automatisch voor u wordt opgenomen).

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

Zie ook het codevoorbeeld in de volgende sectie (Een C++/WinRT-project maken en C++/CX-ondersteuning toevoegen) voor een techniek met behulp van de naamruimtealiassen namespace cx en namespace winrt. Met deze techniek kunt u omgaan met andere mogelijke naamruimteconflicten tussen de C++/WinRT-projectie en de C++/CX-projectie.

Voeg interop_helpers.h toe aan het project

U kunt nu de functies from_cx en to_cx toevoegen aan uw C++/CX-project. Zie de sectie from_cx en to_cx functies hierboven voor instructies.

Een C++/WinRT-project maken en C++/CX-ondersteuning toevoegen

In deze sectie wordt beschreven wat u moet doen als u hebt besloten een nieuw C++/WinRT-project te maken en uw overdrachtswerk daar uit te voeren.

Als u C++/WinRT en C++/CX wilt combineren in een C++/WinRT-project, inclusief het gebruik van de from_cx - en to_cx helperfuncties in het project, moet u handmatig C++/CX-ondersteuning toevoegen aan het project.

  • Maak een nieuw C++/WinRT-project in Visual Studio met behulp van een van de C++/WinRT-projectsjablonen (zie Visual Studio ondersteuning voor C++/WinRT).
  • Schakel projectondersteuning in voor C++/CX. Ga in Projecteigenschappen naar C/C++>Algemeen>Windows Runtime-extensie gebruiken>Ja (/ZW).

Een voorbeeld van een C++/WinRT-project met de twee helperfuncties die in gebruik zijn

In deze sectie kunt u een voorbeeld van een C++/WinRT-project maken dat laat zien hoe u from_cx en to_cx gebruikt. Het laat ook zien hoe u naamruimtealiassen kunt gebruiken voor de verschillende codeeilanden om andere mogelijke naamruimteconflicten tussen de C++/WinRT-projectie en de C++/CX-projectie af te handelen.

  • Maak een Visual C++>Windows Universal>Core App (C++/WinRT)-project.
  • Ga in de projecteigenschappen naar C/C++>Algemeen>Windows Runtime-extensie gebruiken>Ja (/ZW).
  • Voeg interop_helpers.h toe aan het project. Zie de sectie from_cx en to_cx functies hierboven voor instructies.
  • Vervang de inhoud van App.cpp door de onderstaande codevermelding.
  • Bouwen en uitvoeren.

WINRT_ASSERT is een macrodefinitie en wordt uitgebreid naar _ASSERTE.

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

Belangrijke API's