Interoperabilità tra C++/WinRT e C++/CXInterop between C++/WinRT and C++/CX

Prima di leggere questo argomento, sono necessarie le informazioni dell'argomento Passare a C++/WinRT da C++/CX.Before reading this topic, you'll need the info in the topic Move to C++/WinRT from C++/CX. Questo argomento introduce due opzioni di strategia principali per convertire il progetto C++/CX in C++/WinRT.That topic introduces two main strategy options for porting your C++/CX project to C++/WinRT.

  • Convertire l'intero progetto in un unico passaggio.Port the entire project in one pass. Si tratta dell'opzione più semplice per un progetto non troppo esteso.The simplest option for a project that's not too large. Se si dispone di un progetto di componente Windows Runtime, questa strategia costituisce l'unica opzione possibile.If you have a Windows Runtime component project, then this strategy is your only option.
  • Convertire il progetto gradualmente (la dimensione o la complessità della base di codici potrebbe rendere necessaria questa strategia).Port the project gradually (the size or complexity of your codebase might make this necessary). Questa strategia, tuttavia, richiede l'esecuzione di un processo di conversione in cui, per un periodo di tempo, i codici C++/CX e C++/WinRT coesistono nello stesso progetto.But this strategy calls for you to follow a porting process in which for a time C++/CX and C++/WinRT code exists side by side in the same project. Per un progetto XAML, in un determinato momento, i tipi di pagina XAML devono essere o tutti C++/WinRT o tutti C++/CX.For a XAML project, at any given time, your XAML page types must be either all C++/WinRT or all C++/CX.

Questo argomento sull'interoperabilità è pertinente per la seconda strategia: nei casi in cui è necessario convertire gradualmente il progetto.This interop topic is relevant for that second strategy—for cases when you need to port your project gradually. Questo argomento illustra due funzioni helper che è possibile usare per convertire un oggetto C++/CX in un oggetto C++/WinRT (e viceversa) nell'ambito dello stesso progetto.This topic shows you two helper functions that you can use to convert a C++/CX object into a C++/WinRT object (and vice versa) within the same project.

Queste funzioni helper saranno molto utili quando si convertirà il codice gradualmente da C++/CX a C++/WinRT.These helper functions will be very useful as you port your code gradually from C++/CX to C++/WinRT. In alternativa, è possibile scegliere di usare entrambe le proiezioni di linguaggio C++/WinRT e C++/CX nello stesso progetto, indipendentemente dal fatto che si stia eseguendo la conversione o meno, e le funzioni helper per l'interoperabilità tra i due codici.Or you might just choose to use both the C++/WinRT and C++/CX language projections in the same project, whether you're porting or not, and use these helper functions to interoperate between the two.

Dopo la lettura di questo argomento, per informazioni ed esempi di codice che illustrino come supportare le attività e le coroutine della libreria PPL affiancate nello stesso progetto (ad esempio, per la chiamata di coroutine dalle catene di attività), vedere l'argomento più avanzato Asincronia e interoperabilità tra C++/WinRT e C++/CX.After reading this topic, for info and code examples showing how to support PPL tasks and coroutines side by side in the same project (for example, calling coroutines from task chains), see the more advanced topic Asynchrony, and interop between C++/WinRT and C++/CX.

Funzioni from_cx e to_cxThe from_cx and to_cx functions

Ecco un listato di codice sorgente di un file di intestazione denominato interop_helpers.h, che contiene due funzioni helper di conversione.Here's a source code listing of a header file named interop_helpers.h, containing two conversion helper functions. Con la graduale conversione del progetto, alcune parti continuano a essere in C++/CX, mentre altre risultano convertite in C++/WinRT.As you gradually port your project, there will be parts still in C++/CX, and parts that you've ported to C++/WinRT. È possibile usare le funzioni helper per convertire gli oggetti da e verso C++/CX e C++/WinRT nel progetto in corrispondenza dei punti limite tra le due parti.You can use these helper functions to convert objects to and from C++/CX and C++/WinRT in your project at the boundary points between those two parts.

Le sezioni che seguono il listato di codice descrivono le due funzioni e illustrano come creare e usare il file di intestazione nel progetto.The sections that follow the code listing explain the two functions, and how to create and use the header file in your project.

// interop_helpers.h
#pragma once

template <typename T>
T from_cx(Platform::Object^ from)
{
    T to{ 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)));
}

