Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
U kunt de faciliteiten van de C++/WinRT-bibliotheek gebruiken om COM-onderdelen te gebruiken, zoals de krachtige 2D- en 3D-afbeeldingen van de DirectX-API's. C++/WinRT is de eenvoudigste manier om DirectX te gebruiken zonder de prestaties in gevaar te brengen. In dit onderwerp wordt een Direct2D-codevoorbeeld gebruikt om te laten zien hoe u C++/WinRT gebruikt om COM-klassen en -interfaces te gebruiken. U kunt natuurlijk COM- en Windows Runtime-programmering combineren binnen hetzelfde C++/WinRT-project.
Aan het einde van dit onderwerp vindt u een volledige broncodevermelding van een minimale Direct2D-toepassing. We halen fragmenten uit die code op en gebruiken ze om te illustreren hoe u COM-onderdelen gebruikt met behulp van C++/WinRT met behulp van verschillende faciliteiten van de C++/WinRT-bibliotheek.
COM smart pointers (winrt::com_ptr)
Wanneer u met COM programmeert, werkt u rechtstreeks met interfaces in plaats van met objecten (dat geldt ook achter de schermen voor Windows Runtime-API's, die een evolutie van COM zijn). Als u bijvoorbeeld een functie wilt aanroepen in een COM-klasse, activeert u de klasse, haalt u een interface terug en roept u functies op die interface aan. Als u toegang wilt krijgen tot de status van een object, hebt u niet rechtstreeks toegang tot de gegevensleden; In plaats daarvan roept u accessor- en mutatorfuncties aan op een interface.
Om specifieker te zijn, hebben we het over interactie met interfacepointers. En daarvoor profiteren we van het bestaan van het com smart pointer-type in C++/WinRT, het winrt::com_ptr type.
#include <d2d1_1.h>
...
winrt::com_ptr<ID2D1Factory1> factory;
De bovenstaande code laat zien hoe u een niet-geïnitialiseerde slimme aanwijzer declareert naar een COM-interface van ID2D1Factory1 . De slimme aanwijzer is niet-geïnitialiseerd, dus deze wijst nog niet naar een ID2D1Factory1-interface die hoort bij een werkelijk object (het verwijst helemaal niet naar een interface). Maar het heeft potentieel om dit te doen; en als een slimme aanwijzer heeft het de mogelijkheid om via COM-referentietelling de levensduur te beheren van het object dat het eigendom is van de interface waarnaar wordt verwezen, en het medium te zijn waarmee u functies op die interface oproept.
COM-functies die een interfaceaanwijzer retourneren als void
U kunt de functie com_ptr::put_void aanroepen om te schrijven naar de onderliggende ruwe pointer van een niet-geïnitialiseerde smart pointer.
D2D1_FACTORY_OPTIONS options{ D2D1_DEBUG_LEVEL_NONE };
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(factory),
&options,
factory.put_void()
);
Met de bovenstaande code wordt de functie D2D1CreateFactory aangeroepen, die een ID2D1Factory1-interfaceaanwijzer retourneert via de laatste parameter, die het type void** heeft. Veel COM-functies retourneren een ongeldige waarde**. Gebruik voor dergelijke functies com_ptr::put_void zoals weergegeven.
COM-functies die een specifieke interfacepointer retourneren
De functie D3D11CreateDevice retourneert een id3D11Device-interfaceaanwijzer via de derde van laatste parameter, met het type ID3D11Device** . Gebruik com_ptr::putvoor functies die een specifieke interfacepointer retourneren.
winrt::com_ptr<ID3D11Device> device;
D3D11CreateDevice(
...
device.put(),
...);
Het codevoorbeeld in de sectie voordat deze wordt weergegeven, laat zien hoe u de onbewerkte D2D1CreateFactory-functie aanroept. Maar wanneer het codevoorbeeld voor dit onderwerp D2D1CreateFactoryaanroept, wordt er een helperfunctiesjabloon gebruikt dat de onbewerkte API omhult. In het codevoorbeeld wordt dus daadwerkelijk gebruikgemaakt van com_ptr::put.
winrt::com_ptr<ID2D1Factory1> factory;
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
options,
factory.put());
COM-functies die een interfaceaanwijzer retourneren als IUnknown
De functie DWriteCreateFactory retourneert een DirectWrite factory-interfaceaanwijzer via de laatste parameter, die van het type IUnknown is. Gebruik voor een dergelijke functie com_ptr::put, maar herinterpreteer deze naar IUnknown.
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(dwriteFactory2),
reinterpret_cast<IUnknown**>(dwriteFactory2.put()));
Een winrt::com_ptr opnieuw aansluiten
Belangrijk
Als u een winrt::com_ptr heeft die al een doel heeft (de interne ruwe aanwijzer heeft al een doel) en u deze opnieuw wilt toewijzen aan een ander object, moet u eerst nullptr
eraan toekennen, zoals wordt weergegeven in het onderstaande codevoorbeeld. Als u dat niet doet, zal een reeds aanwezige com_ptr uw aandacht op het probleem vestigen (wanneer u com_ptr::put of com_ptr::put_voidaanroept) door te bevestigen dat de interne pointer niet null is.
winrt::com_ptr<ID2D1SolidColorBrush> brush;
...
brush.put()
...
brush = nullptr; // Important because we're about to re-seat
target->CreateSolidColorBrush(
color_orange,
D2D1::BrushProperties(0.8f),
brush.put()));
HRESULT-foutcodes verwerken
Als u de waarde van een HRESULT wilt controleren die is geretourneerd door een COM-functie en een uitzondering genereert in het geval dat deze een foutcode vertegenwoordigt, roept u winrt::check_hresult aan.
winrt::check_hresult(D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(factory),
options,
factory.put_void()));
COM-functies die een specifieke interfacepointer gebruiken
U kunt de functie com_ptr::get aanroepen om uw com_ptr door te geven aan een functie die een specifieke interfacepointer van hetzelfde type gebruikt.
... ExampleFunction(
winrt::com_ptr<ID2D1Factory1> const& factory,
winrt::com_ptr<IDXGIDevice> const& dxdevice)
{
...
winrt::check_hresult(factory->CreateDevice(dxdevice.get(), ...));
...
}
COM-functies die een IUnknown-interfaceaanwijzer gebruiken
U kunt com_ptr::get gebruiken om uw com_ptr door te geven aan een functie die een IUnknown-interfaceaanwijzer gebruikt.
U kunt de winrt::get_unknown free function gebruiken om het adres (met andere woorden, een aanwijzer) te retourneren van de onderliggende onbewerkte IUnknown interface van een object van een geprojecteerd type. U kunt dat adres vervolgens doorgeven aan een functie die een IUnknown-interfaceaanwijzer gebruikt.
Zie API's gebruiken met C++/WinRT-voor informatie over projecttypen.
Zie winrt::get_unknown of de volledige broncode van een minimale Direct2D-toepassing in dit onderwerp voor een codevoorbeeld van get_unknown.
Slimme COM-aanwijzers doorgeven en retourneren
Een functie die een COM-smart pointer in de vorm van een winrt::com_ptr als constante verwijzing of als verwijzing moet gebruiken.
... GetDxgiFactory(winrt::com_ptr<ID3D11Device> const& device) ...
... CreateDevice(..., winrt::com_ptr<ID3D11Device>& device) ...
Een functie die een winrt::com_ptr retourneert, moet dit als waarde doen.
winrt::com_ptr<ID2D1Factory1> CreateFactory() ...
Een query uitvoeren op een COM-slimme aanwijzer voor een andere interface
U kunt de functie com_ptr::as gebruiken om een query uit te voeren op een COM-slimme aanwijzer voor een andere interface. De functie genereert een uitzondering als de query niet slaagt.
void ExampleFunction(winrt::com_ptr<ID3D11Device> const& device)
{
...
winrt::com_ptr<IDXGIDevice> const dxdevice{ device.as<IDXGIDevice>() };
...
}
U kunt ook com_ptr::try_as gebruiken, waarmee een waarde wordt geretourneerd die u kunt controleren nullptr
om te zien of de query is geslaagd.
Volledige broncodevermelding van een minimale Direct2D-toepassing
Opmerking
Voor informatie over het instellen van Visual Studio voor C++/WinRT-ontwikkeling, waaronder het installeren en gebruiken van de C++/WinRT Visual Studio-extensie (VSIX) en het NuGet-pakket (die samen ondersteuning bieden voor projectsjablonen en de build), zie Visual Studio-ondersteuning voor C++/WinRT.
Als u dit broncodevoorbeeld wilt bouwen en uitvoeren, installeert u eerst de nieuwste versie van de C++/WinRT Visual Studio-extensie (VSIX). zie de bovenstaande opmerking. Maak vervolgens in Visual Studio een nieuwe Core-app (C++/WinRT).
Direct2D
is een redelijke naam voor het project, maar u kunt het alles wat u wilt noemen. Richt u op de meest recente algemeen beschikbare (dat wil gezegd, niet preview) versie van de Windows SDK.
Stap 1. Bewerken pch.h
Open pch.h
en voeg #include <unknwn.h>
direct na het opnemen van windows.h
toe. Dit komt omdat we winrt::get_unknown gebruiken. Het is een goed idee om expliciet te #include <unknwn.h>
gebruiken wanneer u winrt::get_unknown gebruikt, zelfs als die header is opgenomen in een andere header.
Opmerking
Als u deze stap weglaat, ziet u de buildfout 'get_unknown': id niet gevonden.
Stap 2. Bewerken App.cpp
Open App.cpp
, verwijder de volledige inhoud en plak de onderstaande vermelding.
In de onderstaande code wordt waar mogelijk de winrt::com_ptr::capture-functie gebruikt.
WINRT_ASSERT
is een macrodefinitie en wordt uitgebreid naar _ASSERTE.
#include "pch.h"
#include <d2d1_1.h>
#include <d3d11.h>
#include <dxgi1_2.h>
#include <winrt/Windows.Graphics.Display.h>
using namespace winrt;
using namespace Windows;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::UI;
using namespace Windows::UI::Core;
using namespace Windows::Graphics::Display;
namespace
{
winrt::com_ptr<ID2D1Factory1> CreateFactory()
{
D2D1_FACTORY_OPTIONS options{};
#ifdef _DEBUG
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
winrt::com_ptr<ID2D1Factory1> factory;
winrt::check_hresult(D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
options,
factory.put()));
return factory;
}
HRESULT CreateDevice(D3D_DRIVER_TYPE const type, winrt::com_ptr<ID3D11Device>& device)
{
WINRT_ASSERT(!device);
return D3D11CreateDevice(
nullptr,
type,
nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr, 0,
D3D11_SDK_VERSION,
device.put(),
nullptr,
nullptr);
}
winrt::com_ptr<ID3D11Device> CreateDevice()
{
winrt::com_ptr<ID3D11Device> device;
HRESULT hr{ CreateDevice(D3D_DRIVER_TYPE_HARDWARE, device) };
if (DXGI_ERROR_UNSUPPORTED == hr)
{
hr = CreateDevice(D3D_DRIVER_TYPE_WARP, device);
}
winrt::check_hresult(hr);
return device;
}
winrt::com_ptr<ID2D1DeviceContext> CreateRenderTarget(
winrt::com_ptr<ID2D1Factory1> const& factory,
winrt::com_ptr<ID3D11Device> const& device)
{
WINRT_ASSERT(factory);
WINRT_ASSERT(device);
winrt::com_ptr<IDXGIDevice> const dxdevice{ device.as<IDXGIDevice>() };
winrt::com_ptr<ID2D1Device> d2device;
winrt::check_hresult(factory->CreateDevice(dxdevice.get(), d2device.put()));
winrt::com_ptr<ID2D1DeviceContext> target;
winrt::check_hresult(d2device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, target.put()));
return target;
}
winrt::com_ptr<IDXGIFactory2> GetDxgiFactory(winrt::com_ptr<ID3D11Device> const& device)
{
WINRT_ASSERT(device);
winrt::com_ptr<IDXGIDevice> const dxdevice{ device.as<IDXGIDevice>() };
winrt::com_ptr<IDXGIAdapter> adapter;
winrt::check_hresult(dxdevice->GetAdapter(adapter.put()));
winrt::com_ptr<IDXGIFactory2> factory;
factory.capture(adapter, &IDXGIAdapter::GetParent);
return factory;
}
void CreateDeviceSwapChainBitmap(
winrt::com_ptr<IDXGISwapChain1> const& swapchain,
winrt::com_ptr<ID2D1DeviceContext> const& target)
{
WINRT_ASSERT(swapchain);
WINRT_ASSERT(target);
winrt::com_ptr<IDXGISurface> surface;
surface.capture(swapchain, &IDXGISwapChain1::GetBuffer, 0);
D2D1_BITMAP_PROPERTIES1 const props{ D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)) };
winrt::com_ptr<ID2D1Bitmap1> bitmap;
winrt::check_hresult(target->CreateBitmapFromDxgiSurface(surface.get(),
props,
bitmap.put()));
target->SetTarget(bitmap.get());
}
winrt::com_ptr<IDXGISwapChain1> CreateSwapChainForCoreWindow(winrt::com_ptr<ID3D11Device> const& device)
{
WINRT_ASSERT(device);
winrt::com_ptr<IDXGIFactory2> const factory{ GetDxgiFactory(device) };
DXGI_SWAP_CHAIN_DESC1 props{};
props.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
props.SampleDesc.Count = 1;
props.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
props.BufferCount = 2;
props.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
winrt::com_ptr<IDXGISwapChain1> swapChain;
winrt::check_hresult(factory->CreateSwapChainForCoreWindow(
device.get(),
winrt::get_unknown(CoreWindow::GetForCurrentThread()),
&props,
nullptr, // all or nothing
swapChain.put()));
return swapChain;
}
constexpr D2D1_COLOR_F color_white{ 1.0f, 1.0f, 1.0f, 1.0f };
constexpr D2D1_COLOR_F color_orange{ 0.92f, 0.38f, 0.208f, 1.0f };
}
struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
winrt::com_ptr<ID2D1Factory1> m_factory;
winrt::com_ptr<ID2D1DeviceContext> m_target;
winrt::com_ptr<IDXGISwapChain1> m_swapChain;
winrt::com_ptr<ID2D1SolidColorBrush> m_brush;
float m_dpi{};
IFrameworkView CreateView()
{
return *this;
}
void Initialize(CoreApplicationView const&)
{
}
void Load(hstring const&)
{
CoreWindow const window{ CoreWindow::GetForCurrentThread() };
window.SizeChanged([&](auto&&...)
{
if (m_target)
{
ResizeSwapChainBitmap();
Render();
}
});
DisplayInformation const display{ DisplayInformation::GetForCurrentView() };
m_dpi = display.LogicalDpi();
display.DpiChanged([&](DisplayInformation const& display, IInspectable const&)
{
if (m_target)
{
m_dpi = display.LogicalDpi();
m_target->SetDpi(m_dpi, m_dpi);
CreateDeviceSizeResources();
Render();
}
});
m_factory = CreateFactory();
CreateDeviceIndependentResources();
}
void Uninitialize()
{
}
void Run()
{
CoreWindow const window{ CoreWindow::GetForCurrentThread() };
window.Activate();
Render();
CoreDispatcher const dispatcher{ window.Dispatcher() };
dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
}
void SetWindow(CoreWindow const&) {}
void Draw()
{
m_target->Clear(color_white);
D2D1_SIZE_F const size{ m_target->GetSize() };
D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f };
m_target->DrawRectangle(rect, m_brush.get(), 100.0f);
char buffer[1024];
(void)snprintf(buffer, sizeof(buffer), "Draw %.2f x %.2f @ %.2f\n", size.width, size.height, m_dpi);
::OutputDebugStringA(buffer);
}
void Render()
{
if (!m_target)
{
winrt::com_ptr<ID3D11Device> const device{ CreateDevice() };
m_target = CreateRenderTarget(m_factory, device);
m_swapChain = CreateSwapChainForCoreWindow(device);
CreateDeviceSwapChainBitmap(m_swapChain, m_target);
m_target->SetDpi(m_dpi, m_dpi);
CreateDeviceResources();
CreateDeviceSizeResources();
}
m_target->BeginDraw();
Draw();
m_target->EndDraw();
HRESULT const hr{ m_swapChain->Present(1, 0) };
if (S_OK != hr && DXGI_STATUS_OCCLUDED != hr)
{
ReleaseDevice();
}
}
void ReleaseDevice()
{
m_target = nullptr;
m_swapChain = nullptr;
ReleaseDeviceResources();
}
void ResizeSwapChainBitmap()
{
WINRT_ASSERT(m_target);
WINRT_ASSERT(m_swapChain);
m_target->SetTarget(nullptr);
if (S_OK == m_swapChain->ResizeBuffers(0, // all buffers
0, 0, // client area
DXGI_FORMAT_UNKNOWN, // preserve format
0)) // flags
{
CreateDeviceSwapChainBitmap(m_swapChain, m_target);
CreateDeviceSizeResources();
}
else
{
ReleaseDevice();
}
}
void CreateDeviceIndependentResources()
{
}
void CreateDeviceResources()
{
winrt::check_hresult(m_target->CreateSolidColorBrush(
color_orange,
D2D1::BrushProperties(0.8f),
m_brush.put()));
}
void CreateDeviceSizeResources()
{
}
void ReleaseDeviceResources()
{
m_brush = nullptr;
}
};
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
CoreApplication::Run(winrt::make<App>());
}
Werken met COM-typen, zoals BSTR en VARIANT
Zoals u ziet, biedt C++/WinRT ondersteuning voor het implementeren en aanroepen van COM-interfaces. Voor het gebruik van COM-typen, zoals BSTR en VARIANT, raden we u aan wrappers te gebruiken die worden geleverd door de Windows-implementatiebibliotheken (WIL), zoals wil::unique_bstr en wil::unique_variant (waarmee de levensduur van resources wordt beheerd).
WIL vervangt en overtreft frameworks zoals de Active Template Library (ATL) en de COM-ondersteuning van de Visual C++-compiler. En we raden aan om het te verkiezen boven het schrijven van uw eigen wrappers of het gebruik van COM-typen zoals BSTR en VARIANT in hun ruwe vorm (samen met de bijbehorende API's).
Naamruimteconflicten voorkomen
Het is gebruikelijk in C++/WinRT, zoals de codevermelding in dit onderwerp laat zien, om gebruik te maken van gebruiksrichtlijnen. In sommige gevallen kan dat echter leiden tot het probleem van het importeren van botsende namen in de globale naamruimte. Hier volgt een voorbeeld.
C++/WinRT bevat een type met de naam winrt::Windows::Foundation::IUnknown; terwijl COM een type met de naam ::IUnknown definieert. Houd dus rekening met de volgende code in een C++/WinRT-project dat COM-headers verbruikt.
using namespace winrt::Windows::Foundation;
...
void MyFunction(IUnknown*); // error C2872: 'IUnknown': ambiguous symbol
De niet-gekwalificeerde naam IUnknown botst in de globale naamruimte, vandaar de dubbelzinnige compilerfout van het symbool. In plaats daarvan kunt u de C++/WinRT-versie van de naam isoleren in de winrt-naamruimte , zoals deze.
namespace winrt
{
using namespace Windows::Foundation;
}
...
void MyFunctionA(IUnknown*); // Ok.
void MyFunctionB(winrt::IUnknown const&); // Ok.
Of, als u het gemak van using namespace winrt
wilt, dan is dat mogelijk voor u. U hoeft alleen maar de globale versie van IUnknownte kwalificeren, zoals dit.
using namespace winrt;
namespace winrt
{
using namespace Windows::Foundation;
}
...
void MyFunctionA(::IUnknown*); // Ok.
void MyFunctionB(winrt::IUnknown const&); // Ok.
Dit werkt natuurlijk met elke C++/WinRT-naamruimte.
namespace winrt
{
using namespace Windows::Storage;
using namespace Windows::System;
}
U kunt vervolgens naar winrt::Windows::Storage::StorageFile verwijzen, bijvoorbeeld als alleen winrt::StorageFile.