Utilizzare API con C++/WinRT

Questo argomento descrive come utilizzare le API C++/WinRT, che siano parte di Windows, vengano implementate da un fornitore di componenti di terze parti o dall'utente stesso.

Importante

Affinché gli esempi di codice di questo argomento siano brevi e facili da provare, è possibile riprodurli creando un nuovo progetto di Applicazione console di Windows (C++/WinRT) e copiando il codice. Tuttavia, non è possibile usare tipi di Windows Runtime personalizzati (di terze parti) da un'applicazione non in pacchetto come questa. In questo modo è possibile usare solo i tipi di Windows.

Per usare tipi di Windows Runtime personalizzati (di terze parti) da un'app console, è necessario fornire all'applicazione un'identità del pacchetto in modo che possa risolvere la registrazione dei tipi personalizzati usati. Per altre informazioni, vedere Progetto di creazione pacchetti per applicazioni Windows.

In alternativa, creare un nuovo progetto dai modelli di progetto App vuota (C++/WinRT), App core (C++/WinRT) o Componente Windows Runtime (C++/WinRT). Questi tipi di app hanno già un'identità del pacchetto.

Se l'API si trova in uno spazio dei nomi Windows

Questo è il caso più comune di utilizzo di un'API Windows Runtime. Per ogni tipo in uno spazio dei nomi Windows definito nei metadati, C++/WinRT definisce un equivalente C++ (chiamato il tipo proiettato). Un tipo proiettato ha lo stesso nome completo del tipo Windows, ma viene posizionato nello spazio dei nomi winrt C++ tramite la sintassi C++. Ad esempio, la classe Windows::Foundation::Uri viene proiettata in C++/WinRT come winrt::Windows::Foundation::Uri.

Ecco un semplice esempio di codice. Se vuoi copiare e incollare i seguenti esempi di codice direttamente nel file di codice sorgente principale di un progetto Applicazione console Windows (C++/WinRT), imposta prima di tutto Senza intestazioni precompilate nelle proprietà del progetto.

// main.cpp
#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    winrt::init_apartment();
    Uri contosoUri{ L"http://www.contoso.com" };
    Uri combinedUri = contosoUri.CombineUri(L"products");
}

L'intestazione inclusa winrt/Windows.Foundation.h fa parte dell'SDK, disponibile all'interno della cartella %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt\. Le intestazioni in tale cartella contengono tipi di spazi dei nomi Windows proiettati in C++/WinRT. In questo esempio winrt/Windows.Foundation.h contiene winrt::Windows::Foundation::Uri, ovvero il tipo proiettato per la classe di runtime Windows::Foundation::Uri.

Suggerimento

Ogni volta che desideri usare un tipo da uno spazio dei nomi di Windows, includi l'intestazione C++/WinRT corrispondente nello spazio dei nomi. Le direttive using namespace sono facoltative, ma utili.

Nell'esempio di codice precedente dopo l'inizializzazione di C++/WinRT, eseguiamo l'allocazione di gruppo del tipo proiettato winrt::Windows::Foundation::Uri tramite uno dei costruttori documentati pubblicamente (Uri(String), in questo esempio). Per questo caso d'uso, che è il più comune, è in genere tutto ciò che è necessario fare. Dopo aver ottenuto il valore di un tipo proiettato C++/WinRT, è possibile utilizzarlo come se fosse un'istanza del tipo Windows Runtime effettivo, poiché dispone degli stessi membri.

In realtà, tale valore proiettato è un proxy. Si tratta in pratica di un puntatore intelligente di un oggetto di supporto. I costruttori del valore proiettato chiamano RoActivateInstance per creare un'istanza della classe Windows Runtime di supporto (Windows.Foundation.Uri, in questo caso) e archiviare l'interfaccia predefinita dell'oggetto all'interno del nuovo valore proiettato. Come illustrato di seguito, le chiamate ai membri del valore proiettato delegano, tramite il puntatore intelligente, all'oggetto di supporto, ovvero dove avvengono le modifiche dello stato.

Il tipo Windows::Foundation::Uri proiettato