Funzione from_cxThe from_cx function

La funzione helper from_cx converte un oggetto C++/CX in un oggetto C++/WinRT equivalente.The from_cx helper function converts a C++/CX object to an equivalent C++/WinRT object. La funzione esegue il cast di un oggetto C++/CX al puntatore all'interfaccia IUnknown sottostante.The function casts a C++/CX object to its underlying IUnknown interface pointer. Quindi chiama QueryInterface sul puntatore per eseguire una query per l'interfaccia predefinita dell'oggetto C++/WinRT.It then calls QueryInterface on that pointer to query for the default interface of the C++/WinRT object. QueryInterface è l'equivalente dell'ABI (Application Binary Interface) Windows Runtime dell'estensione safe_cast C++/CX.QueryInterface is the Windows Runtime application binary interface (ABI) equivalent of the C++/CX safe_cast extension. Inoltre, la funzione winrt::put_abi recupera l'indirizzo del puntatore all'interfaccia IUnknown sottostante di un oggetto C++/WinRT affinché possa essere impostato su un altro valore.And, the winrt::put_abi function retrieves the address of a C++/WinRT object's underlying IUnknown interface pointer so that it can be set to another value.

Funzione to_cxThe to_cx function

La funzione helper to_cx converte un oggetto C++/WinRT in un oggetto C++/CX equivalente.The to_cx helper function converts a C++/WinRT object to an equivalent C++/CX object. La funzione winrt::get_abi recupera un puntatore all'interfaccia IUnknown sottostante di un oggetto C++/WinRT.The winrt::get_abi function retrieves a pointer to a C++/WinRT object's underlying IUnknown interface. La funzione esegue il cast di questo puntatore a un oggetto C++/CX prima di usare l'estensione safe_cast C++/CX per eseguire una query per il tipo C++/CX richiesto.The function casts that pointer to a C++/CX object before using the C++/CX safe_cast extension to query for the requested C++/CX type.

File di intestazione interop_helpers.hThe interop_helpers.h header file

Per usare le due funzioni helper nel progetto, seguire questa procedura.To use the two helper functions in your project, follow these steps.

  • Aggiungere un nuovo elemento del file Header (.h) al progetto e denominarlo interop_helpers.h.Add a new Header File (.h) item to your project and name it interop_helpers.h.
  • Sostituire il contenuto di interop_helpers.h con il listato di codice precedente.Replace the contents of interop_helpers.h with the code listing above.
  • Aggiungere tali inclusioni a pch.h.Add these includes to pch.h.
// pch.h
...
#include <unknwn.h>
// Include C++/WinRT projected Windows API headers here.
...
#include <interop_helpers.h>

Acquisizione di un progetto C++/CX e aggiunta del supporto di C++/WinRTTaking a C++/CX project and adding C++/WinRT support

Questa sezione descrive le operazioni da eseguire se si è deciso di usare il progetto C++/CX esistente, aggiungere il supporto di C++/WinRT ed eseguire l'operazione di conversione.This section describes what to do if you've decided to take your existing C++/CX project, add C++/WinRT support to it, and do your porting work there. Vedere anche Supporto di Visual Studio per C++/WinRT.Also see Visual Studio support for C++/WinRT.

Per combinare C++/CX e C++/WinRT in un progetto C++/CX — incluso l'uso delle funzioni helper from_cx e to_cx nel progetto — è necessario aggiungere manualmente il supporto di C++/WinRT al progetto.To mix C++/CX and C++/WinRT in a C++/CX project—including using the from_cx and to_cx helper functions in the project—you'll need to manually add C++/WinRT support to the project.

Innanzitutto aprire il progetto C++/CX in Visual Studio e verificare che la proprietà del progetto Generale > Versione piattaforma di destinazione sia impostata su 10.0.17134.0 (Windows 10, versione 1803) o versione successiva.First, open your C++/CX project in Visual Studio and confirm that project property General > Target Platform Version is set to 10.0.17134.0 (Windows 10, version 1803) or greater.

Installare il pacchetto NuGet di C++/WinRTInstall the C++/WinRT NuGet package

