Flytta till C++/WinRT från WRL

Det här avsnittet visar hur du porterar kod för Windows Runtime C++-mallbibliotek (WRL) till dess motsvarighet i C++/WinRT.

Det första steget i att portera till C++/WinRT är att manuellt lägga till C++/WinRT-stöd i projektet (se Visual Studio support för C++/WinRT). Det gör du genom att installera Microsoft.Windows. CppWinRT NuGet-paketet i projektet. Öppna project i Visual Studio, klicka på Project>Hantera NuGet-paket...>Bläddra, skriv eller klistra in Microsoft.Windows. CppWinRT i sökrutan, välj objektet i sökresultaten och klicka sedan på Installera för att installera paketet för den project. En effekt av ändringen är att stödet för C++/CX är inaktiverat i projektet. Om du använder C++/CX i projektet kan du lämna supporten avstängd och uppdatera C++/CX-koden till C++/WinRT också (se Flytta till C++/WinRT från C++/CX). Eller så kan du aktivera stödet igen (i projektegenskaper, C/C++>Allmänt>förbruka Windows Runtime Tillägg>Ja (/ZW)) och först fokusera på att portera DIN WRL-kod. C++/CX- och C++/WinRT-kod kan samexistera i samma projekt, med undantag för stöd för XAML-kompilator och Windows Runtime komponenter (se Flytta till C++/WinRT från C++/CX).

Ange den allmänna>målplattformsversionen för projektegenskapen till 10.0.17134.0 (Windows 10 version 1803) eller senare.

I den förkompilerade huvudfilen (vanligtvis pch.h) tar du med winrt/base.h.

#include <winrt/base.h>

Om du inkluderar några C++/WinRT-genererade headerfiler för Windows API (till exempel winrt/Windows.Foundation.h), behöver du inte uttryckligen inkludera winrt/base.h på det här sättet, eftersom den inkluderas automatiskt.

Portning av smarta WRL COM-pekare (Microsoft::WRL::ComPtr)

Portera all kod som använder Microsoft::WRL::ComPtr<T> för att använda winrt::com_ptr<T>. Här är ett exempel på kod före och efter. I versionen efter hämtar medlemsfunktionen com_ptr::put den underliggande råpekaren så att den kan ställas in.

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

Viktigt!

Om du har en winrt::com_ptr som redan finns (dess interna råpekare redan har ett mål) och du vill placera den på nytt så att den pekar på ett annat objekt, måste du först tilldela nullptr det , som du ser i kodexemplet nedan. Om du inte gör det kommer en redan upptagen com_ptr att göra dig uppmärksam på problemet (när du anropar com_ptr::put eller com_ptr::put_void) genom att utlösa en assertion om att dess interna pekare inte är 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())
);

I nästa exempel (i after-versionen) hämtar medlemsfunktionen com_ptr::put_void den underliggande råpekaren som en pekare till en pekare till 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();
}

Ersätt ComPtr::Get med com_ptr::get.

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

När du vill skicka den underliggande råpekaren till en funktion som förväntar sig en pekare till IUnknown använder du funktionen winrt::get_unknown free, som du ser i nästa exempel.

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

Porta en WRL-modul (Microsoft::WRL::Module)

Det här avsnittet gäller portningskod som använder Microsoft::WRL::Modultyp.

Du kan gradvis lägga till C++/WinRT-kod i ett befintligt projekt som använder WRL för att implementera en komponent, och dina befintliga WRL-klasser kommer att fortsätta att stödjas. Det här avsnittet visar hur.

Om du skapar en ny projekttyp av typen Windows Runtime-komponent (C++/WinRT) i Visual Studio och bygger projektet, genereras filen Generated Files\module.g.cpp åt dig. Filen innehåller definitionerna av två användbara C++/WinRT-funktioner (som visas nedan), som du kan kopiera och lägga till i projektet. Dessa funktioner är WINRT_CanUnloadNow och WINRT_GetActivationFactory , och som du ser anropar de villkorligt WRL för att stödja dig oavsett vilket portningsstadium du befinner dig på.

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

När du har dessa funktioner i projektet anropar du WINRT_GetActivationFactory (som anropar WRL-funktionen internt i stället för att anropa Modul::GetActivationFactory direkt). Här är ett exempel på kod före och efter.

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

I stället för att anropa Modul::Avsluta direkt anropar du WINRT_CanUnloadNow (som anropar WRL-funktionen internt). Här är ett exempel på kod före och efter.

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

Migrering av Microsoft::WRL::Wrappers-omslutningsklasser

Det här avsnittet gäller portningskod som använder Microsoft::WRL::Wrappers-omslutningar.

Som du ser i tabellen nedan rekommenderar vi att du använder standardsupportbiblioteket för C++-trådar för att ersätta trådhjälparna. En en-till-en-mappning från WRL-omslutningarna kan vara missvisande eftersom ditt val beror på dina behov. Vissa typer som kan verka uppenbara mappningar är också nya för C++20-standarden, så de är opraktiska om du inte har uppgraderat ännu.

Type Portningsanteckningar
CriticalSection-klass Använda trådstödbiblioteket
Händelseklass (WRL) Använd strukturmallen winrt::event
HandleT-klass Använd winrt::handle struct eller winrt::file_handle struct
HString-klass Använd structen winrt::hstring
HStringReference-klass Ingen ersättning, eftersom C++/WinRT hanterar detta internt på ett sätt som är lika effektivt som HStringReference med fördelen att du inte behöver tänka på det.
Mutex-klass Använda trådstödbiblioteket
RoInitializeWrapper-klass Använd winrt::init_apartment och winrt::uninit_apartment; eller skriv din egen triviala wrapper runt CoInitializeEx och CoUninitialize.
Semaforklass Använda trådstödbiblioteket
SRWLock-klass Använda trådstödbiblioteket

Viktiga API:er