Oggetti Agile in C++/WinRT

Nella maggior parte dei casi un'istanza di una classe Windows Runtime è accessibile da qualsiasi thread (come la maggior parte degli oggetti C++ standard). Questa classe di Windows Runtime è Agile. Solo un numero ridotto di classi Windows Runtime incluse in Windows non è Agile, ma quando le utilizzi devi prendere in considerazione il modello di threading e il comportamento di marshalling, ovvero del passaggio dei dati attraverso un limite dell'apartment. Il fatto di essere Agile è un'impostazione predefinita ottimale per ogni oggetto Windows Runtime, in quanto rende automaticamente Agile tutti i tipi C++/WinRT di tua proprietà.

Puoi tuttavia rifiutare esplicitamente tale impostazione. Potrebbe infatti esserci qualche importante motivo per cui è necessario che un tipo di oggetto di tua proprietà risieda, ad esempio, in un apartment a thread singolo specifico. Questo in genere ha a che vedere con i requisiti di reintegrazione. Sempre più spesso tuttavia anche le API dell'interfaccia utente offrono oggetti Agile. La metodologia Agile è in generale l'opzione più semplice e a prestazioni più elevate. Inoltre, un'eventuale factory di attivazione implementata deve essere Agile anche se la classe di runtime corrispondente non lo è.

Nota

Windows Runtime è basato su COM. In base a COM, una classe Agile viene registrata con ThreadingModel = Both. Per altre informazioni sui modelli di threading COM, vedi Informazioni e uso dei modelli di threading COM.

Esempi di codice

Usiamo un'implementazione di esempio di una classe di runtime per mostrare come C++/WinRT supporta la metodologia Agile.

#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

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

Poiché non abbiamo scelto il rifiuto esplicito, questa implementazione è Agile. Lo struct di base winrt::implements implementa IAgileObject e IMarshal. L'implementazione IMarshal usa CoCreateFreeThreadedMarshaler per eseguire l'operazione corretta per il codice legacy che non dispone di informazioni su IAgileObject.

Questo codice controlla se un oggetto rispetta la metodologia Agile. La chiamata a IUnknown::as genera un'eccezione se myimpl non è Agile.

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

Anziché gestire un'eccezione, puoi chiamare IUnknown::try_as.

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

IAgileObject non è associato a metodi, dunque non risulta particolarmente utile. Questa variante successiva, è quindi più comune.

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

IAgileObject è un'interfaccia dell'indicatore. L'esito positivo o negativo di una query su IAgileObject è dato dall'estensione delle informazioni e dalla relativa utilità.

Rifiuto esplicito del supporto dell'oggetto Agile

Puoi scegliere di rifiutare esplicitamente il supporto di un oggetto Agile passando lo struct indicatore winrt::non_agile come argomento del modello per la classe base.

Se la classe deriva direttamente da winrt::implements.

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

Se si sta creando una classe di runtime.

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

La posizione in cui appare l'elemento struct dell'indicatore nel pacchetto di parametri variadic non ha importanza.

Indipendentemente dalla scelta del rifiuto esplicito di Agile, puoi comunque implementare IMarshal. Ad esempio, puoi usare l'indicatore winrt::non_agile per evitare l'implementazione Agile predefinita e quindi implementare IMarshal per ottenere il supporto della semantica del marshalling in base al valore.

Riferimenti Agile (winrt::agile_ref)

Se l'oggetto in uso non è Agile, ma devi eseguirne il passaggio in potenziali contesti Agile, un'opzione consiste nell'usare il modello struct winrt::agile_ref per ottenere un riferimento Agile a un'istanza di un tipo non Agile o a un'interfaccia di un oggetto non Agile.

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

In alternativa, puoi usare la funzione helper winrt::make_agile.

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

In entrambi i casi, agile può ora essere liberamente passato a un thread in un apartment diverso e usato in tale ambito.

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

La chiamata a agile_ref::get restituisce un proxy che può essere usato in modo sicuro nel contesto del thread in cui è chiamato get.

API importanti