windows C++-通过 C++/WinRT 创作 COM 组件(二)

这部分将演练如何创建使用 C++/WinRT 实现基本组件类(COM 组件或 COM 类)和类工厂的最小控制台应用程序项目。 下面的示例应用程序演示如何提供具有一个回调按钮的 toast 通知,组件类(实现 INotificationActivationCallback COM 接口)使应用程序可以启动并在用户单击 toast 上的该按钮时进行回调。

创建 Windows 控制台应用程序项目

首先在 Microsoft Visual Studio 中创建新项目。 创建“Windows 控制台应用程序(C++/WinRT)”项目,然后将它命名为 ToastAndCallback。

打开 pch.h,在用于任何 C++/WinRT 标头的 include 语句之前添加 #include <unknwn.h>。 下面是结果;可以将你的 pch.h 的内容替换为此清单。

// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>

打开 main.cpp,删除项目模板生成 using 指令。 在其位置插入以下代码(该代码提供我们所需的库、标头和类型名称)。 下面是结果;可以将你的 main.cpp 的内容替换为此清单(我们还会在下面的清单中删除 main 中的代码,因为我们会在以后替换该函数)。 

// main.cpp : Defines the entry point for the console application.

#include "pch.h"

#pragma comment(lib, "advapi32")
#pragma comment(lib, "ole32")
#pragma comment(lib, "shell32")

#include <iomanip>
#include <iostream>
#include <notificationactivationcallback.h>
#include <propkey.h>
#include <propvarutil.h>
#include <shlobj.h>
#include <winrt/Windows.UI.Notifications.h>
#include <winrt/Windows.Data.Xml.Dom.h>

using namespace winrt;
using namespace Windows::Data::Xml::Dom;
using namespace Windows::UI::Notifications;

int main() { }

项目尚未生成;完成添加代码之后,系统会提示生成并运行。 

实现组件类和类工厂

在 C++/WinRT 中,通过从 winrt::implements 基结构派生来实现组件类和类工厂。 紧接着上面所示的三个 using 指令之后(并且在 main 之前),粘贴此代码以实现 toast 通知 COM 激活器组件。

static constexpr GUID callback_guid // BAF2FA85-E121-4CC9-A942-CE335B6F917F
{
    0xBAF2FA85, 0xE121, 0x4CC9, {0xA9, 0x42, 0xCE, 0x33, 0x5B, 0x6F, 0x91, 0x7F}
};

std::wstring const this_app_name{ L"ToastAndCallback" };

struct callback : winrt::implements<callback, INotificationActivationCallback>
{
    HRESULT __stdcall Activate(
        LPCWSTR app,
        LPCWSTR args,
        [[maybe_unused]] NOTIFICATION_USER_INPUT_DATA const* data,
        [[maybe_unused]] ULONG count) noexcept final
    {
        try
        {
            std::wcout << this_app_name << L" has been called back from a notification." << std::endl;
            std::wcout << L"Value of the 'app' parameter is '" << app << L"'." << std::endl;
            std::wcout << L"Value of the 'args' parameter is '" << args << L"'." << std::endl;
            return S_OK;
        }
        catch (...)
        {
            return winrt::to_hresult();
        }
    }
};

struct callback_factory : implements<callback_factory, IClassFactory>
{
    HRESULT __stdcall CreateInstance(
        IUnknown* outer,
        GUID const& iid,
        void** result) noexcept final
    {
        *result = nullptr;

        if (outer)
        {
            return CLASS_E_NOAGGREGATION;
        }

        return make<callback>()->QueryInterface(iid, result);
    }

    HRESULT __stdcall LockServer(BOOL) noexcept final
    {
        return S_OK;
    }
};

上面的组件类实现遵循在使用 C++/WinRT 创作 API 中演示的相同模式。 因此,可以使用相同技术实现 COM 接口以及 Windows 运行时接口。 COM 组件和 Windows 运行时类会通过接口公开其功能。 每个 COM 接口最终派生自 IUnknown 接口接口。 Windows 运行时基于 COM,一个区别是 Windows 运行时接口最终派生自 IInspectable 接口(并且 IInspectable 派生自 IUnknown)。

在上面代码中的组件类中,我们实现 INotificationActivationCallback::Activate 方法,这是在用户单击 toast 通知上的回调按钮时调用的函数。 但是在可以调用该函数之前,需要创建组件类的实例,这是 IClassFactory::CreateInstance 函数的工作。

我们刚刚实现的组件类称为通知的 COM 激活器,其类 id (CLSID) 的形式为如上所示的 callback_guid 标识符(类型为 GUID)。 我们会在以后采用“开始”菜单快捷方式和 Windows 注册表项的形式来使用该标识符。 COM 激活器 CLSID 以及指向其关联 COM 服务器的路径(这是在此处生成的可执行文件的路径)是一种机制,toast 通知该机制可知道在单击其回调按钮时要创建哪个类的实例(无论是否在操作中心内单击通知)。

实现 COM 方法的最佳做法

用于错误处理和资源管理的方法可以一起进行。 使用异常比错误代码更加方便且实用。 如果采用资源获取即初始化 (RAII) 惯用做法,则可以避免显式检查错误代码,然后显式释放资源。 这类显式检查使代码比实际所需更复杂,并使 bug 有大量位置可以隐藏。 相反,使用 RAII,并引发/捕获异常。 这样,资源分配是异常安全的,并且代码十分简单。

但是,不得允许异常转义 COM 方法实现。 可以通过对 COM 方法使用 noexcept 说明符来确保这一点。 可以在方法调用关系图中的任意位置引发异常,只要在方法退出之前处理它们。 如果使用 noexcept,但随后允许异常转义方法,则应用程序会终止。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值