Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Note
Bu makaledeki kod örnekleri, UWP Core App (IFrameworkView) uygulama modelini kullanır. WinUI 3 masaüstü uygulamasında, Microsoft::UI::Xaml::Application'dan bir sınıf türetin ve uygulama giriş noktası olarak Application::Start(...) öğesini çağırın ve CoreDispatcher yerine DispatcherQueue kullanın. Burada gösterilen COM/Direct2D kavramları ve desenleri WinUI 3 uygulamalarına eşit şekilde uygulanır.
DirectX API'lerinin yüksek performanslı 2-B ve 3-B grafikleri gibi COM bileşenlerini kullanmak için C++/WinRT kitaplığının olanaklarını kullanabilirsiniz. C++/WinRT, performanstan ödün vermeden DirectX kullanmanın en basit yoludur. Bu konu, COM sınıflarını ve arabirimlerini kullanmak için C++/WinRT'nin nasıl kullanılacağını göstermek için bir Direct2D kod örneği kullanır. Elbette aynı C++/WinRT projesinde COM ve Windows Çalışma Zamanı programlamayı karıştırabilirsiniz.
Bu konunun sonunda, en az Direct2D uygulamasının tam kaynak kod listesini bulacaksınız. O koddan bazı bölümler alıp, C++/WinRT kitaplığının çeşitli olanaklarından yararlanarak COM bileşenlerinin C++/WinRT ile nasıl kullanılacağını göstermek için kullanacağız.
COM akıllı işaretçileri (winrt::com_ptr)
COM ile programlama yaparken, nesneler yerine doğrudan arabirimlerle çalışırsınız (bu, COM'un evrimi olan Windows Çalışma Zamanı API'ler için de arka planda geçerlidir). Örneğin, BIR COM sınıfındaki bir işlevi çağırmak için sınıfını etkinleştirirsiniz, bir arabirimi geri alırsınız ve ardından bu arabirimdeki işlevleri çağırırsınız. Bir nesnenin durumuna erişmek için, nesnenin veri üyelerine doğrudan erişmezsiniz; bunun yerine, arabirimde erişimci ve mutator işlevlerini çağırırsınız.
Daha açık olmak gerekirse, arabirim işaretçileriyle etkileşime geçmekten bahsediyoruz. Bunun için, C++/WinRT'de COM akıllı işaretçi türünün ( winrt::com_ptr türü) varlığından yararlanıyoruz.
#include <d2d1_1.h>
...
winrt::com_ptr<ID2D1Factory1> factory;
Yukarıdaki kod, ID2D1Factory1 COM arabirimi için başlatılmamış bir akıllı işaretçinin nasıl bildirileceğini gösterir. Akıllı işaretçi başlatılmamış olduğundan, henüz herhangi bir gerçek nesneye ait id2D1Factory1 arabirimine işaret etmemektedir (bir arabirimi hiç işaret etmemektedir). Ancak bunu yapma potansiyeline sahiptir; ve (akıllı bir işaretçi olarak) COM referans sayımı aracılığıyla, işaret ettiği arabirimin sahibi olan nesnenin yaşam süresini yönetebilir ve o arabirim üzerindeki işlevleri çağırmanızı sağlayan araç olabilir.
Arabirim işaretçisini geçersiz olarak döndüren COM işlevleri
Başlatılmamış bir akıllı işaretçinin altta yatan ham işaretçisi üzerine yazmak için com_ptr::put_void işlevini çağırabilirsiniz.
D2D1_FACTORY_OPTIONS options{ D2D1_DEBUG_LEVEL_NONE };
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(factory),
&options,
factory.put_void()
);
Yukarıdaki kod D2D1CreateFactory işlevini çağırır. Bu işlev, void** türüne sahip son parametresi aracılığıyla bir ID2D1Factory1 arabirim işaretçisi döndürür. Birçok COM işlevi geçersiz** döndürür. Bu tür işlevler için, gösterildiği gibi com_ptr::put_void kullanın.
Belirli bir arabirim işaretçisi döndüren COM işlevleri
D3D11CreateDevice işlevi, ID3D11Cihaz** türüne sahip olan son parametresi aracılığıyla bir ID3D11Cihaz arabirimi işaretçisi döndürür. Bunun gibi belirli bir arabirim göstericisi döndüren işlevler için com_ptr::put kullanın.
winrt::com_ptr<ID3D11Device> device;
D3D11CreateDevice(
...
device.put(),
...);
Bundan önceki bölümdeki kod örneği, ham D2D1CreateFactory işlevinin nasıl çağrılacağını gösterir. Ancak aslında, bu konuya ait kod örneği D2D1CreateFactory işlevini çağırdığında, ham API'yi saran bir yardımcı işlev şablonunu kullanır; bu yüzden kod örneği aslında com_ptr::put kullanır.
winrt::com_ptr<ID2D1Factory1> factory;
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
options,
factory.put());
Arabirim işaretçisini IUnknown olarak döndüren COM işlevleri
DWriteCreateFactory işlevi, IUnknown türüne sahip son parametresi aracılığıyla bir DirectWrite fabrika arabirimi işaretçisi döndürür. Böyle bir işlev için com_ptr::put kullanın, ancak bunu IUnknown olarak reinterpret cast edin.
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(dwriteFactory2),
reinterpret_cast<IUnknown**>(dwriteFactory2.put()));
winrt::com_ptr öğesini yeniden yerleştirme
Önemli
Önceden oturmuş bir winrt::com_ptr varsa (iç ham işaretçisinin zaten bir hedefi var) ve bunu farklı bir nesneye işaret etmek için yeniden oturtmak istiyorsanız, önce aşağıdaki kod örneğinde gösterildiği gibi buna atamanız nullptr gerekir. Bunu yapmazsanız, daha önce atanmış bir com_ptr, iç işaretçisinin null olmadığını doğrulayarak sorunu dikkatinize çeker (com_ptr::put veya com_ptr::put_void çağrısı yaptığınızda).
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 hata kodlarını işleme
Bir COM işlevinden döndürülen HRESULT değerini kontrol etmek ve bir hata kodunu temsil etmesi durumunda özel durum fırlatmak için winrt::check_hresult çağırın.
winrt::check_hresult(D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(factory),
options,
factory.put_void()));
Belirli bir arabirim işaretçisini alan COM işlevleri
com_ptr::get işlevini, com_ptr’nizi aynı türden belirli bir arabirim işaretçisi alan bir işleve geçirmek için çağırabilirsiniz.
... ExampleFunction(
winrt::com_ptr<ID2D1Factory1> const& factory,
winrt::com_ptr<IDXGIDevice> const& dxdevice)
{
...
winrt::check_hresult(factory->CreateDevice(dxdevice.get(), ...));
...
}
IUnknown arabirim işaretçisi kullanan COM işlevleri
com_ptr::get öğesini, com_ptr öğenizi IUnknown arabirim işaretçisi alan bir işleve iletmek için kullanabilirsiniz.
Winrt::get_unknown free işlevini kullanarak, yansıtılan türdeki bir nesnenin temel alınan ham IUnknown arabiriminin adresini (başka bir deyişle bir işaretçi) döndürebilirsiniz. Daha sonra bu adresi IUnknown arabirim işaretçisi alan bir işleve geçirebilirsiniz.
Öngörülen türler hakkında bilgi için bkz. C++/WinRT ile API'leri kullanma.
get_unknown kod örneği için, bu konudaki winrt::get_unknown veya en düşük Direct2D uygulamasının tam kaynak kod listesine bakın.
COM akıllı işaretçilerini aktarma ve döndürme
winrt::com_ptr biçiminde bir COM akıllı işaretçisi alan bir işlev bunu sabit başvuru veya başvuru yoluyla yapmalıdır.
... GetDxgiFactory(winrt::com_ptr<ID3D11Device> const& device) ...
... CreateDevice(..., winrt::com_ptr<ID3D11Device>& device) ...
winrt::com_ptr döndüren bir işlev bunu değere göre yapmalıdır.
winrt::com_ptr<ID2D1Factory1> CreateFactory() ...
Farklı bir arabirim için COM akıllı işaretçisini sorgulama
com akıllı işaretçisini farklı bir arabirim için sorgulamak için com_ptr::as işlevini kullanabilirsiniz. İşlev, sorgu başarılı olmazsa bir özel durum fırlatır.
void ExampleFunction(winrt::com_ptr<ID3D11Device> const& device)
{
...
winrt::com_ptr<IDXGIDevice> const dxdevice{ device.as<IDXGIDevice>() };
...
}
Alternatif olarak, sorgunun başarılı olup olmadığını görmek için denetleyebileceğiniz bir değer döndüren nullptr kullanın.
En düşük Direct2D uygulamasının tam kaynak kod listesi
Note
C++/WinRT geliştirmesi için C++/WinRT Visual Studio Uzantısını (VSIX) ve NuGet paketini (birlikte proje şablonu ve derleme desteği sağlayan) yükleme ve kullanma dahil olmak üzere Visual Studio ayarlama hakkında bilgi için bkz. C++/WinRT için Visual Studio desteği.
Bu kaynak kodu örneğini derlemek ve çalıştırmak istiyorsanız, önce C++/WinRT Visual Studio Uzantısının (VSIX) en son sürümünü yükleyin (veya güncelleştirin). Yukarıdaki nota bakın. Ardından Visual Studio yeni bir Çekirdek Uygulama (C++/WinRT) oluşturun.
Direct2D proje için makul bir addır, ancak istediğiniz her şeyi adlandırabilirsiniz. Windows SDK'sının genel kullanıma sunulan (önizleme değil) en son sürümünü hedefleyin.
Adım 1. Düzenle pch.h
pch.h öğesini açın ve windows.h ekledikten hemen sonra #include <unknwn.h> öğesini ekleyin. Bunun nedeni winrt::get_unknown kullanmamızdır. Bu üst bilgi başka bir üst bilgi tarafından dahil edilmiş olsa bile, winrt::get_unknown kullandığınızda #include <unknwn.h> ifadesini açıkça kullanmak iyi bir fikirdir.
Note
Bu adımı atlarsanız 'get_unknown': tanımlayıcı bulunamadı derleme hatasını görürsünüz.
Adım 2. Düzenle App.cpp
App.cpp öğesini açın, tüm içeriğini silin ve aşağıdaki içeriği yapıştırın.
Aşağıdaki kod mümkün olduğunca winrt::com_ptr::capture işlevini kullanır.
WINRT_ASSERT bir makro tanımıdır ve _ASSERTE genişletilir.
#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>());
}
BSTR ve VARIANT gibi COM türleriyle çalışma
Gördüğünüz gibi, C++/WinRT hem COM arabirimlerini uygulama hem de çağırma desteği sağlar. BSTR ve VARIANT gibi COM türlerini kullanmak için, Windows Uygulama Kitaplıkları (WIL) tarafından sağlanan wil::unique_bstr ve wil::unique_variant (kaynak ömrünü yöneten) gibi sarmalayıcıları kullanmanızı öneririz.
WIL , Etkin Şablon Kitaplığı (ATL) ve Visual C++ derleyicisinin COM Desteği gibi çerçevelerin yerini alır. Kendi sarmalayıcılarınızı yazmak veya BSTR ve VARIANT gibi COM türlerini ham halleriyle (uygun API'lerle birlikte) kullanmak yerine bunu öneriyoruz.
Ad alanı çakışmalarını önleme
Bu konudaki kod listesinde gösterildiği gibi C++/WinRT'de kullanım yönergelerini liberal olarak kullanmak yaygın bir uygulamadır. Ancak bazı durumlarda bu, çakışma adlarını genel ad alanına aktarma sorununa yol açabilir. İşte bir örnek.
C++/WinRT winrt::Windows::Foundation::IUnknown adlı bir tür içerir; COM ise ::IUnknown adlı bir tür tanımlar. Bu nedenle, COM üst bilgilerini kullanan bir C++/WinRT projesinde aşağıdaki kodu göz önünde bulundurun.
using namespace winrt::Windows::Foundation;
...
void MyFunction(IUnknown*); // error C2872: 'IUnknown': ambiguous symbol
Nitelendirilmemiş IUnknown adı global ad alanında çakışır; bu nedenle belirsiz sembol derleyici hatasına neden olur. Bunun yerine, adın C++/WinRT sürümünü winrt ad alanında aşağıdaki gibi yalıtabilirsiniz.
namespace winrt
{
using namespace Windows::Foundation;
}
...
void MyFunctionA(IUnknown*); // Ok.
void MyFunctionB(winrt::IUnknown const&); // Ok.
Ya da using namespace winrt kullanmanın sağladığı kolaylığı istiyorsanız, bunu yapabilirsiniz.
IUnknown'un genel sürümünü bu şekilde nitelemeniz yeterlidir.
using namespace winrt;
namespace winrt
{
using namespace Windows::Foundation;
}
...
void MyFunctionA(::IUnknown*); // Ok.
void MyFunctionB(winrt::IUnknown const&); // Ok.
Doğal olarak, bu herhangi bir C++/WinRT ad alanıyla çalışır.
namespace winrt
{
using namespace Windows::Storage;
using namespace Windows::System;
}
Daha sonra winrt::Windows::Storage::StorageFile örneğine, yalnızca winrt::StorageFile gibi başvurabilirsiniz.
Önemli API'ler
Windows developer