Quando il valore contosoUri non rientra nell'ambito, viene distrutto e rilascia il relativo riferimento all'interfaccia predefinita. Se si tratta dell'ultimo riferimento per l'oggetto Windows.Foundation.Uri Windows Runtime di supporto, viene distrutto anche l'oggetto di supporto.

Suggerimento

Un tipo proiettato è un wrapper su un tipo Windows Runtime per scopi di utilizzo delle API. Ad esempio, un'interfaccia proiettata è un wrapper su un'interfaccia di Windows Runtime.

Intestazioni di proiezione C++/WinRT

Per l'utilizzo delle API dello spazio dei nomi Windows da C++/WinRT, includi le intestazioni della cartella %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt. È necessario includere le intestazioni corrispondenti a ogni spazio dei nomi usato.

Ad esempio, per lo spazio dei nomi Windows::Security::Cryptography::Certificates, le definizioni dei tipi C++/WinRT equivalenti si trovano in winrt/Windows.Security.Cryptography.Certificates.h. L'inclusione di tale intestazione consente di accedere a tutti i tipi nello spazio dei nomi Windows::Security::Cryptography::Certificates .

In alcuni casi, un'intestazione dello spazio dei nomi includerà parti di intestazioni dello spazio dei nomi correlate, ma non è consigliabile basarsi su questo dettaglio di implementazione. Includere in modo esplicito le intestazioni per gli spazi dei nomi usati.

Ad esempio, il metodo Certificate::GetCertificateBlob restituisce un'interfaccia Windows::Archiviazione::Flussi::IBuffer. Prima di chiamare il metodo Certificate::GetCertificateBlob, è necessario includere il winrt/Windows.Storage.Streams.h file di intestazione dello spazio dei nomi per assicurarsi di poter ricevere e operare sull'oggetto Windows::Archiviazione::Flussi::IBuffer restituito.

Dimenticare di includere le intestazioni dello spazio dei nomi necessarie prima di usare i tipi in tale spazio dei nomi è un'origine comune di errori di compilazione.

Accesso ai membri tramite l'oggetto, un'interfaccia o l'ABI

