Threading und Marshalling (C++/CX)

In den meisten Fällen können Instanzen von Windows-Runtime Klassen, z. B. C++-Standardobjekten, von jedem Thread aus aufgerufen werden. Solche Klassen werden als "agil" bezeichnet. Eine kleine Anzahl von Windows-Runtime Klassen, die mit Windows ausgeliefert werden, sind jedoch nicht agil und müssen mehr wie COM-Objekte als C++-Standardobjekte genutzt werden. Sie müssen zwar kein COM-Experte sein, um nicht agile Klassen zu verwenden, aber das Threadmodell dieser Klassen sowie ihr Marshallingverhalten beachten. Dieser Artikel ist ein Leitfaden für die seltenen Fälle, in denen Sie die Instanz einer nicht agilen Klasse verarbeiten müssen.

Threadmodell und Marshallingverhalten

Eine Windows-Runtime Klasse kann gleichzeitigen Threadzugriff auf verschiedene Arten unterstützen, wie durch zwei Attribute angegeben, die darauf angewendet werden:

  • Das AttributThreadingModel kann einen der Werte STA, MTA oder Both besitzen, die durch die Enumeration ThreadingModel definiert werden.

  • Das AttributMarshallingBehavior kann einen der Werte Agile, None oder Standard besitzen, die durch die Enumeration MarshallingType definiert werden.

Das Attribut ThreadingModel gibt an, wo die Klasse bei der Aktivierung geladen wird: Nur in einem Benutzeroberflächenthread-Kontext (STA), nur in einem Hintergrundthread-Kontext (MTA) oder im Kontext des Threads, der das Objekt erstellt (Both). Die MarshallingBehavior Attributwerte beziehen sich auf das Verhalten des Objekts in den verschiedenen Threadingkontexten. In den meisten Fällen müssen Sie diese Werte nicht detailliert verstehen. Von den Klassen, die von der Windows-API bereitgestellt werden, haben ungefähr 90 Prozent ThreadingModel=Both und MarshallingType=Agile. Das bedeutet, dass sie Threadingdetails auf niedriger Ebene transparent und effizient verarbeiten können. Wenn Sie ref new verwenden, um eine "agile" Klasse zu erstellen, können Sie Methoden dafür aus dem Haupt-App-Thread oder aus einem oder mehreren Arbeitsthreads aufrufen. Das heißt, Sie können von jeder Stelle im Code aus eine agile Klasse verwenden – unabhängig davon, ob sie von Windows oder von einem Drittanbieter bereitgestellt wird. Sie müssen sich nicht mit dem Threadingmodell oder dem Marshallingverhalten der Klasse befassen.

Verbrauch Windows-Runtime Komponenten

Wenn Sie eine Universelle Windows-Plattform-App erstellen, können Sie sowohl mit agilen als auch nicht agilen Komponenten interagieren. Wenn Sie mit nicht agilen Komponenten interagieren, wird möglicherweise die folgende Warnung angezeigt.

Compilerwarnung C4451 beim Verbrauch von nicht agilen Klassen

Aus verschiedenen Gründen können einige Klassen nicht agil sein. Wenn Sie auf Instanzen von nicht agilen Klassen sowohl über einen Benutzeroberflächenthread als auch über einen Hintergrundthread zugreifen, achten Sie besonders darauf, das richtige Verhalten zur Laufzeit sicherzustellen. Der Microsoft C++-Compiler gibt Warnungen aus, wenn Sie eine nicht agile Laufzeitklasse in Ihrer App im globalen Bereich instanziieren oder einen nicht agilen Typ als Klassenmitglied in einer Referenzklasse deklarieren, die selbst als agil gekennzeichnet ist.

Von den nicht agilen Klassen sind diejenigen am einfachsten zu behandeln, die ThreadingModel=Both und MarshallingType=Standard haben. Sie können diese Klassen agil machen, indem Sie einfach die Hilfsklasse Agile<T> verwenden. Das folgende Beispiel zeigt eine Deklaration eines nicht agilen Objekts des Typs Windows::Security::Credentials::UI::CredentialPickerOptions^und die Compilerwarnung, die infolgedessen ausgegeben wird.

ref class MyOptions
    {
    public:
        property Windows::Security::Credentials::UI::CredentialPickerOptions^ Options

        {
            Windows::Security::Credentials::UI::CredentialPickerOptions^ get()
            {
                return _myOptions;
            }
        }
    private:
        Windows::Security::Credentials::UI::CredentialPickerOptions^ _myOptions;
    };

Hier ist die Warnung, die ausgegeben wird:

Warning 1 warning C4451: 'Platform::Agile<T>::_object' : Usage of ref class 'Windows::Security::Credentials::UI::CredentialPickerOptions' inside this context can lead to invalid marshaling of object across contexts. Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' instead

Wenn Sie im Member- oder im globalen Gültigkeitsbereich zu einem Objekt einen Verweis hinzufügen, der das Marshallingverhalten "Standard" hat, gibt der Compiler eine Warnung aus, die Sie auffordert, den Typ stattdessen in Platform::Agile<T>: Consider using 'Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions>' instead zu umschließen. Wenn Sie Agile<T>verwenden, können Sie die Klasse wie jede andere agile Klasse nutzen. Verwenden Sie in diesem Fall Platform::Agile<T> :

  • Die nicht agile Variable wird im globalen Gültigkeitsbereich deklariert.

  • Die nicht agile Variable wird im Klassengültigkeitsbereich deklariert, und es besteht die Möglichkeit, dass verwendeter Code den Zeiger einschmuggelt, d. h. ihn in einem anderen Apartment ohne das richtige Marshalling verwendet.

