이 주제에서는 Windows 런타임 C++ 템플릿 라이브러리(WRL) 코드를 C++/WinRT에 해당하는 코드로 옮기는 방법을 보여 줍니다.
C++/WinRT로 포팅하는 첫 번째 단계는 프로젝트에 C++/WinRT 지원을 수동으로 추가하는 것입니다( C++/WinRT에 대한 Visual Studio 지원 참조). 이렇게 하려면 프로젝트에 Microsoft.Windows.CppWinRT NuGet 패키지를 설치합니다. Visual Studio에서 프로젝트를 열고
프로젝트 속성 일반>대상 플랫폼 버전을 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
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())
);
다음 예제( 버전 이후의
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
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 형식을 사용하는 코드 포팅과 관련이 있습니다.
WRL을 사용하여 구성 요소를 구현하는 기존 프로젝트에 C++/WinRT 코드를 점진적으로 추가할 수 있으며 기존 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를 직접 호출하는 대신, 내부적으로 WRL 함수를 호출하는 WINRT_CanUnloadNow를 호출합니다. 다음은 전후 코드 예제입니다.
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::래퍼 래퍼
이 섹션은 Microsoft::WRL::Wrappers 래퍼를 사용하는 코드를 이식하는 것과 관련이 있습니다.
아래 표에서 볼 수 있듯이 스레딩 도우미를 바꾸려면 표준 C++ 스레드 지원 라이브러리를 사용하는 것이 좋습니다. WRL 래퍼의 일대일 매핑은 사용자의 필요에 따라 선택이 달라질 수 있으므로 혼란을 초래할 수 있습니다. 또한 명백한 매핑으로 보일 수 있는 일부 형식은 C++20 표준에 새로 추가되었으므로 아직 업그레이드하지 않은 경우 비실용적입니다.
유형 | 포팅 관련 노트 |
---|---|
CriticalSection 클래스 | 스레드 지원 라이브러리 사용 |
이벤트 클래스(WRL) | winrt::event 구조체 템플릿 사용 |
HandleT 클래스 | winrt::handle 구조체 또는 winrt::file_handle 구조체 사용 |
HString 클래스 | winrt::hstring 구조체 사용 |
HStringReference 클래스 | C++/WinRT는 HStringReference 만큼 효율적인 방식으로 이를 내부적으로 처리하므로, 사용자가 신경 쓸 필요가 없다는 장점이 있어 교체할 필요가 없습니다. |
뮤텍스 클래스 | 스레드 지원 라이브러리 사용 |
RoInitializeWrapper 클래스 | winrt::init_apartment 및 winrt::uninit_apartment를 사용하거나 CoInitializeEx 및 CoUninitialize을 위한 간단한 래퍼를 작성합니다. |
세마포 클래스 | 스레드 지원 라이브러리 사용 |
SRWLock 클래스 | 스레드 지원 라이브러리 사용 |