Con la proiezione C++/WinRT la rappresentazione di runtime di una classe di Windows Runtime è costituita esclusivamente dalle interfacce ABI sottostanti. Tuttavia, per praticità, puoi scrivere codice per le classi nel modo previsto dal relativo autore. Ad esempio, puoi chiamare il metodo ToString di un Uri come se fosse un metodo della classe (in realtà, si tratta di un metodo dell'interfaccia IStringable separata).

WINRT_ASSERT è una definizione di macro e si espande in ASSERTE.

Uri contosoUri{ L"http://www.contoso.com" };
WINRT_ASSERT(contosoUri.ToString() == L"http://www.contoso.com/"); // QueryInterface is called at this point.

Questo vantaggio viene ottenuto tramite una query per l'interfaccia appropriata. Ma sei sempre tu che hai il controllo. Puoi scegliere di rinunciare a una parte di tale vantaggio a favore di un leggero aumento delle prestazioni, recuperando manualmente l'interfaccia IStringable e utilizzandola direttamente. Nell'esempio di codice riportato di seguito, ottieni un puntatore di interfaccia IStringable effettivo in fase di esecuzione (tramite una query monouso). La chiamata a ToString viene quindi diretta e impedisce eventuali ulteriori chiamate a QueryInterface.

...
IStringable stringable = contosoUri; // One-off QueryInterface.
WINRT_ASSERT(stringable.ToString() == L"http://www.contoso.com/");

Puoi scegliere questa tecnica se sai che chiamerai diversi metodi sulla stessa interfaccia.

In alcuni casi, puoi accedere ai membri a livello di ABI, se lo desideri. L'esempio di codice seguente mostra come fare. Ulteriori dettagli ed esempi di codice sono inoltre disponibili in Interoperabilità tra C++/WinRT e ABI.

#include <Windows.Foundation.h>
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
using namespace winrt::Windows::Foundation;

int main()
{
    winrt::init_apartment();
    Uri contosoUri{ L"http://www.contoso.com" };

    int port{ contosoUri.Port() }; // Access the Port "property" accessor via C++/WinRT.

    winrt::com_ptr<ABI::Windows::Foundation::IUriRuntimeClass> abiUri{
        contosoUri.as<ABI::Windows::Foundation::IUriRuntimeClass>() };
    HRESULT hr = abiUri->get_Port(&port); // Access the get_Port ABI function.
}

Inizializzazione ritardata

In C++/WinRT ogni tipo proiettato ha un costruttore std::nullptr_t speciale. Ad eccezione di questo, tutti i costruttori dei tipi proiettati, incluso quello predefinito, comportano la creazione di un oggetto Windows Runtime di supporto e forniscono un puntatore intelligente a tale oggetto. Di conseguenza, tale regola si applica a tutti i casi in cui viene usato il costruttore predefinito, ad esempio le variabili locali, globali e membro non inizializzate.

Se però vuoi creare una variabile di un tipo proiettato senza che venga creato un oggetto Windows Runtime di supporto, in modo da poter rinviare tale operazione a un momento successivo, puoi farlo. Dichiara la variabile o il campo usando il costruttore std::nullptr_t di C++/WinRT speciale, che la proiezione di C++/WinRT inserisce in ogni classe di runtime. Questo costruttore speciale viene usato con m_gamerPicBuffer nell'esempio di codice seguente.

#include <winrt/Windows.Storage.Streams.h>
using namespace winrt::Windows::Storage::Streams;

#define MAX_IMAGE_SIZE 1024

struct Sample
{
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer m_gamerPicBuffer{ nullptr };
};

int main()
{
    winrt::init_apartment();
    Sample s;
    // ...
    s.DelayedInit();
}

Tutti i costruttori sul tipo proiettato, tranne il costruttore std::nullptr_t, determinano la creazione di un oggetto Windows Runtime di supporto. Il costruttore std::nullptr_t è essenzialmente una fase senza operazioni e si aspetta che l'oggetto proiettato venga inizializzato in un momento successivo. Pertanto, indipendentemente da se una classe di runtime dispone o meno di un costruttore predefinito, puoi usare questa tecnica per un'inizializzazione ritardata efficiente.

Questa considerazione influisce sulle altre posizioni in cui richiami il costruttore predefinito, ad esempio nei vettori e nell mappe. Considera l'esempio di codice per cui necessiti di un progetto App vuota (C++/WinRT).

std::map<int, TextBlock> lookup;
lookup[2] = value;

L'assegnazione crea un nuovo TextBlock e lo sovrascrive immediatamente con value. Ecco il risultato.

std::map<int, TextBlock> lookup;
lookup.insert_or_assign(2, value);

Vedi anche Effetti del costruttore predefinito sulle raccolte.

Non impostare l'inizializzazione ritardata per errore

Presta attenzione a non richiamare il costruttore std::nullptr_t per errore. La risoluzione dei conflitti del compilatore lo preferisce a discapito dei costruttori di factory. Ad esempio, prendi in considerazione queste due definizioni di classe di runtime.

// GiftBox.idl
runtimeclass GiftBox
{
    GiftBox();
}

// Gift.idl
runtimeclass Gift
{
    Gift(GiftBox giftBox); // You can create a gift inside a box.
}

Supponi di voler creare un oggetto Gift che non si trova all'interno di una scatola, ovvero un oggetto Gift creato con un oggetto GiftBox non inizializzato. Prima di tutto, esaminiamo il modo non corretto di eseguire questa operazione. Sappiamo che esiste un costruttore Gift che accetta un oggetto GiftBox. Anche se è possibile passare un oggetto GiftBox Null (richiamando il costruttore Gift tramite inizializzazione uniforme, come verrà illustrato più avanti) non si otterrebbe il risultato desiderato.

// These are *not* what you intended. Doing it in one of these two ways
// actually *doesn't* create the intended backing Windows Runtime Gift object;
// only an empty smart pointer.

Gift gift{ nullptr };
auto gift{ Gift(nullptr) };

In questo modo, otteniamo un oggetto Gift non inizializzato, ma non un oggetto Gift con GiftBox non inizializzato. Ecco invece il modo corretto per eseguire questa operazione.

// Doing it in one of these two ways creates an initialized
// Gift with an uninitialized GiftBox.

Gift gift{ GiftBox{ nullptr } };
auto gift{ Gift(GiftBox{ nullptr }) };

Nell'esempio non corretto, il passaggio di un valore letterale nullptr si risolve a favore del costruttore con inizializzazione ritardata. Per favorire il costruttore di factory, il tipo del parametro deve essere GiftBox. Hai comunque sempre la possibilità di passare un oggetto GiftBox con esplicita inizializzazione ritardata, come illustrato nell'esempio corretto.

Anche l'esempio seguente è corretto perché il parametro è di tipo GiftBox e non std::nullptr_t.

GiftBox giftBox{ nullptr };
Gift gift{ giftBox }; // Calls factory constructor.

Il problema di ambiguità si presenta solo quando passi un valore letterale nullptr.

Non usare un costruttore di copia per errore

Questo avvertimento è simile a quello descritto nella precedente sezione Non impostare l'inizializzazione ritardata per errore.

Oltre al costruttore con inizializzazione ritardata, la proiezione C++/WinRT inserisce un costruttore di copia in ogni classe di runtime. È un costruttore con parametro singolo che accetta lo stesso tipo dell'oggetto in fase di costruzione. Il puntatore intelligente risultante punta allo stesso oggetto Windows Runtime di supporto a cui punta il parametro del costruttore. Come risultato abbiamo quindi due oggetti puntatore intelligente che puntano allo stesso oggetto di supporto.

Ecco una definizione di classe di runtime che useremo negli esempi di codice.

// GiftBox.idl
runtimeclass GiftBox
{
    GiftBox(GiftBox biggerBox); // You can place a box inside a bigger box.
}

Supponiamo di voler creare un oggetto GiftBox all'interno di un GiftBox più grande.

GiftBox bigBox{ ... };

// These are *not* what you intended. Doing it in one of these two ways
// copies bigBox's backing-object-pointer into smallBox.
// The result is that smallBox == bigBox.

GiftBox smallBox{ bigBox };
auto smallBox{ GiftBox(bigBox) };

Il modo corretto per eseguire questa operazione è quello di chiamare in modo esplicito la factory di attivazione.

GiftBox bigBox{ ... };

// These two ways call the activation factory explicitly.

GiftBox smallBox{
    winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
auto smallBox{
    winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };

Se l'API è implementata in un componente Windows Runtime

Questa sezione si applica se il componente è stato creato da te o proviene da un fornitore.

Nota

Per informazioni sull'installazione e sull'uso dell'Estensione C++/WinRT per Visual Studio (VSIX) e del pacchetto NuGet, che insieme forniscono il modello di progetto e il supporto della compilazione, vedi Supporto di Visual Studio per C++/WinRT.

Nel progetto di applicazione fai riferimento al file di metadati Windows Runtime (.winmd) del componente Windows Runtime ed esegui la compilazione. Durante la compilazione, lo strumento cppwinrt.exe genera una libreria C++ standard che descrive completamente o proietta la superficie dell'API per il componente. In altre parole, la libreria generata contiene i tipi proiettati per il componente.

Quindi, come per un tipo di spazio dei nomi di Windows, includi un'intestazione e costruisci il tipo proiettato tramite uno dei relativi costruttori. Il codice di avvio del progetto di applicazione registra la classe di runtime e il costruttore del tipo proiettato chiama RoActivateInstance per attivare la classe di runtime dal componente a cui viene fatto riferimento.

#include <winrt/ThermometerWRC.h>

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer thermometer;
    ...
};

Per altre informazioni, codice e una procedura dettagliata sull'utilizzo di API implementate in un componente Windows Runtime, vedere Componenti Windows Runtime con C++/WinRT e Creare eventi in CC++/WinRT.

Se l'API è implementata nel progetto di uso

L'esempio di codice in questa sezione è tratto dall'argomento Controlli XAML, binding a una proprietà C++/WinRT. Per altre informazioni, esempi di codice e una procedura dettagliata per l'uso di una classe di runtime implementata nello stesso progetto d'uso, vedere questo argomento.

Un tipo usato dall'interfaccia utente XAML deve essere una classe di runtime, anche se è nello stesso progetto del codice XAML. Per questo scenario, genera un tipo proiettato dai metadati Windows Runtime (.winmd) della classe di runtime. Anche in questo caso, è necessario includere un'intestazione, ma è possibile scegliere tra la versione 1.0 o 2.0 di C++/WinRT per costruire l'istanza della classe di runtime. Il metodo della versione 1.0 usa winrt::make. Il metodo della versione 2.0 è noto come costruzione uniforme. Verranno esaminati entrambi.

Costruzione tramite winrt::make

Viene descritto innanzitutto il metodo predefinito (C++/WinRT versione 1.0), perché è consigliabile avere familiarità almeno con questo modello. Viene creato il tipo proiettato tramite il relativo costruttore std::nullptr_t. Questo costruttore non esegue alcuna inizializzazione, devi pertanto assegnare un valore all'istanza tramite la funzione helper winrt::make, passando gli argomenti del costruttore necessari. Una classe di runtime implementata nello stesso progetto del codice che la usa non deve necessariamente essere registrata, né deve esserne creata un'istanza tramite l'attivazione di Windows Runtime/COM.

Per una procedura dettagliata completa, vedere Controlli XAML, binding a una proprietà C++/WinRT. Questa sezione descrive gli estratti della procedura dettagliata.

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        BookstoreViewModel MainViewModel{ get; };
    }
}

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
    ...
    private:
        Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
