Interoperabilidade entre C++/WinRT e C++/CXInterop between C++/WinRT and C++/CX

Antes de ler este tópico, você precisará das informações no tópico Mover do C++/CX para o C++/WinRT.Before reading this topic, you'll need the info in the topic Move to C++/WinRT from C++/CX. Esse tópico apresenta duas opções principais de estratégia para portar seu projeto C++/CX para C++/WinRT.That topic introduces two main strategy options for porting your C++/CX project to C++/WinRT.

  • Portar todo o projeto em uma passagem.Port the entire project in one pass. A opção mais simples para um projeto que não é muito grande.The simplest option for a project that's not too large. Se você tiver um projeto de componente do Windows Runtime, essa estratégia será sua única opção.If you have a Windows Runtime component project, then this strategy is your only option.
  • Porte o projeto gradualmente (o tamanho ou a complexidade de sua base de código pode tornar isso necessário).Port the project gradually (the size or complexity of your codebase might make this necessary). Contudo, essa estratégia convida você a seguir um processo de portabilidade no qual, por um momento, os códigos C++/CX e C++/WinRT coexistem no mesmo projeto.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. Para um projeto XAML, a qualquer momento, os tipos de página XAML devem estar ou inteiramente em C++/WinRT ou inteiramente em C++/CX.For a XAML project, at any given time, your XAML page types must be either all C++/WinRT or all C++/CX.

Esse tópico de interoperabilidade é relevante para essa segunda estratégia—para os casos em que você precisa portar seu projeto gradualmente.This interop topic is relevant for that second strategy—for cases when you need to port your project gradually. Este tópico apresenta duas funções auxiliares que você pode usar para converter um objeto C++/CX em um objeto C++/WinRT (e vice-versa) dentro do mesmo projeto.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.

Essas funções auxiliares serão muito úteis enquanto você portar seu código gradualmente de C++/CX para C++/WinRT.These helper functions will be very useful as you port your code gradually from C++/CX to C++/WinRT. Ou você pode simplesmente optar por usar as projeções de linguagem C++/WinRT e C++/CX no mesmo projeto, esteja você portando ou não, e usar essas funções auxiliares para interoperar entre as duas.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.

Depois de ler este tópico, para obter informações e exemplos de código que mostram como dar suporte a tarefas PPL e corrotinas lado a lado no mesmo projeto (por exemplo, chamar corrotinas de cadeias de tarefas), confira o tópico mais avançado Assincronia e interoperabilidade entre 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.

As funções from_cx e to_cxThe from_cx and to_cx functions

Esta é uma listagem de código-fonte de um arquivo de cabeçalho chamado interop_helpers.h, que contém duas funções auxiliares de conversão.Here's a source code listing of a header file named interop_helpers.h, containing two conversion helper functions. Conforme você gradualmente portar seu projeto, haverá partes ainda em C++/CX e as partes que você tiver portado para 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. Você pode usar essas funções auxiliares para converter objetos de/em C++/CX e C++/WinRT em seu projeto nos pontos de limite entre essas duas partes.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.

As seções após a listagem do código explicam as duas funções e explicam como criar e usar o arquivo de cabeçalho em seu projeto.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)));
}

A função from_cxThe from_cx function

A função auxiliar from_cx converte um objeto de C++/CX em um objeto de C++/WinRT equivalente.The from_cx helper function converts a C++/CX object to an equivalent C++/WinRT object. A função converte um objeto de C++/CX em seu ponteiro de interface IUnknown subjacente.The function casts a C++/CX object to its underlying IUnknown interface pointer. Em seguida, chama QueryInterface nesse ponteiro para consultar a interface padrão do objeto de C++/WinRT.It then calls QueryInterface on that pointer to query for the default interface of the C++/WinRT object. A QueryInterface é o equivalente da ABI (interface binária de aplicativo) do Windows Runtime da extensão safe_cast do C++/CX.QueryInterface is the Windows Runtime application binary interface (ABI) equivalent of the C++/CX safe_cast extension. A função winrt::put_abi recupera o endereço do ponteiro de interface IUnknown subjacente do objeto de C++/WinRT para que ele possa ser definido com outro valor.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.

A função to_cxThe to_cx function

