C++/WinRT 中的敏捷对象

在绝大多数情况下,可以从任何线程(就像大多数标准 C++ 对象一样)访问Windows 运行时类的实例。 此类 Windows 运行时 类是敏捷的。 只有少量随 Windows 一同提供的 Windows 运行时 类是非敏捷的,但在使用它们时,你需要考虑其线程模型和封送处理行为(封送处理是指跨单元边界传递数据)。 将每个 Windows 运行时 对象默认设为敏捷型是个不错的选择,因此你自己的 C++/WinRT 类型默认也是敏捷型。

但你也可以选择不这样做。例如,你可能有充分的理由要求你的类型的对象驻留在某个给定的单线程单元中。 这通常与重新进入要求有关。 但是,即使是用户界面(UI)API 也越来越提供敏捷对象。 一般来说,敏捷性是最简单、性能最高的选项。 此外,当你实现激活工厂时,即使相应的运行时类不是敏捷的,激活工厂也必须是敏捷的。

注释

Windows 运行时基于 COM。 用 COM 的术语来说,敏捷类注册为 ThreadingModel = Both。 有关 COM 线程模型和单元的详细信息,请参阅 了解和使用 COM 线程模型

代码示例

让我们使用运行时类的示例实现来说明 C++/WinRT 如何支持敏捷性。

#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

struct MyType : winrt::implements<MyType, IStringable>
{
    winrt::hstring ToString(){ ... }
};

由于我们尚未选择退出,因此此实现是敏捷的。 winrt::implements 基结构实现了 IAgileObjectIMarshalIMarshal 实现使用 CoCreateFreeThreadedMarshaler,以便对不识别 IAgileObject 的旧代码做正确处理。

此代码检查对象是否敏捷。 调用 IUnknown::as 会引发异常(如果 myimpl 不是敏捷)。

winrt::com_ptr<MyType> myimpl{ winrt::make_self<MyType>() };
winrt::com_ptr<IAgileObject> iagileobject{ myimpl.as<IAgileObject>() };

可以改为调用 IUnknown::try_as ,而不是处理异常。

winrt::com_ptr<IAgileObject> iagileobject{ myimpl.try_as<IAgileObject>() };
if (iagileobject) { /* myimpl is agile. */ }

IAgileObject 本身没有任何方法,因此你无法用它做太多事情。 接下来的变体更典型。

if (myimpl.try_as<IAgileObject>()) { /* myimpl is agile. */ }

IAgileObject 是一个 标记接口。 对于 IAgileObject 的查询,你所能从中获得的信息和用处,仅限于它是成功还是失败。

停用敏捷对象支持

可以通过将 winrt::non_agile 标记结构作为模板参数传递给基类来显式选择退出敏捷对象支持。

如果直接从 winrt::implements 派生。

struct MyImplementation: implements<MyImplementation, IStringable, winrt::non_agile>
{
    ...
}

如果要编写运行时类。

struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, winrt::non_agile>
{
    ...
}

在可变参数包中,标记结构出现的位置并不重要。

无论你是否选择退出敏捷性,都可以自行实现 IMarshal 。 例如,可以使用 winrt::non_agile 标记来避免默认敏捷性实现,并自行实现 IMarshal ,也许支持按值封送语义。

敏捷引用(winrt::agile_ref)

如果你使用的对象不是敏捷的,但你需要在一些潜在的敏捷上下文中传递它,那么一个选项是使用 winrt::agile_ref 结构模板来获取对非敏捷类型的实例的敏捷引用,或者传递给非敏捷对象的接口。

NonAgileType nonagile_obj;
winrt::agile_ref<NonAgileType> agile{ nonagile_obj };

或者,您可以使用 winrt::make_agile 帮助器函数。

NonAgileType nonagile_obj;
auto agile{ winrt::make_agile(nonagile_obj) };

无论哪种情况,agile 现在都可以自由地传递到不同单元的线程中,并在那里使用。

co_await resume_background();
NonAgileType nonagile_obj_again{ agile.get() };
winrt::hstring message{ nonagile_obj_again.Message() };

agile_ref::get 调用返回一个可在调用 get 的线程上下文中安全使用的代理。

重要 API