C++/WinRT 中的敏捷物件

在絕大多數情況下,Windows 執行階段 類別的實例可以從任何執行緒存取(就像大多數標準 C++ 物件一樣)。 這樣的 Windows 執行階段 類別是敏捷的。 只有少數隨 Windows 附帶的 Windows 執行階段 類別是非敏捷的,但當你使用它們時,必須考慮它們的執行緒模型和編組行為(封組是指跨越公寓邊界的資料傳輸)。 將每個 Windows 執行階段 物件預設為靈活物件是個不錯的做法,因此你自己的 C++/WinRT 類型預設也是靈活的。

但你可以選擇退出。例如,你可能有充分理由要求你這類型的物件存在於某個單線公寓裡。 這通常與再入境要求有關。 但現在,即使是使用者介面(UI)API也提供敏捷的物件。 一般來說,敏捷是最簡單且效能最高的選擇。 另外,當你實作啟動工廠時,即使對應的執行階段類別不具敏捷性,啟動工廠本身也必須具備敏捷性。

Note

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 的舊版程式碼做出正確處理。

這段程式碼用來檢查物件的敏捷性。 如果 myimpl 不具敏捷性,呼叫 IUnknown::as 會擲回例外。

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 ——或許是為了支援按值的 marshal 語意。

敏捷參考(winrt::agile_ref)

如果你正在使用一個非敏捷的物件,但需要在某種潛在敏捷的情境中傳遞它,那麼一個選擇是使用 winrt::agile_ref struct 模板,取得非敏捷類型的敏捷參考,或非敏捷物件的介面。

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