Agile Objekte in C++/WinRT
In den meisten Fällen kann von jedem Thread aus auf eine Instanz einer Windows-Runtime-Klasse zugegriffen werden (wie es bei den meisten C++-Standardobjekten der Fall ist). Eine solche Windows-Runtime-Klasse ist agile. Nur eine kleine Anzahl von Windows-Runtime-Klassen, die mit Windows bereitgestellt werden, sind nicht agil. Wenn Sie sie nutzen, müssen Sie ihr Threadingmodell und ihr Marshallingverhalten berücksichtigen (Marshalling ist die Weitergabe von Daten über eine Apartmentgrenze). Es ist ein guter Standard für jedes Windows-Runtime-Objekt, agil zu sein, sodass Ihre eigenen C++/WinRT-Typen standardmäßig agil sind.
Sie können jedoch auch von dieser Regel abweichen. Möglicherweise haben Sie einen zwingenden Grund, ein Objekt Ihres Typs zu beschränken (zum Beispiel in einer Anwendung mit einem einzelnen Thread). Dies hat typischerweise mit Wiedereinsprungsvoraussetzungen zu tun. Aber auch die UI-APIs (User Interface, Benutzerschnittstelle) bieten zunehmend agile Objekte. Im Allgemeinen ist Agilität die einfachste und leistungsfähigste Option. Auch wenn Sie eine Aktivierungsfactory implementieren, muss diese agil sein (selbst dann, wenn Ihre entsprechende Laufzeitklasse es nicht ist).
Hinweis
Windows-Runtime basiert auf COM. Im Sinne von COM wird eine agile Klasse mit ThreadingModel
= Both registriert. Weitere Informationen zu COM-Threadingmodellen finden Sie unter Verstehen und Verwenden von COM-Threadingmodellen.
Codebeispiele
Sehen wir uns anhand einer Beispielimplementierung einer Laufzeitklasse an, 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 dies nicht explizit ausschließen, ist diese Implementierung agil. Die winrt::implementiert-Basisstruktur implementiert IAgileObject und IMarshal. Die IMarshal-Implementierung verwendet CoCreateFreeThreadedMarshaler, um Legacycode zu unterstützen, dem IAgileObject nicht bekannt ist.
Dieser Code prüft ein Objekt auf Agilitä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 verarbeiten, können Sie IUnknown::try_as aufrufen.
winrt::com_ptr<IAgileObject> iagileobject{ myimpl.try_as<IAgileObject>() };
if (iagileobject) { /* myimpl is agile. */ }
IAgileObject hat keine eigenen Methoden, sodass hier keine Möglichkeiten vorhanden sind. Diese nächste Variante ist eher typisch.
if (myimpl.try_as<IAgileObject>()) { /* myimpl is agile. */ }
IAgileObject ist eine Markerschnittstelle. Der Nutzen der Abfrage von IAgileObject besteht aus den Informationen zum Erfolg oder Misserfolg.
Deaktivieren der Unterstützung von agilen Objekten
Sie können die Unterstützung für agile Objekte explizit deaktivieren, indem Sie die winrt::non_agile-Markerstruktur als template-Argument an Ihre Basisklasse übergeben.
Wenn Sie direkt von winrt::implements ableiten.
struct MyImplementation: implements<MyImplementation, IStringable, winrt::non_agile>
{
...
}
Wenn Sie eine Laufzeitklasse erstellen.
struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, winrt::non_agile>
{
...
}
Dabei spielt es keine Rolle, wo im variadic-Parameterpaket die Markerstruktur verwendet wird.
Unabhängig davon, ob Sie die Agilität abwählen, können Sie IMarshal selbst implementieren. Beispielsweise können Sie den Marker winrt::non_agile verwenden, um die Agilitätsstandardimplementierung zu vermeiden, und IMarshal selbst implementieren (um die Marshal-by-Value-Semantik zu unterstützen).
Agile-Verweise (winrt::agile_ref)
Wenn Sie ein Objekt verwenden, das nicht agil ist, Sie es aber in einem potenziell agilen Kontext übergeben müssen, dann ist eine Option die Verwendung der winrt::agile_ref-Strukturvorlage. So erhalten Sie einen agilen Verweis auf eine Instanz eines nicht agilen Typs oder auf eine Schnittstelle eines nicht agilen Objekts.
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
nun frei 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 Aufruf agile_ref::get gibt einen Proxy zurück, der im Threadkontext, in dem get aufgerufen wird, sicher verwendet werden kann.
Wichtige APIs
- IAgileObject-Schnittstelle
- IMarshal-Schnittstelle
- winrt::agile_ref-Strukturvorlage
- Strukturvorlage „winrt::implements“
- winrt::make_agile Funktionsvorlage
- winrt::non_agile-Markerstruktur
- winrt::Windows::Foundation::IUnknown::as-Funktion
- winrt::Windows::Foundation::IUnknown::try_as-Funktion