Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este tópico mostra como portar código da Windows Runtime C++ Template Library (WRL) para o seu equivalente em C++/WinRT.
O primeiro passo para portar para C++/WinRT é adicionar manualmente suporte a C++/WinRT ao seu projeto (veja o suporte do Visual Studio para C++/WinRT). Para isso, instale o pacote NuGet Microsoft.Windows.CppWinRT no seu projeto. Abra o projeto no Visual Studio, clique em Projeto>Gerir Pacotes NuGet...>Procurar, escreva 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 dos efeitos dessa alteração é que o suporte para C++/CX está desativado no projeto. Se estiveres a usar C++/CX no projeto, podes deixar o suporte desligado e atualizar também o teu código C++/CX para C++/WinRT (ver Migrar de C++/CX para C++/WinRT). Ou podes voltar a ativar o suporte (nas propriedades do projeto, C/C++>General>Consume Windows Runtime Extension>Yes (/ZW)), e primeiro focar-te em portar o teu código WRL. O código C++/CX e C++/WinRT pode coexistir no mesmo projeto, com exceção do suporte ao compilador XAML e dos componentes do Windows Runtime (ver Migrar para C++/WinRT a partir de C++/CX).
Defina a propriedade do projeto Geral>Versão da Plataforma de Destino para 10.0.17134.0 (Windows 10, versão 1803) ou superior.
No seu ficheiro de cabeçalho pré-compilado (normalmente pch.h), inclua winrt/base.h.
#include <winrt/base.h>
Se incluires quaisquer cabeçalhos de API Windows projetados em C++/WinRT (por exemplo, winrt/Windows.Foundation.h), então não precisas de incluir winrt/base.h explicitamente assim porque será incluído automaticamente para ti.
Portar ponteiros inteligentes WRL COM (Microsoft::WRL::ComPtr)
Converta qualquer código que use Microsoft::WRL::ComPtr<T> para utilizar winrt::com_ptr<T>. Aqui está um exemplo de código antes e depois. Na versão after, a função membro com_ptr::put obtém o ponteiro bruto subjacente para que este possa ser definido.
ComPtr<IDXGIAdapter1> previousDefaultAdapter;
DX::ThrowIfFailed(m_dxgiFactory->EnumAdapters1(0, &previousDefaultAdapter));
winrt::com_ptr<IDXGIAdapter1> previousDefaultAdapter;
winrt::check_hresult(m_dxgiFactory->EnumAdapters1(0, previousDefaultAdapter.put()));
Important
Se tiver um winrt::com_ptr que já estiver associado (o respetivo ponteiro interno já aponta para um destino) e quiser fazer com que passe a apontar para outro objeto, tem primeiro de lhe atribuir nullptr — como se mostra no exemplo de código abaixo. Se não o fizeres, um com_ptr já inicializado alertar-te-á para o problema (quando chamares com_ptr::put ou com_ptr::put_void), ao emitir uma asserção de que o 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())
);
Neste próximo exemplo (na versão seguinte), a função membro com_ptr::put_void recupera o ponteiro bruto subjacente como um ponteiro para um ponteiro para void.
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();
}
Substitui 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 quiseres passar o ponteiro bruto subjacente para uma função que espera um ponteiro para IUnknown, usa a função winrt::get_unknown livre, como mostrado neste próximo exemplo.
ComPtr<IDXGISwapChain1> swapChain;
DX::ThrowIfFailed(
m_dxgiFactory->CreateSwapChainForCoreWindow(
m_commandQueue.Get(),
reinterpret_cast<IUnknown*>(m_window.Get()),
&swapChainDesc,
nullptr,
&swapChain
)
);
// Note: In a WinUI 3 desktop app, use CreateSwapChainForHwnd instead of
// CreateSwapChainForCoreWindow, since WinUI 3 doesn't use CoreWindow.
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()
)
);
Portar um módulo WRL (Microsoft::WRL::Module)
Esta secção diz respeito à portabilidade de código que utiliza o tipo Microsoft::WRL::Module.
Podes adicionar gradualmente código C++/WinRT a um projeto existente que usa WRL para implementar um componente, e as tuas classes WRL existentes continuarão a ser suportadas. Esta secção mostra como.
Se criares um novo tipo de projeto Windows Runtime Component (C++/WinRT) no Visual Studio e construíres, então o ficheiro Generated Files\module.g.cpp é gerado por ti. Esse ficheiro contém as definições de duas funções úteis em C++/WinRT (listadas abaixo), que pode copiar e adicionar ao seu projeto. Essas funções são WINRT_CanUnloadNow e WINRT_GetActivationFactory e, como podes ver, chamam condicionalmente o WRL para te apoiarem em qualquer fase do porting em que estejas.
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(); }
}
Depois de tiveres estas funções no teu projeto, em vez de ligares diretamente ao Module::GetActivationFactory , chama o WINRT_GetActivationFactory (que chama a função WRL internamente). Aqui está um exemplo de código antes e depois.
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 diretamente Module::Terminate, chame WINRT_CanUnloadNow (que chama internamente a função WRL). Aqui está um exemplo de código antes e depois.
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;
}
A portar os wrappers da Microsoft::WRL::Wrappers
Esta secção diz respeito à portabilidade de código que utiliza os wrappers Microsoft::WRL::Wrappers.
Como pode ver na tabela abaixo, para substituir os auxiliares de processamento por threads, recomendamos que use a biblioteca-padrão de suporte a threads do C++. Um mapeamento um a um dos wrappers WRL pode ser enganador, já que a sua escolha depende das suas necessidades. Além disso, alguns tipos que podem parecer mapeamentos óbvios são novos para o padrão C++20, por isso serão impraticáveis se ainda não fizeste a atualização.
| Tipo | Notas de portabilidade |
|---|---|
| Classe de Secção Crítica | Usar a biblioteca de suporte de threads |
| Classe de evento (WRL) | Usa o modelo de struct winrt::event |
| Classe HandleT | Usa a estrutura winrt::handle ou a estrutura winrt::file_handle |
| classe HString | Utilize a struct winrt::hstring |
| Classe HStringReference | Sem substituição, porque o C++/WinRT trata disto internamente de uma forma tão eficiente quanto o HStringReference , com a vantagem de não teres de pensar nisso. |
| Classe Mutex | Usar a biblioteca de suporte de threads |
| Classe RoInitializeWrapper | Utilize winrt::init_apartment e winrt::uninit_apartment; ou escreva o seu próprio wrapper simples para CoInitializeEx e CoUninitialize. |
| Classe de semáforo | Usar a biblioteca de suporte de threads |
| Classe SRWLock | Usar a biblioteca de suporte de threads |
APIs importantes
Tópicos relacionados
Windows developer