windows C++-C++/WinRT 中创建组件和事件(上)

本文说明如何通过 C++/WinRT 创建和使用 Windows 运行时组件 - 一种可以从使用任何 Windows 运行时语言生成的通用 Windows 应用调用的组件。

用 C++/WinRT 生成 Windows 运行时组件有多种原因。

  • 在复杂或计算密集型操作中发挥 C++ 的性能优势;
  • 重复使用已编写和测试的标准 C++ 代码;
  • 向用 C# 等语言编写的通用 Windows 平台 (UWP) 应用公开 Win32 功能;

通常,在创作 C++/WinRT 组件时,可以使用标准 C++ 库中的类型和内置类型,但应用程序二进制接口 (ABI) 边界除外,在该边界内,你将与另一个 .winmd 包中的代码进行双向数据传递。 在 ABI 上,使用 Windows 运行时类型。 此外,在 C++/WinRT 代码中,使用委派和事件等类型实现可从组件引发并使用另一种语言处理的事件。 有关 C++/WinRT 的详细信息,请参阅 C++/WinRT。

本文的其余部分介绍如何使用 C++/WinRT 创作 Windows 运行时组件,以及如何从应用程序使用该组件。你将在本文中生成的 Windows 运行时组件包含一个表示温度计的运行时类。 本文还演示了一个核心应用,该应用使用 thermometer 运行时类并调用函数来调节温度。

Windows 运行时组件 dll 的命名最佳做法

本部分介绍建议用于.dll生成Windows 运行时组件的文件(DLL)的命名约定。 使用来自Windows 运行时组件的运行时类时,C++/WinRT 遵循的激活序列。

激活类工厂时,C++/WinRT 首先尝试调用 RoGetActivationFactory。 如果失败,C++/WinRT 会尝试查找直接加载的 DLL。 Windows 运行时激活始终基于完全限定的类名。 逻辑是删除类名(从该完全限定的类名),然后查找为保留的完整命名空间命名的 DLL。 如果未找到,请删除最具体的段名称,然后重复。

例如,如果激活的类具有 Contoso.Instruments.热计WRC.温度计的完全限定名称,并且 RoGetActivationFactory 失败,我们将首先查找一个Contoso.Instruments.ThermometerWRC.dll。 如果未找到,我们将查找 Contoso.Instruments.dll,然后查找 Contoso.dll。

找到 DLL(在该序列中),我们将使用该 DLL 的 DllGetActivationFactory 入口点直接获取工厂(而不是间接通过我们首次尝试的 RoGetActivationFactory 函数)。 即便如此,最终结果与调用方和 DLL 不区分。

此过程是完全自动的,无需注册或工具。 如果要创作Windows 运行时组件,则只需对刚描述的过程使用 DLL 的命名约定即可。 如果你使用的是Windows 运行时组件,并且它未正确命名,则可以按说明选择重命名它。

创建 Windows 运行时组件 (ThermometerWRC)

首先在 Microsoft Visual Studio 中创建新项目。 创建一个 Windows 运行时组件 (C++/WinRT) 项目,然后将其命名为 ThermometerWRC(针对“温度计 Windows 运行时组件”)。 请确保未选中“将解决方案和项目放在同一目录中”。 面向 Windows SDK 的最新正式发布(非预览)版本。 将项目命名为 ThermometerWRC 会让你在执行本主题的其余步骤时拥有最轻松的体验。

暂时不要生成该项目。

该新建项目包含一个名为 Class.idl 的文件。 在解决方案资源管理器中,将该文件重命名为 Thermometer.idl(重命名 .idl 文件还会自动重命名从属的 .h 和 .cpp 文件)。 将 Thermometer.idl 中的内容替换为下表。

// Thermometer.idl
namespace ThermometerWRC
{
    runtimeclass Thermometer
    {
        Thermometer();
        void AdjustTemperature(Single deltaFahrenheit);
    };
}

 保存文件。 该项目目前不会完全生成,但是现在生成很有助益,因为它会生成源代码文件,而你将在该文件中实现 Thermometer 运行时类。 因此,继续生成(此阶段可能发生的生成错误与找不到 Class.h 和 Class.g.h 有关)。

在生成过程中,midl.exe 工具会运行以创建组件的 Windows 运行时元数据文件(即 \ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd)。 然后,cppwinrt.exe 工具运行(具有 -component 选项)以生成源代码文件,从而为你在创作组件时提供支持。 这些文件包含存根,可用于开始实现在 IDL 中声明的 Thermometer 运行时类。 这些存根是 \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.h 和 Thermometer.cpp。