Il pacchetto NuGet Microsoft.Windows.CppWinRT fornisce supporto per la compilazione C++/WinRT (proprietà e destinazioni MSBuild).The Microsoft.Windows.CppWinRT NuGet package provides C++/WinRT build support (MSBuild properties and targets). Per installarlo, fare clic sulla voce di menu Progetto > Gestisci pacchetti NuGet > Sfoglia, digita o incolla Microsoft.Windows.CppWinRT nella casella di ricerca, seleziona l'elemento nei risultati della ricerca e quindi fai clic su Installa per installare il pacchetto per il progetto.To install it, click the menu item Project > Manage NuGet Packages... > Browse, type or paste Microsoft.Windows.CppWinRT in the search box, select the item in search results, and then click Install to install the package for that project.

Importante

L'installazione del pacchetto NuGet C++/WinRT comporta la disattivazione del supporto di C++/CX nel progetto.Installing the C++/WinRT NuGet package causes support for C++/CX to be turned off in the project. Se si esegue la conversione in un unico passaggio, è consigliabile lasciare il supporto disattivato in modo che i messaggi di compilazione consentano di individuare (e convertire) tutte le dipendenze in C++/CX (eventualmente trasformando un progetto C++/CX puro in un progetto C++/WinRT puro).If you're going to port in one pass, then it's a good idea to leave that support turned off so that build messages will help you find (and port) all of your dependencies on C++/CX (eventually turning what was a pure C++/CX project into a pure C++/WinRT project). Per informazioni sulla riattivazione del supporto, vedere la sezione seguente.But see the next section for info about turning it back on.

Riattivare il supporto di C++/CXTurn C++/CX support back on

Se si esegue la conversione in un unico passaggio, non è necessario eseguire questa operazione.If you're porting in one pass, then you don't need to do this. Se, tuttavia, è necessario eseguire la conversione gradualmente, a questo punto sarà necessario riattivare il supporto di C++/CX nel progetto.But if you need to port gradually, then at this point you'll need to turn C++/CX support back on in your project. Nelle proprietà del progetto C/C++ > Generale > Utilizza estensioni di Windows Runtime > Sì (/ZW) ).In project properties, C/C++ > General > Consume Windows Runtime Extension > Yes (/ZW)).

In alternativa, oppure per un progetto XAML, è possibile aggiungere il supporto di C++/CX usando la pagina della proprietà del progetto C++/WinRT in Visual Studio.Alternatively (or, for a XAML project, in addition), you can add C++/CX support by using the C++/WinRT project property page in Visual Studio. Nelle proprietà del progetto Proprietà comuni > C++/WinRT > Project Language (Linguaggio del progetto) > C++/CX.In project properties, Common Properties > C++/WinRT > Project Language > C++/CX. In questo modo, si aggiungerà la proprietà seguente al file .vcxproj.Doing that will add the following property to your .vcxproj file.

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

Importante

Indipendentemente dal tipo di compilazione, per elaborare il contenuto di un file Midl (.idl) in file stub, sarà necessario modificare di nuovo il valore di Project Language (Linguaggio del progetto) impostandolo su C++/WinRT.Whenever you need to build to process the contents of a Midl File (.idl) into stub files, you'll need to change Project Language back to C++/WinRT. Dopo che la compilazione ha generato questi stub, modificare nuovamente Project Language (Linguaggio del progetto) impostandolo su C++/CX.After the build has generated those stubs, change Project Language back to C++/CX.

Per un elenco di opzioni di personalizzazione simili, che ottimizzano il comportamento dello strumento cppwinrt.exe, vedi il file readme del pacchetto NuGet Microsoft.Windows.CppWinRT.For a list of similar customization options (which fine-tune the behavior of the cppwinrt.exe tool), see the Microsoft.Windows.CppWinRT NuGet package readme.

Includere i file di intestazione C++/WinRTInclude C++/WinRT header files

L'operazione di base prevede che nel file di intestazione precompilato (in genere pch.h) si includa winrt/base.h, come illustrato di seguito.The least you should do is, in your precompiled header file (usually pch.h), include winrt/base.h as shown below.

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

