Mudar do WRL para o C++/WinRT
Este tópico mostra como fazer a portabilidade o código da WRL (Biblioteca de Modelos C++ do Tempo de Execução do Windows) para seu equivalente no C++/WinRT.
A primeira etapa da portabilidade para o C++/WinRT é adicionar automaticamente o suporte para C++/WinRT ao seu projeto (confira Suporte do Visual Studio para C++/WinRT). Para isso, instale o pacote NuGet Microsoft.Windows.CppWinRT em seu projeto. Abra o projeto no Visual Studio, clique em 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. Um efeito dessa alteração é que o suporte para C++/CX é desligado no projeto. Se estiver usando o C++/CX no projeto, você poderá deixar o suporte desligado e atualizar o código do C++/CX para C++/WinRT (confira Mudar do C++/CX para C++/WinRT). Ou você pode ativar o suporte novamente (nas propriedades do projeto, C/C++>Geral>Consumir extensão do Windows Runtime>Sim (/ZW)) e focar primeiro na portabilidade do código WRL. Os códigos C++/CX e C++/WinRT podem coexistir no mesmo projeto, com exceção do suporte ao compilador XAML e dos componentes do Windows Runtime (confira Mudar do C++/CX para o C++/WinRT).
Defina a propriedade do projeto Geral>Versão da plataforma de destino como 10.0.17134.0 (Windows 10, versão 1803) ou posterior.
No arquivo de cabeçalho pré-compilado (em geral, pch.h
), inclua winrt/base.h
.
#include <winrt/base.h>
Se você incluir um cabeçalho de API C++/WinRT projetado do Windows (por exemplo, winrt/Windows.Foundation.h
), não será necessário incluir explicitamente winrt/base.h
dessa forma, pois será incluído automaticamente para você.
Portabilidade de ponteiros inteligentes COM do WRL (Microsoft::WRL::ComPtr)
Faça a portabilidade de qualquer código que usar Microsoft::WRL::ComPtr<T> para usar winrt::com_ptr<T>. Veja um exemplo anterior e posterior do código. Na versão posterior, a função de membro com_ptr::put recupera o ponteiro bruto subjacente para poder defini-lo.
ComPtr<IDXGIAdapter1> previousDefaultAdapter;
DX::ThrowIfFailed(m_dxgiFactory->EnumAdapters1(0, &previousDefaultAdapter));
winrt::com_ptr<IDXGIAdapter1> previousDefaultAdapter;
winrt::check_hresult(m_dxgiFactory->EnumAdapters1(0, previousDefaultAdapter.put()));
Importante
Se você tiver um winrt::com_ptr que já esteja estabelecido (seu ponteiro bruto interno já tem um destino) e deseja restabelecê-lo para apontar para outro objeto, primeiramente, será preciso atribuir nullptr
a ele, conforme mostrado no exemplo de código abaixo. Se não o fizer, um com_ptr já estabelecido chamará sua atenção para o problema (quando você chamar com_ptr::put ou com_ptr::put_void) afirmando que seu ponteiro interno não é nulo.
winrt::com_ptr<IDXGISwapChain1> m_pDXGISwapChain1;
...
// We execute the code below each time the window size changes.
m_pDXGISwapChain1 = nullptr; // Important because we're about to re-seat
winrt::check_hresult(
m_pDxgiFactory->CreateSwapChainForHwnd(
m_pCommandQueue.get(), // For Direct3D 12, this is a pointer to a direct command queue, and not to the device.
m_hWnd,
&swapChainDesc,
nullptr,
nullptr,
m_pDXGISwapChain1.put())
);
No exemplo a seguir, (na versão posterior), a função de membro com_ptr::put_void recupera o ponteiro bruto subjacente como um ponteiro para anulação.
ComPtr<ID3D12Debug> debugController;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
{
debugController->EnableDebugLayer();
}
winrt::com_ptr<ID3D12Debug> debugController;
if (SUCCEEDED(D3D12GetDebugInterface(__uuidof(debugController), debugController.put_void())))
{
debugController->EnableDebugLayer();
}
Substitua ComPtr::Get por com_ptr::get.
m_d3dDevice->CreateDepthStencilView(m_depthStencil.Get(), &dsvDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
m_d3dDevice->CreateDepthStencilView(m_depthStencil.get(), &dsvDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
Quando você desejar passar o ponteiro bruto subjacente para uma função que espera um ponteiro para IUnknown, use a função winrt::get_unknown, como mostrado no próximo exemplo.
ComPtr<IDXGISwapChain1> swapChain;
DX::ThrowIfFailed(
m_dxgiFactory->CreateSwapChainForCoreWindow(
m_commandQueue.Get(),
reinterpret_cast<IUnknown*>(m_window.Get()),
&swapChainDesc,
nullptr,
&swapChain
)
);
winrt::agile_ref<winrt::Windows::UI::Core::CoreWindow> m_window;
winrt::com_ptr<IDXGISwapChain1> swapChain;
winrt::check_hresult(
m_dxgiFactory->CreateSwapChainForCoreWindow(
m_commandQueue.get(),
winrt::get_unknown(m_window.get()),
&swapChainDesc,
nullptr,
swapChain.put()
)
);
Portabilidade de um módulo de WRL (Microsoft::WRL::Module)
Esta seção está relacionada ao código de portabilidade que usa o tipo Microsoft::WRL::Module.
Você pode adicionar gradualmente o código do C++/WinRT a um projeto existente que usa o WRL para implementar um componente e as classes WRL existentes continuarão a ser compatíveis. Esta seção mostra como.
Se você criar um novo tipo de projeto do componente do Windows Runtime (C++/WinRT) no Visual Studio e compilar, o arquivo Generated Files\module.g.cpp
será gerado para você. Esse arquivo contém as definições de duas funções úteis de C++/WinRT (listadas abaixo), que você pode copiar e adicionar ao projeto. Essas funções são WINRT_CanUnloadNow e WINRT_GetActivationFactory. Como você pode ver, elas chamam WRL condicionalmente para dar suporte à etapa de portabilidade atual.
HRESULT WINRT_CALL WINRT_CanUnloadNow()
{
#ifdef _WRL_MODULE_H_
if (!::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().Terminate())
{
return S_FALSE;
}
#endif
if (winrt::get_module_lock())
{
return S_FALSE;
}
winrt::clear_factory_cache();
return S_OK;
}
HRESULT WINRT_CALL WINRT_GetActivationFactory(HSTRING classId, void** factory)
{
try
{
*factory = nullptr;
wchar_t const* const name = WINRT_WindowsGetStringRawBuffer(classId, nullptr);
if (0 == wcscmp(name, L"MoveFromWRLTest.Class"))
{
*factory = winrt::detach_abi(winrt::make<winrt::MoveFromWRLTest::factory_implementation::Class>());
return S_OK;
}
#ifdef _WRL_MODULE_H_
return ::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().GetActivationFactory(classId, reinterpret_cast<::IActivationFactory**>(factory));
#else
return winrt::hresult_class_not_available().to_abi();
#endif
}
catch (...) { return winrt::to_hresult(); }
}
Quando essas funções estiverem no projeto, em vez de chamar Module::GetActivationFactory diretamente, chame WINRT_GetActivationFactory (que chama a função WRL internamente). Veja um exemplo anterior e posterior do código.
HRESULT WINAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _Out_ ::IActivationFactory **factory)
{
auto & module = Microsoft::WRL::Module<Microsoft::WRL::InProc>::GetModule();
return module.GetActivationFactory(activatableClassId, factory);
}
HRESULT __stdcall WINRT_GetActivationFactory(HSTRING activatableClassId, void** factory);
HRESULT WINAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _Out_ ::IActivationFactory **factory)
{
return WINRT_GetActivationFactory(activatableClassId, reinterpret_cast<void**>(factory));
}
Em vez de chamar Module::Terminate diretamente, chame WINRT_CanUnloadNow (que chama a função WRL internamente). Veja um exemplo anterior e posterior do código.
HRESULT __stdcall DllCanUnloadNow(void)
{
auto &module = Microsoft::WRL::Module<Microsoft::WRL::InProc>::GetModule();
HRESULT hr = (module.Terminate() ? S_OK : S_FALSE);
if (hr == S_OK)
{
hr = ...
}
return hr;
}
HRESULT __stdcall WINRT_CanUnloadNow();
HRESULT __stdcall DllCanUnloadNow(void)
{
HRESULT hr = WINRT_CanUnloadNow();
if (hr == S_OK)
{
hr = ...
}
return hr;
}
Portabilidade de wrappers Microsoft::WRL::Wrappers
Esta seção está relacionada ao código de portabilidade que usa os wrappers Microsoft::WRL::Wrappers.
Como mostrado na tabela a seguir, para substituir os auxiliares de threading, recomendamos que você use a biblioteca de suporte a thread C++ Standard. Um mapeamento um-para-um dos wrappers WRL pode ser equivocado, uma vez que sua escolha depende de suas necessidades. Além disso, alguns tipos que podem parecer mapeamentos óbvios são novos para o padrão C++ 20; portanto, serão impraticáveis se você ainda não tiver feito a atualização.
Tipo | Observações sobre portabilidade |
---|---|
classe CriticalSection | Use a biblioteca de suporte a thread |
Classe de evento (WRL) | Use winrt::event struct template |
Classe HandleT | Use winrt::handle struct ou winrt::file_handle struct |
Classe HString | Use winrt::hstring struct |
Classe HStringReference | Sem substituição, porque o C++/WinRT manipula isso internamente de uma forma tão eficiente quanto HStringReference com a vantagem de que você não precisa pensar nisso. |
Classe Mutex | Use a biblioteca de suporte a thread |
Classe RoInitializeWrapper | Use winrt::init_apartment e winrt::uninit_apartment, ou escreva seu wrapper trivial em volta de CoInitializeEx e CoUninitialize. |
Classe Semaphore | Use a biblioteca de suporte a thread |
Classe SRWLock | Use a biblioteca de suporte a thread |