...

// MainPage.cpp
...
#include "BookstoreViewModel.h"

MainPage::MainPage()
{
    m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
    ...
}

Costruzione uniforme

Con C++/WinRT versione 2.0 e successive, è disponibile un modulo di costruzione ottimizzato, noto come costruzione uniforme. Vedere Funzionalità nuove e aggiornate di C++/WinRT 2.0.

Per una procedura dettagliata completa, vedere Controlli XAML, binding a una proprietà C++/WinRT. Questa sezione descrive gli estratti della procedura dettagliata.

Per usare la costruzione uniforme invece di winrt::make, sarà necessaria una factory di attivazione. Un metodo efficace per generarne una consiste nell'aggiungere un costruttore all'IDL.

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

Quindi in MainPage.h dichiarare e inizializzare m_mainViewModel in un solo passaggio, come illustrato di seguito.

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
    ...
    private:
        Bookstore::BookstoreViewModel m_mainViewModel;
        ...
    };
}
...

Nel costruttore MainPage in MainPage.cpp non è quindi necessario il codice m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();.

Per altre informazioni sulla costruzione uniforme ed esempi di codice, vedere Acconsentire esplicitamente alla costruzione uniforme e all'accesso diretto all'implementazione.

Creazione di istanze e restituzione di interfacce e tipi proiettati

