Interoperabilidade entre C++/WinRT e C++/CX
Antes de ler este tópico, você precisará das informações no tópico Mover do C++/CX para o C++/WinRT. Esse tópico apresenta duas opções principais de estratégia para portar seu projeto C++/CX para C++/WinRT.
- Portar todo o projeto em uma passagem. A opção mais simples para um projeto que não é muito grande. Se você tiver um projeto de componente do Windows Runtime, essa estratégia será sua única opção.
- Porte o projeto gradualmente (o tamanho ou a complexidade de sua base de código pode tornar isso necessário). 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. 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.
Esse tópico de interoperabilidade é relevante para essa segunda estratégia – para os casos em que você precisa portar seu projeto gradualmente. Este tópico apresenta várias formas de pares de funções auxiliares que você pode usar para converter um objeto C++/CX (e outros tipos) em um objeto C++/WinRT (e vice-versa) dentro do mesmo projeto.
Essas funções auxiliares serão muito úteis enquanto você portar seu código gradualmente de C++/CX para 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.
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.
As funções from_cx e to_cx
Esta é uma listagem de código-fonte de um arquivo de cabeçalho chamado interop_helpers.h
, que contém várias funções auxiliares de conversão. Conforme você gradualmente portar seu projeto, haverá partes ainda em C++/CX e as partes que você tiver portado para C++/WinRT. Você pode usar essas funções auxiliares para converter objetos (e outros tipos) de/em C++/CX e C++/WinRT em seu projeto nos pontos de limite entre essas duas partes.
As seções após a listagem do código explicam as funções auxiliares e como criar e usar o arquivo de cabeçalho em seu projeto.
// 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 função from_cx
A função auxiliar from_cx converte um objeto de C++/CX em um objeto de C++/WinRT equivalente. A função converte um objeto de C++/CX em seu ponteiro de interface IUnknown subjacente. Em seguida, chama QueryInterface nesse ponteiro para consultar a interface padrão do objeto de C++/WinRT. A QueryInterface é o equivalente da ABI (interface binária de aplicativo) do Windows Runtime da extensão safe_cast
do C++/CX. 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.
A função to_cx
A função auxiliar to_cx converte um objeto de C++/WinRT em um objeto de C++/CX equivalente. A função winrt::get_abi recupera um ponteiro para a interface IUnknown subjacente de um objeto de C++/WinRT. 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.
O arquivo de cabeçalho interop_helpers.h
Para usar as funções auxiliares em seu projeto, siga estas etapas.
- Adicione um novo item Arquivo de Cabeçalho (.h) ao seu projeto e nomeie-o como
interop_helpers.h
. - Substitua o conteúdo de
interop_helpers.h
pela listagem de códigos acima. - Adicione essas inclusões a
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++/CX
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á. Confira também Suporte do Visual Studio para 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.
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.
Instalar o pacote NuGet do C++/WinRT
O pacote NuGet Microsoft.Windows.CppWinRT fornece suporte ao build do C++/WinRT (propriedades e destinos do MSBuild). 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, em seguida, clique em Instalar para instalar o pacote para esse projeto.
Importante
A instalação do pacote NuGet do C++/WinRT faz o suporte ao C++/CX ser desativado no projeto. 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). No entanto, confira a próxima seção para obter informações sobre como ativá-lo novamente.
Ativar o suporte do C++/CX novamente
Se você estiver portando em uma passagem, não precisará fazer isso. Mas se você precisar portar gradualmente, então, neste ponto, você precisará ativar C++/CX novamente em seu projeto. Nas propriedades do projeto, C/C++>Geral>Consumir Extensão do Windows Runtime>Sim (/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. Nas propriedades do projeto, Propriedades Comuns>C++/WinRT>Linguagem do Projeto >C++/CX. Fazer isso adicionará a propriedade a seguir ao arquivo .vcxproj
.
<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. Depois que o build gerar esses stubs, altere a Linguagem do Projeto de volta para 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.
Incluir arquivos de cabeçalho do C++/WinRT
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.
// pch.h
...
#include <winrt/base.h>
...
Mas você certamente precisará dos tipos no namespace winrt::Windows::Foundation. Talvez você já conheça outros namespaces de que precisará. 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ê).
// 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
. 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.
Adicionar interop_helpers.h
ao projeto
Agora você poderá adicionar as funções from_cx e to_cx ao seu projeto do C++/CX. Para obter instruções sobre como fazer isso, confira a seção Funções from_cx e to_cx funções acima.
Adicionar suporte do C++/CX a um projeto C++/WinRT
Esta seção descreve o que fazer se você decidiu criar um projeto C++/WinRT e fazer seu trabalho de portabilidade lá.
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.
- Crie um projeto C++/WinRT no Visual Studio usando um dos modelos de projeto C++/WinRT (confira Suporte ao Visual Studio para C++/WinRT).
- Ative o suporte ao projeto para C++/CX. Nas propriedades do projeto, C/C++>Geral>Utilizar Extensão do Windows Runtime>Sim (/ZW).
Um exemplo de projeto C++/WinRT mostrando as duas funções auxiliares em uso
Nesta seção, você poderá criar um projeto C++/WinRT de exemplo que demonstra como usar from_cx e 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.
- Crie um projeto Visual C++>Windows Universal>App Core (C++/WinRT).
- Nas propriedades do projeto, C/C++>Geral>Utilizar Extensão do Windows Runtime>Sim (/ZW).
- Adicione
interop_helpers.h
ao projeto. Para obter instruções sobre como fazer isso, confira a seção Funções from_cx e to_cx funções acima. - Substitua o conteúdo de
App.cpp
pela listagem de códigos a seguir. - Compilar e executar.
WINRT_ASSERT
é uma definição de macro e se expande para _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>());
}