WRL에서 C++/WinRT로 이동Move to C++/WinRT from WRL

Windows 런타임 C++ 템플릿 라이브러리(WRL) 코드를 C++/WinRT의 상응하는 코드로 포팅하는 방법을 보여줍니다.This topic shows how to port Windows Runtime C++ Template Library (WRL) code to its equivalent in C++/WinRT.

C++/WinRT로 포팅하는 첫 번째 단계는 수동으로 C++/WinRT 지원을 프로젝트에 추가하는 것입니다(C++/WinRT에 대한 Visual Studio 지원 참조).The first step in porting to C++/WinRT is to manually add C++/WinRT support to your project (see Visual Studio support for C++/WinRT). 이 작업을 수행하려면 Microsoft.Windows.CppWinRT NuGet 패키지를 프로젝트에 설치합니다.To do that, install the Microsoft.Windows.CppWinRT NuGet package into your project. Visual Studio에서 프로젝트를 열고 프로젝트 > NuGet 패키지 관리... > 찾아보기를 클릭합니다. 검색 상자에 Microsoft.Windows.CppWinRT를 입력하거나 붙여넣고 검색 결과에서 항목을 선택한 다음, 설치를 클릭하여 해당 프로젝트용 패키지를 설치합니다.Open the project in Visual Studio, click Project > Manage NuGet Packages... > Browse, type or paste Microsoft.Windows.CppWinRT in the search box, select the item in search results, and then click Install to install the package for that project. 해당 변경의 효과 중 하나는 프로젝트에서 C++/CX에 대한 지원이 꺼진다는 것입니다.One effect of that change is that support for C++/CX is turned off in the project. 프로젝트에서 C++/CX를 사용 중인 경우 지원을 끈 채로 유지하고 C++/CX 코드를 C++/WinRT에도 업데이트할 수 있습니다(C++/CX에서 C++/WinRT로 이동 참조).If you're using C++/CX in the project, then you can leave support turned off and update your C++/CX code to C++/WinRT as well (see Move to C++/WinRT from C++/CX). 또는 지원을 다시 켜고(프로젝트 속성에서 C/C++ > 일반 > Windows 런타임 확장 사용 > 예(/ZW) ) 먼저 WRL 코드 포트에 집중할 수 있습니다.Or you can turn support back on (in project properties, C/C++ > General > Consume Windows Runtime Extension > Yes (/ZW)), and first focus on porting your WRL code. C++/CX 및 C++/WinRT 코드는 같은 프로젝트에 공존할 수 있으며, XAML 컴파일러 지원 및 Windows 런타임 구성 요소의 예외가 있습니다(C++/CX에서 C++/WinRT로 이동 참조).C++/CX and C++/WinRT code can coexist in the same project, with the exception of XAML compiler support, and Windows Runtime components (see Move to C++/WinRT from C++/CX).

프로젝트 속성 일반 > 대상 플랫폼 버전을 10.0.17134.0(Windows 10 버전 1803) 이상으로 설정합니다.Set project property General > Target Platform Version to 10.0.17134.0 (Windows 10, version 1803) or greater.

컴파일된 헤더 파일에(일반적으로 pch.h) winrt/base.h를 포함합니다.In your precompiled header file (usually pch.h), include winrt/base.h.

#include <winrt/base.h>

C++/WinRT 프로젝션된 Windows API 헤더를 포함하는 경우(예: winrt/Windows.Foundation.h) 자동으로 포함되기 때문에 명시적으로 이와 같은 winrt/base.h를 포함할 필요가 없습니다.If you include any C++/WinRT projected Windows API headers (for example, winrt/Windows.Foundation.h), then you don't need to explicitly include winrt/base.h like this because it will be included automatically for you.

WRL COM 스마트 포인터 포팅(Microsoft: WRL::ComPtr)Porting WRL COM smart pointers (Microsoft::WRL::ComPtr)

Microsoft::WRL::ComPtr<T> 를 사용하는 코드를 포팅하여 winrt::com_ptr<T> 를 사용합니다.Port any code that uses Microsoft::WRL::ComPtr<T> to use winrt::com_ptr<T>. 다음은 코드 이전과 이후의 예제입니다.Here's a before-and-after code example. 이후 버전에서 com_ptr::put 멤버 함수는 기본 원시 포인터를 설정할 수 있도록 검색합니다.In the after version, the com_ptr::put member function retrieves the underlying raw pointer so that it can be set.

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을 먼저 할당해야 합니다.If you have a winrt::com_ptr that's already seated (its internal raw pointer already has a target) and you want to re-seat it to point to a different object, then you first need to assign nullptr to it—as shown in the code example below. 할당하지 않으면, com_ptr::put 또는 com_ptr::put_void를 호출할 때 이미 배치된 com_ptr에서 내부 포인터가 Null이 아님을 어설션하여 문제가 부각됩니다.If you don't, then an already-seated com_ptr will draw the issue to your attention (when you call com_ptr::put or com_ptr::put_void) by asserting that its internal pointer is not null.

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 멤버 함수는 피할 포인터에 대한 포인터로 기본 원시 포인터를 검색합니다.In this next example (in the after version), the com_ptr::put_void member function retrieves the underlying raw pointer as a pointer to a pointer to 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::Getcom_ptr::get으로 바꿉니다.Replace ComPtr::Get with 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 프리 함수를 사용합니다.When you want to pass the underlying raw pointer to a function that expects a pointer to IUnknown, use the winrt::get_unknown free function, as shown in this next example.

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)Porting a WRL module (Microsoft::WRL::Module)

C++/WinRT 코드를 구성 요소를 구현하기 위해 WRL을 사용하는 기존 프로젝트에 서서히 추가할 수 있으며 기존 WRL 클래스는 계속 지원됩니다.You can gradually add C++/WinRT code to an existing project that uses WRL to implement a component, and your existing WRL classes will continue to be supported. 이 섹션에서는 그 방법을 보여줍니다.This section shows how.

Visual Studio에서 새 Windows 런타임 구성 요소(C++/WinRT) 프로젝트 형식을 만드는 경우 Generated Files\module.g.cpp 파일이 생성됩니다.If you create a new Windows Runtime Component (C++/WinRT) project type in Visual Studio, and build, then the file Generated Files\module.g.cpp is generated for you. 해당 파일은 프로젝트에 복사하고 추가할 수 있는 두 가지 유용한 C++/WinRT 함수(아래에 나열)의 정의를 포함합니다.That file contains the definitions of two useful C++/WinRT functions (listed out below), which you can copy and add to your project. 이러한 함수는 WINRT_CanUnloadNowWINRT_GetActivationFactory이며, 보시다시피 조건에 따라 사용자가 포팅하는 스테이지를 지원하기 위한 순서로 WRL을 호출합니다.Those function are WINRT_CanUnloadNow and WINRT_GetActivationFactory and, as you can see, they conditionally call WRL in order to support you whatever stage of porting you're at.

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 함수를 호출)를 호출합니다.Once you have these functions in your project, instead of calling Module::GetActivationFactory directly, call WINRT_GetActivationFactory (which calls the WRL function internally). 다음은 코드 이전과 이후의 예제입니다.Here's a before-and-after code example.

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 함수를 호출)를 호출합니다.Instead of calling Module::Terminate directly, call WINRT_CanUnloadNow (which calls the WRL function internally). 다음은 코드 이전과 이후의 예제입니다.Here's a before-and-after code example.

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

중요 APIImportant APIs