右键单击项目节点,然后单击“打开文件资源管理器中的文件夹”。 执行此操作,将在文件资源管理器中打开项目文件夹。 将存根文件 Thermometer.h 和 Thermometer.cpp 从文件夹 \ThermometerWRC\ThermometerWRC\Generated Files\sources\ 复制到包含项目文件的文件夹(即 \ThermometerWRC\ThermometerWRC\),并替换目标中的文件。 现在,让我们打开 Thermometer.h 和 Thermometer.cpp 并实现运行时类。 在 Thermometer.h 中,将一个新的私有成员添加到 Thermometer 的实现(而不是工厂实现)。

// Thermometer.h
...
namespace winrt::ThermometerWRC::implementation
{
    struct Thermometer : ThermometerT<Thermometer>
    {
        ...

    private:
        float m_temperatureFahrenheit { 0.f };
    };
}
...

 在 Thermometer.cpp 中实现 AdjustTemperature 方法,如下面的列表所示。

// Thermometer.cpp
...
namespace winrt::ThermometerWRC::implementation
{
    void Thermometer::AdjustTemperature(float deltaFahrenheit)
    {
        m_temperatureFahrenheit += deltaFahrenheit;
    }
}

你会在 Thermometer.h 和 Thermometer.cpp 的顶部看到 static_assert(需要删除)。 现在将生成项目。

如果任何警告阻止你进行生成,请处理这些警告或将项目属性“C/C++”>“常规”>“将警告视为错误”设置为“否(/WX-)”,然后重新生成该项目。

创建核心应用 (ThermometerCoreApp) 以测试 Windows 运行时组件

现在创建新项目(在 ThermometerWRC 解决方案中,或在一个新解决方案中)。 创建核心应用 (C++/WinRT) 项目,然后将其命名为 ThermometerCoreApp。 如果两个项目位于同一个解决方案中,则将 ThermometerCoreApp 设置为启动项目。

如前文所述,文件夹 \ThermometerWRC\Debug\ThermometerWRC\ 中创建了 Windows 运行时组件的 Windows 运行时元数据文件(其项目命名为 ThermometerWRC)。 该路径的第一段是包含解决方案文件的文件夹的名称;下一段是名为 Debug 的子目录;最后一段是为 Windows 运行时组件命名的子目录。 如果未将项目命名为 ThermometerWRC,则元数据文件将位于 \<YourProjectName>\Debug\<YourProjectName>\ 文件夹中。

现在,在核心应用项目 (ThermometerCoreApp) 中添加一个引用,然后浏览到 \ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd(或者,如果同一解决方案中有两个项目,则添加一个项目到项目的引用)。 单击“添加”,然后单击“确定”。 现在生成 ThermometerCoreApp。 在极少数情况下,如果看到一个显示有效负载文件 readme.txt 不存在的错误,请从 Windows 运行时组件项目中排除该文件,重新生成该文件,然后重新生成 ThermometerCoreApp。

生成过程期间,cppwinrt.exe 工具会运行以将引用的 .winmd 文件处理到包含投影类型的源代码文件中,从而为你在使用组件时提供支持。 组件的运行时类的投影类型的头文件(名为 ThermometerWRC.h)将生成到文件夹 \ThermometerCoreApp\ThermometerCoreApp\Generated Files\winrt\ 中。

App.cpp 中包含该头文件。

// App.cpp
...
#include <winrt/ThermometerWRC.h>
...

 此外,在 App.cpp 中,添加以下代码以实例化 Thermometer对象(使用投影类型的默认构造函数),并对 thermometer 对象调用方法。

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer m_thermometer;
    ...
    
    void OnPointerPressed(IInspectable const &, PointerEventArgs const & args)
    {
        m_thermometer.AdjustTemperature(1.f);
        ...
    }
    ...
};

每次单击窗口时,thermometer 对象的温度都会增加。 如果要单步执行代码,以确认应用程序确实正在调用 Windows 运行时组件,则可以设置断点。 

事件的设计指南

建议将事件而不是委托作为函数参数传递。 winrt::event 的 add 函数是例外,因为在这种情况下必须传递委托 。 这条指导原则的原因是,委托在各种 Windows 运行时语中可以具有各种形式(根据支持一个还是多个客户端注册)。 事件及其多个订阅者模型构成更可预测且更一致的选项。

事件处理程序委托的签名应该由以下两个形参组成:发送方 (IInspectable) 和实参(RoutedEventArgs 等事件实参类型)。

注意,如果设计的是内部 API,这些指导原则不一定适用。 但随时间推移,内部 API 通常会公开。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值