Dela via


Flytta till C++/WinRT från WRL

Det här avsnittet visar hur du portar Windows Runtime C++ Template Library (WRL) kod 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-stöd för C++/WinRT). Det gör du genom att installera Microsoft.Windows.CppWinRT NuGet-paketet i projektet. Öppna projektet i Visual Studio, klicka på Project>Manage NuGet Packages...>Browse, type or paste Microsoft.Windows.CppWinRT i sökrutan, välj objektet i sökresultatet och klicka sedan på Installera för att installera paketet för projektet. En effekt av ändringen är att stöd 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-). Du kan också aktivera stödet igen (i projektegenskaper, C/C++>Allmänt>Använda 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 XAML-kompilatorstöd och Windows Runtime-komponenter (se Flytta till C++/WinRT från C++/CX-).

Ange projektegenskapen General>Target Platform Version 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 C++/WinRT-projicerade Windows API-huvuden (till exempel winrt/Windows.Foundation.h) behöver du inte uttryckligen ta med winrt/base.h så här eftersom det inkluderas automatiskt åt dig.

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

Överför 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 för hämtar medlemfunktionen com_ptr::put den underliggande råpekaren så att den kan anges.

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 sitter (dess interna råpekare redan har ett mål) och du vill placera den på nytt för att peka på ett annat objekt, måste du först tilldela nullptr till det , som visas i kodexemplet nedan. Om du inte gör det kommer en redan inställd com_ptr att informera dig om problemet (när du anropar com_ptr::put eller com_ptr::put_void) genom att säkerställa 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 efter version) 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 den fria funktionen winrt::get_unknown, som visas i nästa exempel.

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

Portering av en WRL-modul (Microsoft::WRL::Module)

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

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 Windows Runtime-komponent (C++/WinRT) projekttyp i Visual Studio och skapar, 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, i stället för att anropa Module::GetActivationFactory direkt, anropar du WINRT_GetActivationFactory (som anropar WRL-funktionen internt). 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;
}

Portning av Microsoft::WRL::Wrappers wrappers

Det här avsnittet gäller portning av kod som använder Microsoft::WRL::Wrappers inslagningar.

Som du kan se i tabellen nedan, rekommenderar vi att du använder standard C++ trådstödsbibliotek. En en-till-en-mappning från WRL-wrapper 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.

Typ Porteringsanteckningar
CriticalSection-klass Använd trådstödbiblioteket
Händelseklass (WRL) Använd mallen winrt::event struct
HandleT-klass Använd winrt::handle struct eller winrt::file_handle struct
HString-klass Använd winrt::hstring struct
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änd trådstödbiblioteket
RoInitializeWrapper-klass Använd winrt::init_apartment och winrt::uninit_apartment; eller skriv din egen enkla wrapper runt CoInitializeEx och CoUninitialize.
Semaphore-klass Använd trådstödbiblioteket
SRWLock-klass Använd trådstödbiblioteket

Viktiga API:er