A função auxiliar to_cx converte um objeto de C++/WinRT em um objeto de C++/CX equivalente.The to_cx helper function converts a C++/WinRT object to an equivalent C++/CX object. A função winrt::get_abi recupera um ponteiro para a interface IUnknown subjacente de um objeto de C++/WinRT.The winrt::get_abi function retrieves a pointer to a C++/WinRT object's underlying IUnknown interface. A função converte esse ponteiro em um objeto de C++/CX antes de usar a extensão safe_cast do C++/CX para consultar o tipo de C++/CX solicitado.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.

O arquivo de cabeçalho interop_helpers.hThe interop_helpers.h header file

Para usar as duas funções auxiliares em seu projeto, siga estas etapas.To use the two helper functions in your project, follow these steps.

  • Adicione um novo item Arquivo de Cabeçalho (.h) ao seu projeto e dê a ele o nome interop_helpers.h.Add a new Header File (.h) item to your project and name it interop_helpers.h.
  • Substitua o conteúdo de interop_helpers.h pela listagem de códigos acima.Replace the contents of interop_helpers.h with the code listing above.
  • Adicione essas inclusões 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>

Adicionar suporte do C++/WinRT a um projeto C++/CXTaking a C++/CX project and adding C++/WinRT support

Esta seção descreve o que fazer se você decidiu usar adicionar suporte do C++/WinRT ao seu projeto C++/CX existente e fazer o trabalho de portabilidade lá.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. Confira também Suporte do Visual Studio para C++/WinRT.Also see Visual Studio support for C++/WinRT.

Para combinar o C++/CX e o C++/WinRT em um projeto C++/CX—incluindo o uso das funções auxiliares from_cx e to_cx no projeto—, você precisará adicionar manualmente o suporte do C++/WinRT ao projeto.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.

Primeiro, abra seu projeto C++/CX no Visual Studio e confirme que a propriedade do projeto Geral > Versão da Plataforma de Destino está definida como 10.0.17134.0 (Windows 10, versão 1803) ou posterior.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.

Instalar o pacote NuGet do C++/WinRTInstall the C++/WinRT NuGet package

O pacote NuGet Microsoft.Windows.CppWinRT fornece suporte ao build do C++/WinRT (propriedades e destinos do MSBuild).The Microsoft.Windows.CppWinRT NuGet package provides C++/WinRT build support (MSBuild properties and targets). Para instalá-lo, clique no item de menu Projeto > Gerenciar Pacotes NuGet... > Procurar, digite ou cole Microsoft.Windows.CppWinRT na caixa de pesquisa, selecione o item nos resultados da pesquisa e clique em Instalar para instalar o pacote do projeto.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

A instalação do pacote NuGet do C++/WinRT faz o suporte ao C++/CX ser desativado no projeto.Installing the C++/WinRT NuGet package causes support for C++/CX to be turned off in the project. Se você vai portar em uma passagem, convém deixar esse suporte desativado para que as mensagens de build ajudem você a encontrar (e portar) todas as suas dependências no C++/CX (eventualmente, transformando o que era um projeto C++/CX puro em um projeto 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). No entanto, confira a próxima seção para obter informações sobre como ativá-lo novamente.But see the next section for info about turning it back on.

Ativar o suporte do C++/CX novamenteTurn C++/CX support back on

Se você estiver portando em uma passagem, não precisará fazer isso.If you're porting in one pass, then you don't need to do this. Mas se você precisar portar gradualmente, então, neste ponto, você precisará ativar C++/CX novamente em seu projeto.But if you need to port gradually, then at this point you'll need to turn C++/CX support back on in your project. Nas propriedades do projeto, C/C++ > Geral>Consumir Extensão do Windows Runtime > Sim (/ZW) .In project properties, C/C++ > General > Consume Windows Runtime Extension > Yes (/ZW)).

Como alternativa (ou, para um projeto XAML, além disso), você pode adicionar o suporte do C++/CX usando a página de propriedades do projeto C++/WinRT no 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. Nas propriedades do projeto, Propriedades Comuns > C++/WinRT > Linguagem do Projeto > C++/CX.In project properties, Common Properties > C++/WinRT > Project Language > C++/CX. Fazer isso adicionará a propriedade a seguir ao arquivo .vcxproj.Doing that will add the following property to your .vcxproj file.

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

Importante

