Agile Objekte in C++/WinRT

In den meisten Fällen kann von jedem Thread aus auf eine Instanz einer Windows-Runtime-Klasse zugegriffen werden (genau wie die meisten C++-Standardobjekte). Eine solche Windows-Runtime Klasse ist agil. Nur eine kleine Anzahl der mit Windows ausgelieferten Windows-Runtime-Klassen ist nicht agil, aber wenn Sie sie verwenden, müssen Sie ihr Threadingmodell und ihr Marshallingverhalten berücksichtigen (Marshalling bezeichnet die Übergabe von Daten über eine Apartmentgrenze). Es ist ein guter Standardwert für jedes Windows-Runtime-Objekt, agil zu sein, sodass Ihre eigenen C++/WinRT-Typen standardmäßig agil sind.

Sie können jedoch darauf verzichten. Möglicherweise haben Sie einen triftigen Grund, zu verlangen, dass sich ein Objekt Ihres Typs beispielsweise in einem bestimmten Single-Threaded Apartment befindet. Das hängt typischerweise mit Reentrancy-Anforderungen zusammen. Aber immer häufiger bieten auch BENUTZERoberflächen-APIs agile Objekte. Im Allgemeinen ist Agilität die einfachste und leistungsfähigste Option. Wenn Sie eine Aktivierungsfactory implementieren, muss sie auch dann agil sein, wenn ihre entsprechende Laufzeitklasse nicht vorhanden ist.

Hinweis

Die Windows-Runtime basiert auf COM. In COM-Ausdrücken wird eine agile Klasse bei ThreadingModel = Both registriert. Weitere Informationen zu COM-Threadingmodellen und Apartments finden Sie unter Grundlegendes zu COM-Threadingmodellen und deren Verwendung.

Code-Beispiele

Wir verwenden eine Beispielimplementierung einer Laufzeitklasse, um zu zeigen, wie C++/WinRT Agilität unterstützt.

#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

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

Da wir uns nicht abgemeldet haben, ist diese Implementierung agil. Die winrt::implements-Basisstruktur implementiert IAgileObject und IMarshal. Die IMarshal-Implementierung verwendet CoCreateFreeThreadedMarshaler, um für älteren Code, der IAgileObject nicht kennt, das Richtige zu tun.

Dieser Code überprüft ein Objekt auf Flexibilität. Der Aufruf von IUnknown::as löst eine Ausnahme aus, wenn myimpl nicht agil ist.

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

Anstatt eine Ausnahme zu behandeln, können Sie stattdessen IUnknown::try_as aufrufen.

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

IAgileObject hat keine eigenen Methoden, sodass Sie damit nicht viel tun können. Diese nächste Variante ist dann typischer.

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

IAgileObject ist eine Markerschnittstelle. Der bloße Erfolg oder das Fehlschlagen einer Abfrage von IAgileObject ist bereits alles an Information und Nutzen, das Sie daraus erhalten.

Unterstützung für agile Objekte deaktivieren

Sie können die Unterstützung agiler Objekte explizit deaktivieren, indem Sie die Winrt::non_agile Markerstruktur als Vorlagenargument an Ihre Basisklasse übergeben.

Wenn Sie direkt von winrt::implements abgeleitet sind.

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

Wenn Sie eine Laufzeitklasse erstellen.

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

Es spielt keine Rolle, wo im variadischen Parameterpaket die Markierungsstruktur angezeigt wird.

Unabhängig davon, ob Sie sich gegen Agilität entscheiden, können Sie IMarshal selbst implementieren. Sie können z. B. den Marker winrt::non_agile verwenden, um die Standardmäßige Agilitätsimplementierung zu vermeiden und IMarshal selbst zu implementieren – vielleicht um Marshal-by-Value-Semantik zu unterstützen.

agile Referenzen (winrt::agile_ref)

Wenn Sie ein Objekt verwenden, das nicht agil ist, sie aber in einem potenziell agilen Kontext übergeben müssen, besteht eine Möglichkeit darin, die Winrt::agile_ref Strukturvorlage zu verwenden, um einen agilen Verweis auf eine Instanz eines nicht agilen Typs oder auf eine Schnittstelle eines nicht agilen Objekts zu erhalten.

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

Sie können auch die Hilfsfunktion "winrt::make_agile " verwenden.

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

In beiden Fällen kann agile jetzt problemlos an einen Thread in einem anderen Apartment übergeben und dort verwendet werden.

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

Der agile_ref::get-Aufruf gibt einen Proxy zurück, der im Threadkontext, in dem get aufgerufen wird, sicher verwendet werden kann.

Wichtige APIs