WRL から C++/WinRT への移行

このトピックでは、Windows ランタイム C++ Template Library (WRL) コードを C++/WinRT での同等のコードに移植する方法について説明します。

C++/WinRT への移植の最初の手順は、C++/WinRT のサポートをプロジェクトに手動で追加することです (C++/WinRT のサポートVisual Studio参照)。 これを行うには、Microsoft.Windows.CppWinRT NuGet パッケージをプロジェクトにインストールします。 Visual Studio でプロジェクトを開き、Project>Manage NuGet Packages...>Browse をクリックし、検索ボックスに Microsoft.Windows.CppWinRT と入力または貼り付けて、検索結果の項目を選択し、Install をクリックしてそのプロジェクトにパッケージをインストールします。 その変更の 1 つの効果は、 C++/CX のサポートがプロジェクトでオフになっているということです。 プロジェクトで C++/CX を使用している場合は、サポートをオフのままにして、C++/CX コードを C++/WinRT に更新することもできます (C++ /CX から C++/WinRT への移動を参照)。 または、サポートを再度有効にして (プロジェクト プロパティ、C/C++>General>Consume Windows ランタイム Extension>Yes (/ZW)) を有効にして、最初に WRL コードの移植に重点を置くことができます。 C++/CX と C++/WinRT コードは、XAML コンパイラのサポートとWindows ランタイムコンポーネントを除き、同じプロジェクト内に共存できます (C++/CX から C++/WinRT への移動を参照)。

プロジェクト プロパティ General>Target Platform Version を 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::p ut メンバー関数は、基になる生ポインターを取得して設定できるようにします。

ComPtr<IDXGIAdapter1> previousDefaultAdapter;
DX::ThrowIfFailed(m_dxgiFactory->EnumAdapters1(0, &previousDefaultAdapter));
winrt::com_ptr<IDXGIAdapter1> previousDefaultAdapter;
winrt::check_hresult(m_dxgiFactory->EnumAdapters1(0, previousDefaultAdapter.put()));

Von Bedeutung

既に座っている 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())
);

次の例(after バージョン)では、com_ptr::put_void メンバー関数は、基になる生ポインターを 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 free 関数を使用します。

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()
    )
);

WRL モジュールの移植 (Microsoft::WRL::Module)

このセクションでは、Microsoft::WRL::Module 型を使用するコードの移植に関連します。

WRL を使用してコンポーネントを実装する既存のプロジェクトに C++/WinRT コードを徐々に追加できます。既存の WRL クラスは引き続きサポートされます。 このセクションでは、その方法を示します。

Visual Studioで新しいWindows ランタイム コンポーネント (C++/WinRT) プロジェクトの種類を作成し、ビルドすると、ファイル Generated Files\module.g.cppが自動的に生成されます。 このファイルには、2 つの便利な C++/WinRT 関数 (以下に示す) の定義が含まれています。この関数は、プロジェクトにコピーして追加できます。 これらの関数は WINRT_CanUnloadNowWINRT_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 ラッパーを使用するコードの移植に関連します。

次の表に示すように、スレッド ヘルパーを置き換えるには、Standard C++ スレッド サポート ライブラリを使用することをお勧めします。 WRL ラッパーからの 1 対 1 のマッピングは、ニーズによって異なるため、誤解を招く可能性があります。 また、明らかなマッピングと思われる型の中には、C++20 標準の新しい型もあるため、まだアップグレードしていない場合は実用的ではありません。

タイプ 移植に関する注意事項
CriticalSection クラス スレッド サポート ライブラリを使用する
イベント クラス (WRL) winrt::event 構造体テンプレートを使用する
HandleT クラス winrt::handle 構造体または winrt::file_handle 構造体を使用する
HString クラス winrt::hstring 構造体を使用する
HStringReference クラス C++/WinRT は、 HStringReference と同じくらい効率的な方法で内部的に処理するため、代わりには使用できません。その利点を考慮する必要はありません。
Mutex クラス スレッド サポート ライブラリを使用する
RoInitializeWrapper クラス winrt::init_apartmentwinrt::uninit_apartment; を使用します。または、CoInitializeExCoUninitialize に関する独自の簡易ラッパーを記述します。
Semaphore クラス スレッド サポート ライブラリを使用する
SRWLock クラス スレッド サポート ライブラリを使用する

重要な API