Compartir a través de


Cambiar a C++/WinRT desde WRL

En este tema se muestra cómo migrar biblioteca de plantillas de C++ de Windows Runtime (WRL) código a su equivalente en C++/WinRT.

El primer paso para migrar a C++/WinRT es agregar manualmente compatibilidad con C++/WinRT al proyecto (consulte compatibilidad de Visual Studio con C++/WinRT). Para ello, instale el paquete NuGet Microsoft.Windows.CppWinRT en tu proyecto. Abra el proyecto en Visual Studio, haga clic en Proyecto>Administrar paquetes NuGet...>Examinar, escriba o pegue Microsoft.Windows.CppWinRT en el cuadro de búsqueda, seleccione el elemento en los resultados de búsqueda y haga clic en Instalar para instalar el paquete para ese proyecto. Un efecto de ese cambio es que la compatibilidad con de C++/CX está desactivada en el proyecto. Si estás usando C++/CX en el proyecto, entonces puedes dejar el soporte desactivado y actualizar también tu código de C++/CX a C++/WinRT (consulta Cambiar de C++/CX a C++/WinRT). O bien, puedes volver a activar la compatibilidad (en las propiedades del proyecto, C/C++>General>Consumir extensión de Windows Runtime>Sí (/ZW)) y centrarte primero en migrar el código WRL. El código de C++/CX y C++/WinRT puede coexistir en el mismo proyecto, a excepción de la compatibilidad con el compilador XAML y los componentes de Windows Runtime (consulta Mover a C++/WinRT desde C++/CX).

Establezca la propiedad del proyecto General>Versión de la plataforma de destino en 10.0.17134.0 (Windows 10, versión 1803) o superior.

En el archivo de encabezado precompilado (normalmente pch.h), incluya winrt/base.h.

#include <winrt/base.h>

Si incluye algún encabezado de API de Windows proyectado de C++/WinRT (por ejemplo, winrt/Windows.Foundation.h), no es necesario incluir explícitamente winrt/base.h de esta manera porque se incluirá automáticamente.

Traslado de punteros inteligentes WRL COM (Microsoft::WRL::ComPtr)

Porte cualquier código que use Microsoft::WRL::ComPtr<T> para usar winrt::com_ptr<T>. Este es un ejemplo de código anterior y posterior. En el después de versión, la función miembro com_ptr::p ut recupera el puntero sin procesar subyacente para que se pueda establecer.

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

Si tienes un winrt::com_ptr que ya está sentado (su puntero sin procesar interno ya tiene un destino) y quieres volver a sentarlo para que apunte a un objeto diferente, primero debes asignarle nullptr, como se muestra en el ejemplo de código siguiente. Si no lo hace, un com_ptr ya sentado llamará su atención (cuando llame a com_ptr::put o com_ptr::put_void) mediante la afirmación de que su puntero interno no es 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())
);

En este siguiente ejemplo (en la versión después de), la función miembro com_ptr::put_void recupera el puntero sin procesar subyacente como puntero a un puntero a 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();
}

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

Cuando desee pasar el subyacente puntero sin procesar a una función que espera un puntero a IUnknown, use la función libre winrt::get_unknown , como se muestra en el siguiente ejemplo.

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

Migración de un módulo WRL (Microsoft::WRL::Module)

Esta sección se refiere a portar código que utiliza el tipo Microsoft::WRL::Module.

Puede agregar gradualmente código de C++/WinRT a un proyecto existente que use WRL para implementar un componente y las clases WRL existentes seguirán siendo compatibles. En esta sección se muestra cómo.

Si creas un nuevo componente de Windows Runtime (C++/WinRT) en Visual Studio de tipo de proyecto y compilas, el archivo Generated Files\module.g.cpp se genera automáticamente. Ese archivo contiene las definiciones de dos funciones útiles de C++/WinRT (enumeradas a continuación), que puede copiar y agregar al proyecto. Esas funciones son WINRT_CanUnloadNow y WINRT_GetActivationFactory y, como puede ver, llaman condicionalmente a WRL para ayudarle en cualquier fase de portabilidad en la que se encuentre.

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

Una vez que tenga estas funciones en el proyecto, en lugar de llamar a Module::GetActivationFactory directamente, llame a WINRT_GetActivationFactory (que llama a la función WRL internamente). Este es un ejemplo de código anterior y posterior.

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

En lugar de llamar a Module::Terminate directamente, llame a WINRT_CanUnloadNow (que llama a la función WRL internamente). Este es un ejemplo de código anterior y posterior.

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;
}

Portar wrappers de Microsoft::WRL::Wrappers

Esta sección se relaciona con el código de migración que utiliza los Microsoft::WRL::Wrappers wrappers.

Como puede ver en la tabla siguiente, para reemplazar los asistentes de subprocesos, se recomienda usar la biblioteca de compatibilidad con subprocesos estándar de C++ . Una correspondencia uno a uno de las envolturas WRL podría ser engañosa, ya que su elección depende de sus necesidades. Además, algunos tipos que podrían parecer mapeos obvios son nuevos en el estándar de C++20, por lo que resultarán poco prácticos si aún no se ha actualizado.

Tipo Notas de portabilidad
clase CriticalSection Usar la biblioteca de soporte de subprocesos
clase de evento (WRL) Use la plantilla de estructura winrt::event
de clase HandleT Use el de estructura winrt::handle o la estructura winrt::file_handle
clase HString Usar la estructura winrt::hstring de
clase HStringReference No hay reemplazo, ya que C++/WinRT controla esto internamente de una manera tan eficaz como HStringReference con la ventaja de que no tiene que pensar en ello.
de clase Mutex Usar la biblioteca de soporte de subprocesos
clase RoInitializeWrapper Use winrt::init_apartment y winrt::uninit_apartment; o escriba su propio contenedor trivial con CoInitializeEx y CoUninitialize.
Clase Semáforo Usar la biblioteca de soporte de subprocesos
Clase SRWLock de Usar la biblioteca de soporte de subprocesos

API importantes