Sempre que precisar criar para processar o conteúdo de um Arquivo Midl (.idl) em arquivos stub, você precisará alterar a Linguagem do Projeto de volta para 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. Depois que o build gerar esses stubs, altere a Linguagem do Projeto de volta para C++/CX.After the build has generated those stubs, change Project Language back to C++/CX.

Para obter uma lista de opções de personalização semelhantes (que ajustam o comportamento da ferramenta cppwinrt.exe), confira o arquivo Leiame do pacote 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.

Incluir arquivos de cabeçalho do C++/WinRTInclude C++/WinRT header files

O mínimo que você deve fazer é, em seu arquivo de cabeçalho pré-compilado (geralmente pch.h), inclua winrt/base.h conforme mostrado abaixo.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>
...

Mas você certamente precisará dos tipos no namespace winrt::Windows::Foundation.But you'll almost certainly need the types in the winrt::Windows::Foundation namespace. Talvez você já conheça outros namespaces de que precisará.And you might already know of other namespaces that you'll need. Assim, inclua os cabeçalhos de API C++/WinRT projetados do Windows correspondentes a esses namespaces como esse (você não precisa incluir explicitamente winrt/base.h agora porque ele será incluído automaticamente para você).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.
...

Confira também o exemplo de código na seção a seguir (Adicionar suporte do C++/WinRT a um projeto C++/CX) para uma técnica que usa os aliases de namespace 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. Essa técnica permite que você lide com possíveis conflitos de namespace entre a projeção do C++/WinRT e a projeção do C++/CX.That technique lets you deal with otherwise potential namespace collisions between the C++/WinRT projection and the C++/CX projection.

Adicionar interop_helpers.h ao projetoAdd interop_helpers.h to the project

Agora você poderá adicionar as funções from_cx e to_cx ao seu projeto do C++/CX.You'll now be able to add the from_cx and to_cx functions to your C++/CX project. Para obter instruções sobre como fazer isso, confira a seção Funções from_cx e to_cx funções acima.For instructions on doing that, see the from_cx and to_cx functions section above.

Adicionar suporte do C++/CX a um projeto C++/WinRTTaking a C++/WinRT project and adding C++/CX support

Esta seção descreve o que fazer se você decidiu criar um projeto C++/WinRT e fazer seu trabalho de portabilidade lá.This section describes what to do if you've decided to create a new C++/WinRT project, and do your porting work there.

Para combinar o C++/WinRT e o C++/CX em um projeto C++/WinRT—incluindo o uso das funções auxiliares from_cx e to_cx no projeto—, você precisará adicionar manualmente o suporte do C++/CX ao projeto.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.

  • Crie um projeto C++/WinRT no Visual Studio usando um dos modelos de projeto C++/WinRT (confira Suporte ao Visual Studio para 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).
  • Ative o suporte ao projeto para C++/CX.Turn on project support for C++/CX. Nas propriedades do projeto, C/C++ > Geral > Utilizar Extensão do Windows Runtime > Sim (/ZW).In project properties, C/C++ > General > Consume Windows Runtime Extension > Yes (/ZW).

Um exemplo de projeto C++/WinRT mostrando as duas funções auxiliares em usoAn example C++/WinRT project showing the two helper functions in use

Nesta seção, você poderá criar um projeto C++/WinRT de exemplo que demonstra como usar 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. Ele também ilustra como é possível usar aliases de namespace para diferentes ilhas de código, a fim de lidar com outros possíveis conflitos de namespace entre as projeções do C++/WinRT e do 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.

  • Crie um projeto Visual C++ > Windows Universal > App Core (C++/WinRT).Create a Visual C++ > Windows Universal > Core App (C++/WinRT) project.
  • Nas propriedades do projeto, C/C++ > Geral > Utilizar Extensão do Windows Runtime > Sim (/ZW).In project properties, C/C++ > General > Consume Windows Runtime Extension > Yes (/ZW).
  • Adicione interop_helpers.h ao projeto.Add interop_helpers.h to the project. Para obter instruções sobre como fazer isso, confira a seção Funções from_cx e to_cx funções acima.For instructions on doing that, see the from_cx and to_cx functions section above.
  • Substitua o conteúdo de App.cpp pela listagem de códigos abaixo, crie e execute.Replace the contents of App.cpp with the code listing below, build, and run.

WINRT_ASSERT é uma definição de macro e se expande para _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>());
}

APIs importantesImportant APIs