從 WRL 移到 C++/WinRT
本主題示範如何將 Windows 執行階段 C++ 範本庫 (WRL) 程式碼移植到其在 C++/WinRT 中的對等項目。
移植 C+/WinRT 中的第一個步驟是手動新增 C++/WinRT 支援您的專案 (請參閱 Visual Studio 支援 C++/WinRT)。 若要這麼做,請將 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: 110:: 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();
}
使用 com_ptr::get 取代 ComPtr::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: 110:: 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));
}
呼叫 WINRT_CanUnloadNow( 其內部呼叫 WRL 函式),而非直接呼叫 Module::Terminate。 以下是變更前後的程式碼範例。
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 類別 | 使用執行緒支援程式庫 |
事件類別 (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 類別 | 使用執行緒支援程式庫 |