Ist keine dieser Bedingungen zutreffend, können Sie die enthaltende Klasse als nicht agil markieren. Mit anderen Worten: Sie sollten nicht agile Objekte nur in nicht agilen Klassen enthalten und nicht agile Objekte über Platform::Agile<T> in agilen Klassen halten.

Im folgenden Beispiel wird gezeigt, wie Sie Agile<T> verwenden müssen, damit Sie die Warnung sicher ignorieren können.

#include <agile.h>
ref class MyOptions
    {
    public:
        property Windows::Security::Credentials::UI::CredentialPickerOptions^ Options

        {
            Windows::Security::Credentials::UI::CredentialPickerOptions^ get()
            {
                return m_myOptions.Get();
            }
        }
    private:
        Platform::Agile<Windows::Security::Credentials::UI::CredentialPickerOptions^> m_myOptions;

    };

Beachten Sie, dass Agile nicht als Rückgabewert oder Parameter in einer Verweisklasse übergeben werden kann. Die Methode Agile<T>::Get() gibt ein "handle-to-object" (^) zurück, das Sie über die Anwendungsbinärdateischnittstelle (ABI) in einer öffentlichen Methode oder Eigenschaft übergeben können.

Wenn Sie einen Verweis auf eine proc-Windows-Runtime-Klasse erstellen, die ein Marshallingverhalten von "None" aufweist, gibt der Compiler die Warnung C4451 aus, schlägt jedoch nicht vor, dass Sie die Verwendung in Platform::Agile<T>Betracht ziehen. Der Compiler kann über diese Warnung hinaus keine Hilfe anbieten, sodass es in Ihrer Verantwortung liegt, die Klasse richtig zu verwenden und sicherzustellen, dass der Code STA-Komponenten nur aus dem Benutzeroberflächenthread und MTA-Komponenten nur aus einem Hintergrundthread aufruft.

Erstellen agiler Windows-Runtime-Komponenten

Wenn Sie eine Verweisklasse in C++/CX definieren, ist sie standardmäßig agil – d. h. sie hat ThreadingModel=Both und MarshallingType=Agile. Wenn Sie die Windows-Runtime C++-Vorlagenbibliothek verwenden, können Sie Ihre Klasse agil gestalten, indem Sie von FtmBase, die die FreeThreadedMarshaller. Wenn Sie eine Klasse erstellen, die ThreadingModel=Both oder ThreadingModel=MTA hat, überprüfen Sie, ob die Klasse threadsicher ist.

Sie können das Threadingmodell und das Marshallingverhalten einer Verweisklasse ändern. Wenn Sie Änderungen vornehmen, die die Klasse zu "nicht agil" rendern, müssen Sie die Auswirkungen verstehen, die mit diesen Änderungen verbunden sind.

Das folgende Beispiel zeigt, wie Sie eine Laufzeitklasse in einer Windows-Runtime-Klassenbibliothek anwenden und ThreadingModel Attribute anwendenMarshalingBehavior. Wenn eine App die DLL verwendet und das Schlüsselwort ref new benutzt, um ein MySTAClass -Klassenobjekt zu aktivieren, ist das Objekt in einem Singlethread-Apartment aktiviert und unterstützt kein Marshalling.

using namespace Windows::Foundation::Metadata;
using namespace Platform;

[Threading(ThreadingModel::STA)]
[MarshalingBehavior(MarshalingType::None)]
public ref class MySTAClass
{
};

In einer unversiegelten Klasse müssen die Marshalling- und Threadingattribute festgelegt sein, damit der Compiler überprüfen kann, ob abgeleitete Klassen für diese Attribute den gleichen Wert haben. Wenn die Klasse diese Einstellungen nicht aufweist, generiert der Compiler einen Fehler, und es findet keine Kompilierung statt. Jede Klasse, die von einer unversiegelten Klasse abgeleitet ist, generiert in den folgenden Fällen einen Compilerfehler:

  • Die Attribute ThreadingModel und MarshallingBehavior sind in der abgeleiteten Klasse nicht definiert.

  • Die Werte der Attribute ThreadingModel und MarshallingBehavior stimmen in der abgeleiteten Klasse nicht mit denen in der Basisklasse überein.

Die Threading- und Marshallinginformationen, die von einer Drittanbieter-Windows-Runtime Komponente benötigt werden, werden in den App-Manifestregistrierungsinformationen für die Komponente angegeben. Es wird empfohlen, alle Ihre Windows-Runtime Komponenten agil zu gestalten. Dadurch wird sichergestellt, dass Clientcode die Komponente von jedem Thread in der App aufrufen kann. Außerdem wird dadurch die Leistung dieser Aufrufe verbessert, da sie direkte Aufrufe sind, die kein Marshalling haben. Wenn Sie die Klasse auf diese Weise erstellen, braucht Clientcode Platform::Agile<T> nicht zu verwenden, um die Klasse zu nutzen.

Siehe auch

ThreadingModel
MarshallingBehavior