C++/WinRT 中的敏捷对象Agile objects in C++/WinRT

在大多数情况下,Windows 运行时类的实例可以从任何线程访问(如同大多数标准的 C++ 对象一样)。In the vast majority of cases, an instance of a Windows Runtime class can be accessed from any thread (just like most standard C++ objects can). 此类 Windows 运行时类是敏捷的 。Such a Windows Runtime class is agile. Windows 附带的 Windows 运行时类中只有一小部分是非敏捷的,但当你使用它们时,你需要考虑它们的线程模型和封送行为(封送是指跨单元边界传送数据)。Only a small number of Windows Runtime classes that ship with Windows are non-agile, but when you consume them you need to take into consideration their threading model and marshaling behavior (marshaling is passing data across an apartment boundary). 好消息是,每个 Windows 运行时对象默认情况下都是敏捷的,因此你自己的 C++/WinRT 类型默认情况下也是敏捷的。It's a good default for every Windows Runtime object to be agile, so your own C++/WinRT types are agile by default.

但你可以选择退出。例如,你可能会有令人信服的理由来要求类型的对象驻留在某个给定的单线程单元中。But you can opt out. You might have a compelling reason to require an object of your type to reside, for example, in a given single-threaded apartment. 对于重入要求,通常需要这样做。This typically has to do with reentrancy requirements. 不过,敏捷对象越来越多,甚至用户界面 (UI) API 也提供敏捷对象。But increasingly, even user interface (UI) APIs offer agile objects. 总之,敏捷性是最简单、最有效率的选项。In general, agility is the simplest and most performant option. 此外,当实现一个激活工厂时,即使对应的运行时类不是敏捷的,激活工厂也必须是敏捷的。Also, when you implement an activation factory, it must be agile even if your corresponding runtime class isn't.


Windows 运行时基于 COM。The Windows Runtime is based on COM. 在 COM 环境中,注册的敏捷类使用 ThreadingModel = Both 。In COM terms, an agile class is registered with ThreadingModel = Both. 有关 COM 线程模型和单元的详细信息,请参阅了解和使用 COM 线程模型For more info about COM threading models, and apartments, see Understanding and Using COM Threading Models.

代码示例Code examples

我们将使用运行时类的示例实现以说明 C++/WinRT 是如何支持敏捷性的。Let's use an example implementation of a runtime class to illustrate how C++/WinRT supports agility.

#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

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

由于我们没有选择退出,因此此实现是敏捷的。Because we haven't opted out, this implementation is agile. winrt::implements 基结构实现 IAgileObjectIMarshalThe winrt::implements base struct implements IAgileObject and IMarshal. IMarshal 实现使用 CoCreateFreeThreadedMarshaler 来为不了解 IAgileObject 的旧代码执行正确的操作 。The IMarshal implementation uses CoCreateFreeThreadedMarshaler to do the right thing for legacy code that doesn't know about IAgileObject.

此代码检查对象的敏捷性。This code checks an object for agility. 如果 myimpl 不是敏捷的,则对 IUnknown::as 的调用将引发异常 。The call to IUnknown::as throws an exception if myimpl is not agile.

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

你可以调用 IUnknown::try_as,而不必处理异常 。Rather than handle an exception, you can call IUnknown::try_as instead.

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

IAgileObject 没有自己的方法,因此你无法使用它执行很多操作 。IAgileObject has no methods of its own, so you can't do much with it. 接下来的变体将更典型一些。This next variant, then, is more typical.

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

IAgileObject 是一个标记接口 。IAgileObject is a marker interface. IAgileObject 查询的成功或失败只不过是你从中获取的信息和实用工具的多少而已 。The mere success or failure of querying for IAgileObject is the extent of the information and utility you get from it.

选择退出敏捷对象支持Opting out of agile object support

你可通过以下方法选择明确退出敏捷对象支持:将 winrt::non_agile 标记结构作为模板参数传递给基类 。You can choose explicitly to opt out of agile object support by passing the winrt::non_agile marker struct as a template argument to your base class.

如果你直接从 winrt::implements 派生 。If you derive directly from winrt::implements.

struct MyImplementation: implements<MyImplementation, IStringable, winrt::non_agile>

如果正在创作运行时类。If you're authoring a runtime class.

struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, winrt::non_agile>

标记结构在 variadic 参数包中的位置无关紧要。It doesn't matter where in the variadic parameter pack the marker struct appears.

无论是否选择退出敏捷性,均可自行实现 IMarshal 。Whether or not you opt out of agility, you can implement IMarshal yourself. 例如,可使用 winrt::non_agile 标记来避免默认的敏捷性实现,并自行实现 IMarshal(可能是为了支持按值封送语义) 。For example, you can use the winrt::non_agile marker to avoid the default agility implementation, and implement IMarshal yourself—perhaps to support marshal-by-value semantics.

敏捷引用 (winrt::agile_ref)Agile references (winrt::agile_ref)

如果你正在使用一个非敏捷对象,但你需要在某些潜在的敏捷上下文中传递该对象,那么一个选项是使用 winrt::agile_ref 结构模板来获取对非敏捷类型的实例或非敏捷对象的接口的敏捷引用 。If you're consuming an object that isn't agile, but you need to pass it around in some potentially agile context, then one option is to use the winrt::agile_ref struct template to get an agile reference to an instance of a non-agile type, or to an interface of a non-agile object.

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

或者,你可以使用 winrt::make_agile 帮助程序函数 。Or, you can use the use the winrt::make_agile helper function.

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

在上述任一种情况中,agile 现在可以自由地传递到不同的单元中的线程并在其中使用。In either case, agile may now be freely passed to a thread in a different apartment, and used there.

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

Agile_ref::get 调用将返回一个可在调用 get 的线程上下文中安全使用的代理 。The agile_ref::get call returns a proxy that may safely be used within the thread context in which get is called.

重要的 APIImportant APIs