Partilhar via


Transição do WRL para C++/WinRT

Este tópico mostra como portar código de WRL (Biblioteca de Modelos C++ do Tempo de Execução do Windows) para seu equivalente em C++/WinRT.

A primeira etapa na portabilidade para C++/WinRT é adicionar manualmente o suporte a C++/WinRT ao seu projeto (consulte Suporte do Visual Studio para C++/WinRT). Para fazer isso, instale o pacote NuGet Microsoft.Windows.CppWinRT em seu projeto. Abra o projeto no Visual Studio, clique em Project>Manage NuGet Packages...>Navegue, 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 para esse projeto. Um efeito dessa mudança é que o suporte para C++/CX está desativado no projeto. Se estiveres a usar C++/CX no projeto, podes deixar o suporte desativado e atualizar o teu código C++/CX para C++/WinRT também (vê Mover para C++/WinRT de C++/CX). Ou você pode ativar novamente o suporte (em propriedades do projeto, C/C++>General>Consume Windows Runtime Extension>Yes (/ZW)) e primeiro se concentrar na portabilidade do código WRL. O código C++/CX e C++/WinRT podem coexistir no mesmo projeto, com exceção do suporte do compilador XAML e dos componentes Windows Runtime (consulte a transição de C++/CX para C++/WinRT).

Defina a propriedade do projeto General>Target Platform Version como 10.0.17134.0 (Windows 10, versão 1803) ou superior.

No seu arquivo de cabeçalho pré-compilado (geralmente pch.h), inclua winrt/base.h.

#include <winrt/base.h>

Se incluir qualquer cabeçalho de API do Windows projetado em C++/WinRT (por exemplo, winrt/Windows.Foundation.h), não precisará incluir winrt/base.h explicitamente como este, pois ele será incluído automaticamente para si.

Transposição de ponteiros inteligentes WRL COM (Microsoft::WRL::ComPtr)

Portar qualquer código que use Microsoft::WRL::ComPtr<T>, para usar winrt::com_ptr<T>. Aqui está um exemplo de código antes e depois. Na versão após, a função de membro com_ptr::put recupera o ponteiro bruto subjacente para que ele seja utilizado para definição.

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 sentado (seu ponteiro bruto interno já tem um destino) e quiser recolocá-lo para apontar para um objeto diferente, primeiro será necessário atribuir nullptr a ele, conforme mostrado no exemplo de código abaixo. Se não o fizer, então um com_ptr já estabelecido chamará a sua atenção para o problema (quando chamar com_ptr::put ou com_ptr::put_void) afirmando 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 após), a função de 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();
}

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 se pretender passar o ponteiro bruto subjacente para uma função que espera um ponteiro para IUnknown, utilize a função livre de uso winrt::get_unknown, conforme demonstrado no exemplo seguinte.

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()
    )
);

Portar um módulo WRL (Microsoft::WRL::Module)

Esta seção refere-se à adaptação de código que utiliza o tipo Microsoft::WRL::Module .

Você pode adicionar gradualmente código C++/WinRT a um projeto existente que usa WRL para implementar um componente, e suas classes WRL existentes continuarão a ser suportadas. Esta seção mostra como.

Se criar um novo Componente do Tempo de Execução do Windows (C++/WinRT) no Visual Studio e compilar, o ficheiro Generated Files\module.g.cpp é gerado automaticamente. Esse arquivo contém as definições de duas funções úteis do C++/WinRT (listadas abaixo), que você pode copiar e adicionar ao seu projeto. Essas funções são WINRT_CanUnloadNow e WINRT_GetActivationFactory e, como você pode ver, elas condicionalmente chamam WRL para apoiá-lo em qualquer estágio de portabilidade em que você esteja.

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 ter essas funções em seu projeto, em vez de chamar Module::GetActivationFactory diretamente, chame 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, opte por chamar WINRT_CanUnloadNow (que, por sua vez, chama a função WRL internamente). 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;
}

Portabilidade Microsoft::WRL::Wrappers wrappers

Esta seção está relacionada ao código de portabilidade que usa o Microsoft::WRL::Wrappers wrappers.

Como você pode ver na tabela abaixo, para substituir os auxiliares de threading, recomendamos que você use a biblioteca de suporte de thread de C++ padrão. Um mapeamento um-a-um dos WRL wrappers pode ser enganoso, dado que a sua escolha depende das suas necessidades. Além disso, alguns tipos que podem parecer mapeamentos óbvios são novos no padrão C++20, então eles serão impraticáveis se você ainda não tiver atualizado.

Tipo Notas de portabilidade
classe CriticalSection Use a biblioteca de suporte de thread
classe de evento (WRL) Utilize o modelo struct de evento winrt::
classe HandleT Utilize a estrutura winrt::handle ou a estrutura winrt::file_handle
classe HString Use o struct winrt::hstring
HStringReference classe Sem substituição, porque o C++/WinRT lida com 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 de thread
classe RoInitializeWrapper Use winrt::init_apartment e winrt::uninit_apartment; ou escreva o seu próprio invólucro trivial em torno CoInitializeEx e CoUninitialize.
Classe Semáforo Use a biblioteca de suporte de thread
Classe SRWLock Use a biblioteca de suporte de thread

APIs importantes