Ecco un esempio del possibile aspetto di interfacce e tipi proiettati nel tuo progetto di uso. Tieni presente che un tipo proiettato (come quello analizzato in questo esempio), viene generato da strumenti e non creato manualmente.

struct MyRuntimeClass : MyProject::IMyRuntimeClass, impl::require<MyRuntimeClass,
    Windows::Foundation::IStringable, Windows::Foundation::IClosable>

MyRuntimeClass è un tipo proiettato. Le interfacce proiettate includono IMyRuntimeClass, IStringable e IClosable . In questo argomento sono stati descritti i diversi modi per creare un'istanza di un tipo proiettato. Di seguito sono riportati un promemoria e un riepilogo, utilizzando MyRuntimeClass come esempio.

// The runtime class is implemented in another compilation unit (it's either a Windows API,
// or it's implemented in a second- or third-party component).
MyProject::MyRuntimeClass myrc1;

// The runtime class is implemented in the same compilation unit.
MyProject::MyRuntimeClass myrc2{ nullptr };
myrc2 = winrt::make<MyProject::implementation::MyRuntimeClass>();
  • Puoi accedere ai membri di tutte le interfacce di un tipo proiettato.
  • Puoi restituire un tipo proiettato a un chiamante.
  • Le interfacce e i tipi proiettati derivano da winrt::Windows::Foundation::IUnknown. Puoi pertanto chiamare IUnknown::as su un tipo proiettato o un'interfaccia proiettata per eseguire una query su altre interfacce proiettate, che potrai usare o restituire al chiamante. La funzione membro as agisce in modo analogo a QueryInterface.
