Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este tema se muestra cómo convertir el código de la biblioteca de plantillas de C++ de Windows Runtime (WRL) a su equivalente en C++/WinRT.
El primer paso para migrar a C++/WinRT es agregar manualmente compatibilidad con C++/WinRT al proyecto (consulte Visual Studio compatibilidad con C++/WinRT). Para ello, instale el paquete NuGet Microsoft.Windows.CppWinRT en su proyecto. Abra el project en Visual Studio, haga clic en Project>Administrar paquetes NuGet...>Examine, escriba o pegue Microsoft.Windows. CppWinRT en el cuadro de búsqueda, seleccione el elemento en los resultados de búsqueda y, a continuación, haga clic en Instalar para instalar el paquete para ese project. Un efecto de ese cambio es que la compatibilidad con C++/CX está desactivada en el proyecto. Si usas C++/CX en el proyecto, puedes dejar la compatibilidad desactivada y migrar también tu código de C++/CX a C++/WinRT (consulta Pasa de C++/CX a C++/WinRT). O bien, puede volver a activar la compatibilidad (en las propiedades del proyecto, C/C++>General>Consume Windows Runtime Extensión>Sí (/ZW)) y centrarse 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 de proyecto General>Target Platform Version en 10.0.17134.0 (Windows 10, versión 1803) o posterior.
En el archivo de encabezado precompilado (normalmente pch.h), incluya winrt/base.h.
#include <winrt/base.h>
Si incluye alguno de los encabezados de la API de Windows proyectados por C++/WinRT (por ejemplo, winrt/Windows.Foundation.h), no necesita incluir explícitamente winrt/base.h de esta manera, porque se incluirá automáticamente.
Portar punteros inteligentes COM WRL (Microsoft::WRL::ComPtr)
Portar 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 la versión después, la función miembro com_ptr::put recupera el puntero sin formato subyacente para poder asignarlo.
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
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 asignarlo nullptr , como se muestra en el ejemplo de código siguiente. Si no es así, un com_ptr ya inicializado le señalará el problema (cuando llame a com_ptr::put o com_ptr::put_void) mediante una aserció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 el siguiente ejemplo (en la versión posterior), la función miembro com_ptr::put_void recupera el puntero sin formato subyacente como un puntero a 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 quiera pasar el puntero sin procesar subyacente a una función que espera un puntero a IUnknown, use la función libre winrt::get_unknown , como se muestra en este ejemplo siguiente.
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()
)
);
Adaptación de un módulo WRL (Microsoft::WRL::Module)
Esta sección está relacionada con el código de portabilidad que usa 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 crea un nuevo tipo de proyecto componente de Windows Runtime (C++/WinRT) en Visual Studio y compila, 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, recurren de forma condicional a WRL para darle soporte, sea cual sea la fase del proceso de adaptación 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 directamente a Module::GetActivationFactory , 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 directamente a Module::Terminate , 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;
}
Migración de los wrappers de Microsoft::WRL::Wrappers
Esta sección está relacionada con el código de portabilidad que usa los contenedores Microsoft::WRL::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 los envoltorios de WRL podría ser engañosa, ya que la elección depende de sus necesidades. Además, algunos tipos que podrían parecer correspondencias evidentes son nuevos en el estándar C++20, por lo que esas correspondencias no resultarán prácticas si aún no has actualizado.
| Type | Notas de portabilidad |
|---|---|
| clase CriticalSection | Utilice la biblioteca de compatibilidad con hilos |
| Clase de eventos (WRL) | Usar la plantilla de estructura winrt::event |
| Clase HandleT | Use la estructura winrt::handle o la estructura winrt::file_handle |
| clase HString | Use la estructura winrt::hstring |
| clase HStringReference | Ningún 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. |
| Clase Mutex | Use la biblioteca de compatibilidad con hilos |
| clase RoInitializeWrapper | Utilice winrt::init_apartment y winrt::uninit_apartment; o escriba su propio envoltorio sencillo para CoInitializeEx y CoUninitialize. |
| Clase Semaphore | Usar la biblioteca de soporte para subprocesos |
| Clase SRWLock | Utilice la biblioteca de compatibilidad con hilos |