Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo argomento illustra come eseguire il porting del codice Windows Runtime C++ Template Library (WRL) nel relativo equivalente in C++/WinRT.
Il primo passaggio della conversione in C++/WinRT consiste nell'aggiungere manualmente il supporto C++/WinRT al progetto (vedere Visual Studio supporto per C++/WinRT). Per farlo, installa il pacchetto NuGet Microsoft.Windows.CppWinRT nel tuo progetto. Aprire il project in Visual Studio, fare clic su Project>Gestisci pacchetti NuGet...>Sfoglia, digita o incolla Microsoft.Windows. CppWinRT nella casella di ricerca, selezionare l'elemento nei risultati della ricerca e quindi fare clic su Installa per installare il pacchetto per tale project. Un effetto di tale modifica è che il supporto per C++/CX è disattivato nel progetto. Se usi C++/CX nel progetto, puoi lasciare disattivato il supporto e aggiornare il codice C++/CX anche in C++/ WinRT (vedi Passare a C++/WinRT da C++/CX). In alternativa, è possibile riattivare il supporto (nelle proprietà del progetto, C/C++>General>Consume Windows Runtime Extension>Yes (/ZW) e concentrarsi innanzitutto sulla conversione del codice WRL. Il codice C++/CX e C++/WinRT può coesistere nello stesso progetto, ad eccezione del supporto del compilatore XAML e dei componenti Windows Runtime (vedere Passare a C++/WinRT da C++/CX).
Impostare la proprietà del progetto Generale>Versione della piattaforma di destinazione su 10.0.17134.0 (Windows 10, versione 1803) o versione successiva.
Nel file header precompilato (in genere pch.h), includi winrt/base.h.
#include <winrt/base.h>
Se includi header dell'API di Windows proiettati da C++/WinRT (ad esempio winrt/Windows.Foundation.h), non è necessario includere esplicitamente winrt/base.h come segue, perché verrà incluso automaticamente.
Conversione di puntatori intelligenti COM WRL (Microsoft::WRL::ComPtr)
Convertire qualsiasi codice che usa Microsoft::WRL::ComPtr<T> per usare winrt::com_ptr<T>. Ecco un esempio di codice prima e dopo. Nella versione successiva, la funzione membro com_ptr::put recupera il puntatore raw 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 winrt::com_ptr già impostato (il suo puntatore raw interno punta già a una destinazione) e vuoi reimpostarlo in modo che punti a un oggetto diverso, devi prima assegnargli nullptr, come illustrato nell'esempio di codice seguente. In caso contrario, un com_ptr già inizializzato richiamerà la tua attenzione sul problema (quando chiami com_ptr::put o com_ptr::put_void) tramite un'asserzione che segnala che il suo puntatore interno non è nullo.
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())
);
In questo esempio successivo (nella versione after), la funzione membro com_ptr::put_void recupera il puntatore raw sottostante come puntatore a puntatore a 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();
}
Sostituire 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 si vuole passare il puntatore non elaborato sottostante a una funzione che prevede un puntatore a IUnknown, usare la funzione gratuita winrt::get_unknown , come illustrato in questo esempio successivo.
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()
)
);
Migrazione di un modulo WRL (Microsoft::WRL::Module)
Questa sezione si riferisce al codice di conversione che usa il tipo Microsoft::WRL::Module.
È possibile aggiungere gradualmente codice C++/WinRT a un progetto esistente che usa 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 compila, il file Generated Files\module.g.cpp viene generato automaticamente. Questo file contiene le definizioni di due funzioni C++/WinRT utili (elencate di seguito), che è possibile copiare e aggiungere al progetto. Queste funzioni sono WINRT_CanUnloadNow e WINRT_GetActivationFactory e, come puoi vedere, richiamano condizionalmente WRL per supportarti in qualunque fase del porting 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 aver creato queste funzioni nel progetto, invece di chiamare direttamente Module::GetActivationFactory , chiamare 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));
}
Anziché chiamare direttamente Module::Terminate , chiamare 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 Microsoft::WRL::Wrapper
Questa sezione riguarda il codice di porting che usa i Microsoft::WRL::Wrappers wrappers.
Come si può notare nella tabella seguente, per sostituire gli helper di threading, è consigliabile usare la libreria di supporto thread C++ Standard. Una corrispondenza uno a uno a partire dai wrapper WRL potrebbe essere fuorviante, poiché la scelta dipende dalle proprie necessità. Inoltre, alcuni tipi che potrebbero sembrare corrispondenze ovvie sono stati introdotti con lo standard C++20, quindi risultano poco pratici se non hai ancora effettuato l’aggiornamento.
| Type | Conversione delle note |
|---|---|
| Classe CriticalSection | Usare la libreria di supporto per i thread |
| Classe di evento (WRL) | Usa il template di struct winrt::event |
| Classe HandleT | Usare la struttura winrt::handle o la struttura winrt::file_handle |
| Classe HString | Usare la 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 per i thread |
| Classe RoInitializeWrapper | Usare winrt::init_apartment e winrt::uninit_apartment; oppure scrivere un semplice wrapper intorno a CoInitializeEx e CoUninitialize. |
| Classe Semaforo | Usare la libreria di supporto per i thread |
| Classe SRWLock | Usare la libreria di supporto per i thread |