Sdílet prostřednictvím


Přechod na C++/WinRT z WRL

Toto téma ukazuje, jak portovat kód knihovny šablony Windows Runtime C++ (WRL) na ekvivalent v C++/WinRT.

Prvním krokem při přenosu do C++/WinRT je ruční přidání podpory C++/WinRT do projektu (viz podpora sady Visual Studio pro C++/WinRT). Uděláte to tak, že do projektu nainstalujete balíček NuGet Microsoft.Windows.CppWinRT . Otevřete projekt ve Visual Studio, klikněte na Project>Spravovat balíčky NuGet...>Procházet, zadejte nebo vložte Microsoft.Windows.CppWinRT do vyhledávacího pole, vyberte položku ve výsledcích hledání a potom kliknutím na Nainstalovat nainstalujte balíček pro tento projekt. Jedním z důsledků této změny je, že podpora C++/CX je v projektu vypnutá. Pokud v projektu používáte C++/CX, můžete ponechat podporu vypnutou a aktualizovat kód C++/CX na C++/WinRT (viz Přechod na C++/WinRT z C++/CX). Nebo můžete znovu zapnout podporu (ve vlastnostech projektu, C/C++>Obecné>Využívat rozšíření prostředí Windows Runtime>Ano (/ZW)) a nejprve se zaměřit na portování svého kódu WRL. Kód C++/CX a C++/WinRT může existovat ve stejném projektu, s výjimkou podpory kompilátoru XAML a součástí prostředí Windows Runtime (viz Přesun do C++/WinRT z C++/CX).

Nastavte vlastnost projektu Obecné>Verze cílové platformy na 10.0.17134.0 (Windows 10, verze 1803) nebo vyšší.

Do souboru předkompilované hlavičky (obvykle pch.h) zahrňte winrt/base.h.

#include <winrt/base.h>

Pokud zahrnete jakékoli hlavičky rozhraní Windows API C++/WinRT (například winrt/Windows.Foundation.h), nemusíte již explicitně zahrnovat winrt/base.h, protože tato bude automaticky zahrnuta.

Portování inteligentních ukazatelů COM knihovny WRL (Microsoft::WRL::ComPtr)

Portovat jakýkoli kód, který používá Microsoft::WRL::ComPtr<T>, tak aby používal winrt::com_ptr<T>. Tady je příklad kódu před a po. Ve verzi po členka funkce com_ptr::put načte základní nezpracovaný ukazatel tak, aby ho bylo možné nastavit.

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

Důležité

Pokud máte winrt::com_ptr, který je již obsazen (jeho interní holý ukazatel už má cíl) a chcete ho znovu připojit tak, aby odkazoval na jiný objekt, musíte mu nejdřív přiřadit nullptr – jak je znázorněno v příkladu kódu níže. Pokud ne, pak již inicializovaný com_ptr upozorní na tento problém (při volání com_ptr::put nebo com_ptr::put_void) tím, že potvrdí, že jeho vnitřní ukazatel není nulový.

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

V tomto dalším příkladu (v po verzi) funkce člena com_ptr::put_void získá nezpracovaný ukazatel jako ukazatel na ukazatel na 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();
}

Nahraďte ComPtr::Getcom_ptr::get.

m_d3dDevice->CreateDepthStencilView(m_depthStencil.Get(), &dsvDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
m_d3dDevice->CreateDepthStencilView(m_depthStencil.get(), &dsvDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());

Pokud chcete předat základní nezpracovaný ukazatel do funkce, která očekává ukazatel na IUnknown, použijte volnou funkci winrt::get_unknown, jak je znázorněno v dalším příkladu.

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

Přenos modulu WRL (Microsoft::WRL::Module)

Tato část se týká přenosu kódu, který používá typ Microsoft::WRL::Module .

Kód C++/WinRT můžete postupně přidat do existujícího projektu, který k implementaci komponenty používá knihovnu WRL, a vaše stávající třídy knihovny WRL budou nadále podporovány. V této části se dozvíte, jak na to.

Pokud v sadě Visual Studio vytvoříte nový typ projektu komponenta Windows Runtime (C++/WinRT) a sestavíte jej, soubor Generated Files\module.g.cpp je vygenerován automaticky. Tento soubor obsahuje definice dvou užitečných funkcí C++/WinRT (uvedených níže), které můžete zkopírovat a přidat do projektu. Tyto funkce jsou WINRT_CanUnloadNow a WINRT_GetActivationFactory a jak vidíte, podmíněně volají WRL, aby vás podpořily v jakékoli fázi portování, ve které se nacházíte.

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

Jakmile budete mít tyto funkce v projektu, místo volání Module::GetActivationFactory přímo volejte WINRT_GetActivationFactory (která volá funkci WRL interně). Tady je příklad kódu před a po.

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

Místo volání Module::Terminate přímo volejte WINRT_CanUnloadNow (který interně volá funkci WRL). Tady je příklad kódu před a po.

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

Přenos obálky Microsoft::WRL::Wrappers

Tato část se vztahuje na portování kódu, který používá Microsoft::WRL::Wrappers wrappry.

Jak je vidět v následující tabulce, pro nahrazení pomocníků vláken doporučujeme použít standardní knihovnu podpory vláken C++ . Mapování 1:1 z obálky WRL může být zavádějící, protože vaše volba závisí na vašich potřebách. Některé typy, které můžou vypadat jako běžné mapování, jsou pro standard C++20 nové, takže pokud jste ještě neupgradovali, budou nepraktické.

Typ Poznámky k přenosu
CriticalSection – třída Použijte knihovnu podpory pro vlákna
třída událostí (WRL) Použijte šablonu struktury winrt::event
třídy HandleT Použijte strukturu winrt::handle nebo strukturu winrt::file_handle
třídy HString Použijte winrt::hstring struct
třídy HStringReference Žádná náhrada, protože C++/WinRT to interně zpracovává způsobem, který je stejně efektivní jako HStringReference s výhodou, kterou o tom nemusíte přemýšlet.
třídy Mutex Použijte knihovnu podpory pro vlákna
třídy RoInitializeWrapper Použijte winrt::init_apartment a winrt::uninit_apartment; nebo napište vlastní triviální obálku kolem CoInitializeEx a CoUninitialize.
Semaphore třídy Použijte knihovnu podpory pro vlákna
SRWLock – třída Použijte knihovnu podpory pro vlákna

Důležitá rozhraní API