Condividi tramite


Passare a C++/WinRT da WRL

Questo argomento illustra come convertire il codice della Libreria modelli C++ per Windows Runtime (WRL) nell'equivalente in C++/WinRT.

Il primo passaggio nella conversione in C++/WinRT consiste nell'aggiungere manualmente il supporto C++/WinRT al progetto (vedi supporto di Visual Studio per C++/WinRT). A tale scopo, installa il pacchetto NuGet Microsoft.Windows.CppWinRT nel progetto. Aprire il progetto in Visual Studio, fare clic su Progetto>Gestisci pacchetti NuGet...>Sfoglia, digitare o incollare Microsoft.Windows.CppWinRT nella casella di ricerca, selezionare l'elemento nei risultati della ricerca, quindi fare clic su Installa per installare il pacchetto per tale progetto. Un effetto di questa modifica è che il supporto C++/CX è disattivato nel progetto. Se usi C++/CX nel progetto, puoi lasciare il supporto disattivato e aggiornare anche il codice C++/CX a C++/WinRT (vedi Passare a C++/WinRT da C++/CX). Oppure puoi attivare di nuovo il supporto (nelle proprietà del progetto, C/C++>Generale>Utilizza estensioni di Windows Runtime>Sì (/ZW)), e passare prima alla conversione del codice WRL. Il codice C++/CX e il codice C++/WinRT possono coesistere nello stesso progetto, con le eccezioni del supporto del compilatore XAML e dei componenti Windows Runtime (vedi Passare a C++/WinRT da C++/CX).

Imposta le proprietà del progetto Generale>Versione piattaforma di destinazione su 10.0.17134.0 (Windows 10, versione 1803) o versione successiva.

Nel file di intestazione precompilata (in genere pch.h), includi winrt/base.h.

#include <winrt/base.h>

Se includi qualsiasi intestazione API Windows proiettata C++/WinRT (ad esempio, winrt/Windows.Foundation.h), non devi includere in modo esplicito winrt/base.h in questo modo, poiché verrà incluso automaticamente.

Conversione di puntatori intelligenti COM WRL (Microsoft:: WRL::ComPtr)

Converti il codice che usa Microsoft::WRL::ComPtr<T> in modo che usi winrt::com_ptr<T>. Ecco un esempio di codice prima e dopo. Nella versione dopo, la funzione membro com_ptr::put recupera il puntatore non elaborato sottostante in modo da poterlo impostare.

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

Importante

Se hai un puntatore winrt::com_ptr già collocato (il relativo puntatore non elaborato interno ha già una destinazione) e destinarlo ridestinarlo in modo che punti a un oggetto diverso, devi prima di tutto assegnare nullptr al puntatore, come illustrato nell'esempio di codice seguente. In caso contrario, un com_ptr già collocato porterà il problema alla tua attenzione (quando chiami com_ptr::put oppure com_ptr:: put_void) indicando che il relativo puntatore interno non è 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())
);

Nell'esempio seguente (nella versione dopo), la funzione membro com_ptr::put_void recupera il puntatore non elaborato sottostante come un puntatore a un puntatore 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();
}

Sostituisci ComPtr::Get con com_ptr::get.

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

Quando vuoi passare il puntatore non elaborato sottostante a una funzione che prevede un puntatore a IUnknown, usa la funzione libera winrt::get_unknown, come illustrato nell'esempio seguente.

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

Conversione di un modulo WRL (Microsoft:: WRL::Module)

Questa sezione si riferisce al codice di conversione che usa il tipo Microsoft::WRL::Module.

Puoi aggiungere gradualmente codice C++/WinRT a un progetto esistente che utilizza WRL per implementare un componente e le classi WRL esistenti continueranno a essere supportate. Questa sezione illustra come.

Se crei un nuovo tipo di progetto Componente Windows Runtime (C++/WinRT) in Visual Studio e passi allo sviluppo, il file Generated Files\module.g.cpp viene generato automaticamente. Questo file contiene le definizioni di due utili funzioni C++/WinRT (elencate di seguito), che puoi copiare e aggiungere al progetto. Tali funzioni sono WINRT_CanUnloadNow e WINRT_GetActivationFactory e, come puoi vedere, chiamano in modo condizionale WRL per supportarti in qualsiasi fase di conversione ti trovi.

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

Dopo avere incluso queste funzioni nel progetto, invece di chiamare Module::GetActivationFactory direttamente, chiama WINRT_GetActivationFactory (che chiama internamente la funzione WRL). Ecco un esempio di codice prima e dopo.

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

Invece di chiamare Module::Terminate direttamente, chiama WINRT_CanUnloadNow (che chiama internamente la funzione WRL). Ecco un esempio di codice prima e dopo.

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

Conversione di wrapper Microsoft::WRL::Wrappers

Questa sezione si riferisce al codice di conversione che usa i wrapper Microsoft::WRL::Wrappers.

Come si può notare nella tabella seguente, per sostituire gli helper di threading, è consigliabile usare la libreria di supporto thread C++ Standard. Un mapping uno-a-uno dai wrapper WRL potrebbe essere fuorviante, perché la scelta dipende dalle proprie esigenze. Inoltre, alcuni tipi che potrebbero sembrare ovvi mapping sono nuovi per lo standard C++20, quindi questi saranno poco pratici se non è ancora stato aggiornato.

Tipo Conversione delle note
Classe CriticalSection Usare la libreria di supporto thread
Classe di evento (WRL) Usare il modello di struct winrt::event
Classe HandleT Usare lo struct winrt::handle o lo struct winrt::file_handle
Classe HString Usare lo struct winrt::hstring
Classe HStringReference Nessuna sostituzione, perché C++/WinRT gestisce internamente questa operazione in modo altrettanto efficiente come HStringReference con il vantaggio che non devi pensarci.
Classe Mutex Usare la libreria di supporto thread
Classe RoInitializeWrapper Usare winrt::init_apartment e winrt::uninit_apartmentoppure scrivere un wrapper semplice relativamente a CoInitializeEx e CoUninitialize.
Classe Semaphore Usare la libreria di supporto thread
Classe SRWLock Usare la libreria di supporto thread

API importanti