Saranno, tuttavia, necessari i tipi nello spazio dei nomi winrt::Windows::FoundationBut you'll almost certainly need the types in the winrt::Windows::Foundation namespace. ed è possibile che si conoscano già altri spazi dei nomi necessari.And you might already know of other namespaces that you'll need. Includere quindi le intestazione API Windows proiettate C++/WinRT che corrispondono a tali spazi dei nomi, non è necessario includere in modo esplicito winrt/base.h in questo momento, poiché verrà inclusa automaticamente.So include the C++/WinRT projected Windows API headers that correspond to those namespaces like this (you don't need to explicitly include winrt/base.h now because it will be included automatically for you).

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

Vedere anche l'esempio di codice nella sezione seguente (Acquisizione di un progetto C++/WinRT e aggiunta del supporto di C++/CX) per una tecnica che usa gli alias degli spazi dei nomi namespace cx e namespace winrt.Also see the code example in the following section (Taking a C++/WinRT project and adding C++/CX support) for a technique using the namespace aliases namespace cx and namespace winrt. Questa tecnica consente di gestire i conflitti di spazio dei nomi altrimenti possibili tra le proiezioni C++/WinRT e C++/CX.That technique lets you deal with otherwise potential namespace collisions between the C++/WinRT projection and the C++/CX projection.

Aggiungere interop_helpers.h al progettoAdd interop_helpers.h to the project

A questo punto è possibile aggiungere le funzioni from_cx e to_cx al progetto C++/CX.You'll now be able to add the from_cx and to_cx functions to your C++/CX project. Per istruzioni su come eseguire questa operazione, vedere la sezione delle funzioni from_cx e to_cx precedente.For instructions on doing that, see the from_cx and to_cx functions section above.

Acquisizione di un progetto C++/WinRT e aggiunta del supporto di C++/CXTaking a C++/WinRT project and adding C++/CX support

Questa sezione descrive le operazioni da eseguire se si è deciso di creare un nuovo progetto C++/WinRT ed eseguire l'operazione di conversione.This section describes what to do if you've decided to create a new C++/WinRT project, and do your porting work there.

Per combinare C++/WinRT e C++/CX in un progetto C++/W — incluso l'uso delle funzioni helper from_cx e to_cx nel progetto — è necessario aggiungere manualmente il supporto di C++/CX al progetto.To mix C++/WinRT and C++/CX in a C++/WinRT project—including using the from_cx and to_cx helper functions in the project—you'll need to manually add C++/CX support to the project.

  • Creare un nuovo progetto C++/WinRT in Visual Studio usando uno dei modelli di progetto di C++/WinRT. Vedere Supporto di Visual Studio per C++/WinRT.Create a new C++/WinRT project in Visual Studio using one of the C++/WinRT project templates (see Visual Studio support for C++/WinRT).
  • Attivare il supporto del progetto per C++/CX.Turn on project support for C++/CX. Nelle proprietà del progetto C /C++ ** > Generale > Utilizza estensioni di Windows Runtime > ** Sì (/ZW).In project properties, C/C++ > General > Consume Windows Runtime Extension > Yes (/ZW).

Progetto C++/WinRT di esempio che illustra le due funzioni helper in usoAn example C++/WinRT project showing the two helper functions in use

In questa sezione è possibile creare un progetto C++/WinRT di esempio che illustra come usare from_cx e to_cx.In this section, you can create an example C++/WinRT project that demonstrates how to use from_cx and to_cx. Illustra anche come usare gli alias degli spazi dei nomi per fare in modo che le diverse isole di codice gestiscano i conflitti di spazio dei nomi altrimenti possibili tra le proiezioni C++/WinRT e C++/CX.It also illustrates how you can use namespace aliases for the different islands of code, in order to deal with otherwise potential namespace collisions between the C++/WinRT projection and the C++/CX projection.

  • Crea un progetto Visual C++ > Windows Universal > Core App (C++/WinRT).Create a Visual C++ > Windows Universal > Core App (C++/WinRT) project.
  • Nelle proprietà del progetto C /C++ ** > Generale > Utilizza estensioni di Windows Runtime > ** Sì (/ZW).In project properties, C/C++ > General > Consume Windows Runtime Extension > Yes (/ZW).
  • Aggiungere interop_helpers.h al progetto.Add interop_helpers.h to the project. Per istruzioni su come eseguire questa operazione, vedere la sezione delle funzioni from_cx e to_cx precedente.For instructions on doing that, see the from_cx and to_cx functions section above.
  • Sostituire il contenuto di App.cpp con il listato di codice riportato di seguito, compilare ed eseguire.Replace the contents of App.cpp with the code listing below, build, and run.

WINRT_ASSERT è una definizione di macro e si espande in ASSERTE.WINRT_ASSERT is a macro definition, and it expands to _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>());
}

API importantiImportant APIs