从 WRL 移动到 C++/WinRT
本主题介绍了如何将 Windows 运行时 C++ 模板库 (WRL) 代码移植到 C++/WinRT 中的等效项。
移植到 C++/WinRT 的第一步是向项目手动添加 C++/WinRT 支持(请参阅针对 C++/WinRT 的 Visual Studio 支持)。 为此,请在项目中安装 Microsoft.Windows.CppWinRT NuGet 包。 在 Visual Studio 中打开项目,单击“项目”>“管理 NuGet 包...”>“浏览”,在搜索框中键入或粘贴“Microsoft.Windows.CppWinRT”,在搜索结果中选择该项,然后单击“安装”以安装该项目的包。 这一更改的一个效果是对 C++/CX 的支持在项目中关闭。 如果你在项目中使用 C++/CX,那么你可以让支持保持关闭状态,同时将 C++/CX 代码更新到 C++/WinRT(请参阅从 C++/CX 移动到 C++/WinRT)。 或者你可以重新打开支持(在项目属性中,“C/C++”>“常规”>“使用 Windows 运行时扩展”>“是(/ZW)”),并首先关注移植 WRL 代码。 C++/CX 和 C++/WinRT 代码可以在同一个项目中共存,但是 XAML 编译器支持和 Windows 运行时组件除外(请参阅从 C++/CX 移动到 C++/WinRT)。
将项目属性“常规”>“目标平台版本”设置为 10.0.17134.0(Windows 10 版本 1803)或更高版本。
在预编译的标头文件(通常为 pch.h
)中,包括 winrt/base.h
。
#include <winrt/base.h>
如果你包括了任何 C++/WinRT 投影 Windows API 标头(例如,winrt/Windows.Foundation.h
),那么你无需像这样明确包括 winrt/base.h
,因为它将自动为你包含在内。
移植 WRL COM 智能指针 (Microsoft::WRL::ComPtr)
移植任何使用 Microsoft::WRL::ComPtr<T> 的代码以使用 winrt::com_ptr<T>。 下面是之前和之后的代码示例。 在之后的版本中,com_ptr::put 成员函数检索基础原始指针,以便可以进行设置。
ComPtr<IDXGIAdapter1> previousDefaultAdapter;
DX::ThrowIfFailed(m_dxgiFactory->EnumAdapters1(0, &previousDefaultAdapter));
winrt::com_ptr<IDXGIAdapter1> previousDefaultAdapter;
winrt::check_hresult(m_dxgiFactory->EnumAdapters1(0, previousDefaultAdapter.put()));
重要
如果某个 winrt::com_ptr 已定位(其内部原始指针已有目标),而你想要将其重新定位为指向不同的对象,则首先需要向其分配 nullptr
,如以下代码示例所示。 否则,已定位的 com_ptr 会断言其内部指针不为 null,从而产生需要关注的问题(调用 com_ptr::put 或 com_ptr::put_void 时)。
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())
);
在下一个示例中(之后版本),com_ptr::put_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();
}
将 ComPtr::Get 替换为 com_ptr::get。
m_d3dDevice->CreateDepthStencilView(m_depthStencil.Get(), &dsvDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
m_d3dDevice->CreateDepthStencilView(m_depthStencil.get(), &dsvDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
当你想要将基础原始指针传递到预期 IUnknown 指针的函数时,应使用 winrt::get_unknown 自由函数,如下一个示例所示。
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()
)
);
移植 WRL 模块 (Microsoft::WRL::Module)
本部分涉及移植使用 Microsoft::WRL::Module 类型的代码。
可以将 C++/WinRT 代码逐渐添加到使用 WRL 来实现组件的现有项目,现有的 WRL 类会继续受支持。 此部分显示如何操作。
如果你在 Visual Studio 中创建一个新的 Windows 运行时组件 (C++/WinRT) 投影类型并生成,则将为你生成文件 Generated Files\module.g.cpp
。 该文件包含两个有用的 C++/WinRT 函数的定义(下方已列出),你可以将其复制并添加到项目中。 这些函数是 WINRT_CanUnloadNow 和 WINRT_GetActivationFactory,如你所见,它们有条件地调用 WRL 以便为你提供支持,不论你正处于哪个移植阶段。
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(); }
}
在项目中加入这些函数后,不要直接调用 Module::GetActivationFactory,而应调用 WINRT_GetActivationFactory(这内部调用 WRL 函数)。 下面是之前和之后的代码示例。
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));
}
不要直接调用 Module::Terminate,而应调用 WINRT_CanUnloadNow(这将内部调用 WRL 函数)。 下面是之前和之后的代码示例。
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;
}
移植 Microsoft::WRL::Wrappers 包装器
本部分涉及移植使用 Microsoft::WRL::Wrappers 包装器的代码。
如下表所示,若要替换线程处理帮助程序,建议使用标准 C++ 线程支持库。 WRL 包装器中的一对一映射可能会产生误导,因为所选内容取决于需求。 此外,某些看起来显而易见的映射类型是 C++20 标准的新增内容,因此,如果尚未升级,将无法使用这些类型。
类型 | 移植说明 |
---|---|
CriticalSection 类 | 使用线程支持库 |
Event 类 (WRL) | 使用 winrt::event 结构模板 |
HandleT 类 | 使用 winrt::handle 结构或 winrt::file_handle 结构 |
HString 类 | 使用 winrt::hstring 结构 |
HStringReference 类 | 无需替换,因为 C++/WinRT 在内部处理此情况,处理方式与 HStringReference 一样高效,并且你完全无需担心。 |
Mutex 类 | 使用线程支持库 |
RoInitializeWrapper 类 | 使用 winrt::init_apartment 和 winrt::uninit_apartment;或围绕 CoInitializeEx 和 CoUninitialize 编写你自己的普通包装器 。 |
Semaphore 类 | 使用线程支持库 |
SRWLock 类 | 使用线程支持库 |