windows C++- C++/WinRT和COM组件(上)-待发布

可以通过 C++/WinRT 库的工具来使用 COM 组件,例如 DirectX API 的高性能 2D 和 3D 图形。 C++/ WinRT 是在不影响性能的情况下使用 DirectX 的最简单方法。 本文借助一个 Direct2D 代码示例演示如何通过 C++/WinRT 使用 COM 类和接口。 当然,你也可以在同一个 C++/WinRT 项目中混合使用 COM 和 Windows 运行时编程方法。

本文的末尾提供了一个精简 Direct2D 应用程序的完整源代码列表。 我们将提取该代码的摘录内容,演示如何使用 C++/WinRT 库的各种工具通过 C++/WinRT 来使用 COM 组件。

COM 智能指针 (winrt::com_ptr)

使用 COM 编程时,你会直接使用接口而不是对象(Windows 运行时 API 在幕后也是如此,这是 COM 的一种演进)。 若要针对 COM 类调用函数(例如,激活该类),需先获取一个接口,然后针对该接口调用该函数。 若要访问对象的状态,请不要直接访问其数据成员,而应该针对某个接口调用取值函数和赋值函数。

更具体而言,我们将讨论如何与接口指针交互。 对于这种操作,我们可以受益于 C++/WinRT 中的 COM 智能指针类型 winrt::com_ptr 类型。

#include <d2d1_1.h>
...
winrt::com_ptr<ID2D1Factory1> factory;

以上代码演示如何声明指向 ID2D1Factory1 COM 接口的未初始化智能指针。 该智能指针未初始化,因此暂时不会指向属于任何实际对象的 ID2D1Factory1 接口根本不指向任何接口。 但它可以做到这一点;作为一个智能指针,它能够通过 COM 引用计数来管理它所指向的接口的拥有对象的生存期,并充当中介来让你针对该接口调用函数。 

返回 void 形式的接口指针的 COM 函数

可以调用 com_ptr::put_void 函数写入到未初始化智能指针的基础原始指针。

D2D1_FACTORY_OPTIONS options{ D2D1_DEBUG_LEVEL_NONE };
D2D1CreateFactory(
    D2D1_FACTORY_TYPE_SINGLE_THREADED,
    __uuidof(factory),
    &options,
    factory.put_void()
);

以上代码调用 D2D1CreateFactory 函数,该函数通过其最后一个参数返回 void** 类型的 ID2D1Factory1 接口指针。 许多 COM 函数返回 void**。 对于此类函数,请按如下所示使用 com_ptr::put_void。

返回特定接口指针的 COM 函数

D3D11CreateDevice 函数通过其倒数第三个参数返回 ID3D11Device** 类型的 ID3D11Device 接口指针。 对于返回此类特定接口指针的函数,请使用 com_ptr::put。

winrt::com_ptr<ID3D11Device> device;
D3D11CreateDevice(
    ...
    device.put(),
    ...);

 此节前面一节中的代码示例演示如何调用原始 D2D1CreateFactory 函数。 但实际上,当本主题中的代码示例调用 D2D1CreateFactory 时,它会使用一个帮助器函数模板来包装原始 API,因此,代码示例实际使用的是 com_ptr::put。

winrt::com_ptr<ID2D1Factory1> factory;
D2D1CreateFactory(
    D2D1_FACTORY_TYPE_SINGLE_THREADED,
    options,
    factory.put());
返回 IUnknown 形式的接口指针的 COM 函数

DWriteCreateFactory 函数通过其最后一个参数返回 IUnknown 类型的 DirectWrite 工厂接口指针。 对于此类函数,请使用 com_ptr::put,但需要将其重新解释并转换为 IUnknown。

DWriteCreateFactory(
    DWRITE_FACTORY_TYPE_SHARED,
    __uuidof(dwriteFactory2),
    reinterpret_cast<IUnknown**>(dwriteFactory2.put()));
重新定位 winrt::com_ptr

如果某个 winrt::com_ptr 已定位(其内部原始指针已有目标),而你想要将其重新定位为指向不同的对象,则首先需要向其分配 nullptr,如以下代码示例所示。 否则,已定位的 com_ptr 会断言其内部指针不为 null,从而产生需要关注的问题(调用 com_ptr::put 或 com_ptr::put_void 时)。

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 错误代码

若要检查 COM 函数返回的 HRESULT 值,并在它显示错误代码时引发异常,请调用 winrt::check_hresult。

winrt::check_hresult(D2D1CreateFactory(
    D2D1_FACTORY_TYPE_SINGLE_THREADED,
    __uuidof(factory),
    options,
    factory.put_void()));
采用特定接口指针的 COM 函数

可以调用 com_ptr::get 函数,将 com_ptr 传递给采用相同类型的特定接口指针的函数。

... ExampleFunction(
    winrt::com_ptr<ID2D1Factory1> const& factory,
    winrt::com_ptr<IDXGIDevice> const& dxdevice)
{
    ...
    winrt::check_hresult(factory->CreateDevice(dxdevice.get(), ...));
    ...
}
采用 IUnknown 接口指针的 COM 函数

可以使用 com_ptr::get,将 com_ptr 传递给采用 IUnknown 接口指针的函数。

可以使用 winrt::get_unknown 自由函数返回投影类型的对象的基础原始 IUnknown 接口的地址,也就是说,指向该接口的指针。 然后,可以将该地址传递给采用 IUnknown 接口指针的函数。

传递和返回 COM 智能指针

采用 winrt::com_ptr 形式的 COM 智能指针的函数应该按常量引用或按引用来采用该指针。

... GetDxgiFactory(winrt::com_ptr<ID3D11Device> const& device) ...

... CreateDevice(..., winrt::com_ptr<ID3D11Device>& device) ...

// 返回 winrt::com_ptr 的函数应按值返回该指针。

winrt::com_ptr<ID2D1Factory1> CreateFactory() ...
查询不同接口的 COM 智能指针

可以使用 com_ptr::as 函数查询不同接口的 COM 智能指针。 如果查询未成功,该函数将引发异常。

void ExampleFunction(winrt::com_ptr<ID3D11Device> const& device)
{
    ...
    winrt::com_ptr<IDXGIDevice> const dxdevice{ device.as<IDXGIDevice>() };
    ...
}

 或者可以使用 com_ptr::try_as,该函数会返回一个值,将该值与 nullptr 进行比较可以确定查询是否成功。

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值