void f(MyProject::MyRuntimeClass const& myrc)
{
    myrc.ToString();
    myrc.Close();
    IClosable iclosable = myrc.as<IClosable>();
    iclosable.Close();
}

Factory di attivazione

Di seguito viene riportato il conveniente metodo diretto per creare un oggetto C++/WinRT.

using namespace winrt::Windows::Globalization::NumberFormatting;
...
CurrencyFormatter currency{ L"USD" };

In alcuni casi, potresti tuttavia voler creare manualmente la factory di attivazione e usarla per creare oggetti in base alle tue esigenze. Ecco alcuni esempi che mostrano come eseguire tale operazione, usando il modello di funzione winrt::get_activation_factory.

using namespace winrt::Windows::Globalization::NumberFormatting;
...
auto factory = winrt::get_activation_factory<CurrencyFormatter, ICurrencyFormatterFactory>();
CurrencyFormatter currency = factory.CreateCurrencyFormatterCode(L"USD");
using namespace winrt::Windows::Foundation;
...
auto factory = winrt::get_activation_factory<Uri, IUriRuntimeClassFactory>();
Uri uri = factory.CreateUri(L"http://www.contoso.com");

Le classi nei due esempi precedenti sono tipi di uno spazio dei nomi Windows. Nell'esempio seguente ThermometerWRC::Thermometer è un tipo personalizzato implementato in un componente Windows Runtime.

auto factory = winrt::get_activation_factory<ThermometerWRC::Thermometer>();
ThermometerWRC::Thermometer thermometer = factory.ActivateInstance<ThermometerWRC::Thermometer>();

Ambiguità tra membri e tipi

Quando una funzione membro ha lo stesso nome di un tipo, è presente un'ambiguità. Le regole per la ricerca di nomi C++ non qualificati nelle funzioni membro danno priorità alla ricerca nella classe rispetto a quella negli spazi dei nomi. La regola in base alla quale la mancata sostituzione non è un errore (SFINAE) non è applicabile (si applica durante la risoluzione dell'overload dei modelli di funzione). Se quindi il nome all'interno della classe non ha senso, il compilatore non continua a cercare una corrispondenza migliore, ma si limita a segnalare un errore.

struct MyPage : Page
{
    void DoWork()
    {
        // This doesn't compile. You get the error
        // "'winrt::Windows::Foundation::IUnknown::as':
        // no matching overloaded function found".
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Style>() };
    }
}

Nell'esempio precedente il compilatore pensa che stai passando FrameworkElement.Style() (una funzione membro in C++/WinRT) come parametro di modello a IUnknown::as. La soluzione consiste nel forzare l'interpretazione di Style come tipo Windows::UI::Xaml::Style.

struct MyPage : Page
{
    void DoWork()
    {
        // One option is to fully-qualify it.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Windows::UI::Xaml::Style>() };

        // Another is to force it to be interpreted as a struct name.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<struct Style>() };

        // If you have "using namespace Windows::UI;", then this is sufficient.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Xaml::Style>() };

        // Or you can force it to be resolved in the global namespace (into which
        // you imported the Windows::UI::Xaml namespace when you did
        // "using namespace Windows::UI::Xaml;".
        auto style = Application::Current().Resources().
            Lookup(L"MyStyle").as<::Style>();
    }
}

La ricerca di nomi non qualificati presenta un'eccezione speciale quando il nome è seguito da ::, nel qual caso ignora funzioni, variabili e valori enum. Ciò consente di eseguire operazioni come questa.

struct MyPage : Page
{
    void DoSomething()
    {
        Visibility(Visibility::Collapsed); // No ambiguity here (special exception).
    }
}

La chiamata a Visibility() viene risolta nel nome della funzione membro UIElement.Visibility. Il parametro Visibility::Collapsed segue tuttavia la parola Visibility con :: e quindi il nome del metodo viene ignorato e il compilatore trova la classe enum.

API importanti