Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym temacie pokazano, jak port biblioteka szablonów języka C++ środowiska uruchomieniowego systemu Windows (WRL) kod do jego odpowiednika w C++/WinRT.
Pierwszym krokiem przenoszenia do języka C++/WinRT jest ręczne dodanie obsługi języka C++/WinRT do projektu (zobacz Obsługa programu Visual Studio dla języka C++/WinRT). W tym celu zainstaluj pakiet NuGet Microsoft.Windows.CppWinRT w projekcie. Otwórz projekt w programie Visual Studio, kliknij Project>Manage NuGet Packages...>Browse, wpisz lub wklej Microsoft.Windows.CppWinRT w polu wyszukiwania, wybierz element w wynikach wyszukiwania, a następnie kliknij Zainstaluj, aby zainstalować pakiet dla tego projektu. Jednym z efektów tej zmiany jest to, że obsługa języka C++/CX jest wyłączona w projekcie. Jeśli używasz języka C++/CX w projekcie, możesz pozostawić obsługę wyłączoną i zaktualizować kod C++/CX również do języka C++/WinRT (zobacz Przenoszenie do języka C++/WinRT z języka C++/CX). Możesz też włączyć ponownie obsługę (we właściwościach projektu C/C++>Ogólne>Korzystanie z rozszerzenia środowiska uruchomieniowego systemu Windows>Tak (/ZW)) i najpierw skupić się na przeniesieniu kodu biblioteki WRL. Kod C++/CX i C++/WinRT może współistnieć w tym samym projekcie, z wyjątkiem obsługi kompilatora XAML i składników środowiska uruchomieniowego systemu Windows (zobacz Przenoszenie do języka C++/WinRT z języka C++/CX).
Ustaw właściwość projektu Ogólna>platforma docelowa na 10.0.17134.0 (Windows 10, wersja 1803) lub nowszą.
W swoim wstępnie skompilowanym pliku nagłówkowym (zwykle pch.h) dołącz winrt/base.h.
#include <winrt/base.h>
Jeśli dołączysz jakiekolwiek nagłówki interfejsu API systemu Windows projektowane w C++/WinRT (na przykład winrt/Windows.Foundation.h), nie musisz jawnie dołączać winrt/base.h, ponieważ zostanie on automatycznie uwzględniony.
Przenoszenie wskaźników inteligentnych COM WRL (Microsoft::WRL::ComPtr)
Przenieś dowolny kod, który używa Microsoft::WRL::ComPtr<T>, aby używał winrt::com_ptr<T>. Oto przykład kodu przed i po. W wersji po funkcja składowa com_ptr::put pobiera podstawowy surowy wskaźnik, aby można było go ustawić.
ComPtr<IDXGIAdapter1> previousDefaultAdapter;
DX::ThrowIfFailed(m_dxgiFactory->EnumAdapters1(0, &previousDefaultAdapter));
winrt::com_ptr<IDXGIAdapter1> previousDefaultAdapter;
winrt::check_hresult(m_dxgiFactory->EnumAdapters1(0, previousDefaultAdapter.put()));
Ważne
Jeśli masz winrt::com_ptr, który jest już osadzony (jego wewnętrzny surowy wskaźnik ma już docelowy obiekt) i chcesz przypisać go ponownie, aby wskazywał na inny obiekt, musisz najpierw przypisać do niego nullptr — jak pokazano w poniższym przykładzie kodu. Jeśli tak nie jest, to już obecny com_ptr zasygnalizuje problem (po wywołaniu com_ptr::put lub com_ptr::put_void) twierdząc, że jego wewnętrzny wskaźnik nie jest pusty.
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())
);
W następnym przykładzie (w po wersji) funkcja składowa com_ptr::put_void pobiera podstawowy wskaźnik surowy jako wskaźnik do wskaźnika 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();
}
Zamień ComPtr::Get na com_ptr::get.
m_d3dDevice->CreateDepthStencilView(m_depthStencil.Get(), &dsvDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
m_d3dDevice->CreateDepthStencilView(m_depthStencil.get(), &dsvDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
Jeśli chcesz przekazać bazowy surowy wskaźnik do funkcji, która oczekuje wskaźnika IUnknown, użyj funkcji wolnostojącej winrt::get_unknown, jak pokazano w następnym przykładzie.
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()
)
);
Portowanie modułu WRL (Microsoft::WRL::Module)
Ta sekcja dotyczy przenoszenia kodu, który używa typu Microsoft::WRL::Module.
Możesz stopniowo dodawać kod C++/WinRT do istniejącego projektu, który korzysta z biblioteki WRL do implementacji składnika, a istniejące klasy WRL będą dalej obsługiwane. W tej sekcji pokazano, jak to zrobić.
Jeśli utworzysz nowy projekt typu składnik środowiska uruchomieniowego Windows (C++/WinRT) w programie Visual Studio i skompilujesz, plik Generated Files\module.g.cpp zostanie wygenerowany. Ten plik zawiera definicje dwóch przydatnych funkcji C++/WinRT (wymienionych poniżej), które można skopiować i dodać do projektu. Te funkcje to WINRT_CanUnloadNow i WINRT_GetActivationFactory, a jak widać, warunkowo wywołują bibliotekę WRL, aby wspierać cię na dowolnym etapie przenoszenia.
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(); }
}
Po utworzeniu tych funkcji w projekcie zamiast bezpośrednio wywoływać metodę Module::GetActivationFactory , wywołaj WINRT_GetActivationFactory (która wywołuje funkcję WRL wewnętrznie). Oto przykład kodu przed i 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));
}
Zamiast wywoływać funkcję Module::Terminate bezpośrednio, wywołaj WINRT_CanUnloadNow (która wywołuje funkcję WRL wewnętrznie). Oto przykład kodu przed i 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;
}
Przenoszenie Microsoft::WRL::Wrappers opakowań
Ta sekcja dotyczy przenoszenia kodu, który używa Microsoft::WRL::Wrappers wrapperów.
Jak widać w poniższej tabeli, aby zastąpić narzędzia pomocnicze do wątków, zalecamy użycie biblioteki obsługi wątków Standard C++. Mapowanie jeden do jednego z opakowań WRL może być mylące, ponieważ wybór zależy od Twoich potrzeb. Ponadto niektóre typy, które mogą wydawać się oczywistymi mapowaniami, są nowe w standardzie C++20, więc te będą niepraktyczne, jeśli jeszcze nie zostały uaktualnione.
| Typ | Przenoszenie notatek |
|---|---|
| klasa CriticalSection | Użyj biblioteki obsługi wątków |
| klasa zdarzenia (WRL) | Korzystanie z szablonu struktury winrt::event |
| HandleT klasy | Użyj struktury winrt::handle lub struktury winrt::file_handle |
| klasy HString | Użyj struktury winrt::hstring |
| klasa HStringReference | Brak zamiany, ponieważ język C++/WinRT obsługuje to wewnętrznie w sposób, który jest tak skuteczny, jak HStringReference z zaletą, że nie trzeba o tym myśleć. |
| Mutex klasy | Użyj biblioteki obsługi wątków |
| klasa RoInitializeWrapper | Użyj winrt::init_apartment i winrt::uninit_apartment; lub napisz własną prostą osłonę wokół CoInitializeEx i CoUninitialize. |
| klasy Semaphore | Użyj biblioteki obsługi wątków |
| klasy SRWLock | Użyj biblioteki obsługi wątków |
Ważne interfejsy API
- szablon struktury winrt::com_ptr
- winrt::Windows::Foundation::IUnknown, struktura