Utilizzare le API con C++/WinRT

Questo argomento illustra come usare le API C++/WinRT, sia che facciano parte di Windows, implementate da un fornitore di componenti di terze parti o implementate da soli.

Importante

Pertanto, gli esempi di codice in questo argomento sono 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 utilizzare tipi di Windows Runtime personalizzati arbitrari (di terze parti) da un'app non pacchettizzata in questo modo. In questo modo è possibile utilizzare solo i tipi di Windows.

Per utilizzare tipi di Windows Runtime personalizzati (di terze parti) da un'app console, è necessario assegnare all'app un'identità del pacchetto in modo che possa risolvere la registrazione dei tipi personalizzati utilizzati. Per altre info, vedi Windows Project di creazione di pacchetti di applicazioni.

In alternativa, crea un nuovo progetto usando i modelli di progetto App vuota, pacchettizzata (WinUI 3 in Desktop) per C++ o Componente di 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 in cui si userà un'API Windows Runtime. Per ogni tipo in uno spazio dei nomi Windows definito nei metadati, C++/WinRT definisce un equivalente C++-friendly (denominato tipo proiettato). Un tipo proiettato ha lo stesso nome completo del tipo Windows, ma viene inserito nello spazio dei nomi winrt C++ usando la sintassi C++. Ad esempio, Windows::Foundation::Uri viene proiettato in C++/WinRT come winrt::Windows::Foundation::Uri.

Ecco un semplice esempio di codice. Se vuoi copiare e incollare direttamente i seguenti esempi di codice nel file di codice sorgente principale di un progetto Applicazione console di Windows (C++/WinRT), imposta prima Non usare 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 winrt/Windows.Foundation.h inclusa fa parte dell'SDK, disponibile all'interno della cartella %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt\. I file di intestazione in quella cartella contengono tipi dello spazio 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 vuoi usare un tipo da uno spazio dei nomi Windows, includi l'intestazione C++/WinRT corrispondente a tale spazio dei nomi. Le using namespace direttive sono facoltative, ma utili.

Nell'esempio di codice precedente, dopo l'inizializzazione di C++/WinRT, si alloca un valore del tipo winrt::Windows::Foundation::Uri proiettato tramite uno dei relativi costruttori documentati pubblicamente (Uri(String), in questo esempio). In questo caso, il caso d'uso più comune, di solito è tutto ciò che devi fare. Una volta che hai un valore di tipo proiettato C++/WinRT, puoi considerarlo come se fosse un'istanza del tipo di Windows Runtime effettivo, poiché ha tutti gli stessi membri.

Infatti, tale valore proiettato è un proxy; si tratta essenzialmente di un puntatore intelligente a un oggetto di backup. I costruttori del valore proiettato chiamano RoActivateInstance per creare un'istanza della classe di backup Windows Runtime (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 vengono in realtà delegate, tramite lo smart pointer, ai membri dell'oggetto sottostante; ed è lì che avvengono le modifiche di stato.

Il tipo Windows::Foundation::Uri proiettato

Quando il valore contosoUri esce dall'ambito, viene distrutto e rilascia il riferimento all'interfaccia predefinita. Se tale riferimento è l'ultimo riferimento all'oggetto sottostante di Windows Runtime Windows.Foundation.Uri, anche l'oggetto sottostante viene distrutto.

Suggerimento

Un tipo proiettato è un wrapper su un tipo Windows Runtime ai fini dell'utilizzo delle API. Ad esempio, un'interfaccia proiettata è un wrapper su un'interfaccia Windows Runtime.

Intestazioni di proiezione C++/WinRT

Per utilizzare da C++/WinRT le API degli spazi dei nomi di Windows, includi i file di intestazione dalla 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 risiedono in winrt/Windows.Security.Cryptography.Certificates.h. L'inclusione di questa intestazione consente di accedere a tutti i tipi dello 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. Includi esplicitamente i file di intestazione per i namespace che utilizzi.

Ad esempio, il metodo Certificate::GetCertificateBlob restituisce un'interfaccia Windows::Storage::Streams::IBuffer. Prima di chiamare il metodo Certificate::GetCertificateBlob, è necessario includere il file di intestazione del namespace winrt/Windows.Storage.Streams.h per assicurarsi di poter ricevere e utilizzare il valore restituito Windows::Storage::Streams::IBuffer.

Dimenticare di includere gli header del namespace richiesti prima di usare i tipi in quel namespace è una causa comune di errori di compilazione.

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

Con la proiezione C++/WinRT, la rappresentazione in fase di esecuzione di una classe di Windows Runtime non è altro che le interfacce ABI sottostanti. Tuttavia, per praticità, è possibile codificare le classi nel modo previsto dall'autore. Ad esempio, è possibile chiamare il metodo ToString di un Uri come se fosse un metodo della classe (in realtà, sotto le quinte, si tratta di un metodo nell'interfaccia IStringable separata).

WINRT_ASSERT è una definizione di macro e si espande fino a _ASSERTE.

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

Questa comodità si ottiene mediante un'interrogazione dell'interfaccia appropriata. Ma sei sempre in controllo. È possibile scegliere di rinunciare a un po' di praticità in cambio di un piccolo miglioramento delle prestazioni, ottenendo direttamente l'interfaccia IStringable e usandola direttamente. Nell'esempio di codice seguente si ottiene un puntatore effettivo all'interfaccia IStringable in fase di esecuzione (tramite un'unica query). Successivamente, la chiamata a ToString è diretta ed evita qualsiasi ulteriore chiamata a QueryInterface.

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

È possibile scegliere questa tecnica se si sa che verranno chiamati diversi metodi sulla stessa interfaccia.

Per inciso, se si desidera accedere ai membri a livello di ABI, è possibile. L'esempio di codice seguente mostra come e sono disponibili altri dettagli ed esempi di codice 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 speciale C++/WinRT std::nullptr_t . Ad eccezione di quel costruttore, tutti i costruttori dei tipi proiettati, incluso il costruttore predefinito, comportano la creazione di un oggetto Windows Runtime sottostante e forniscono un puntatore intelligente a tale oggetto. Tale regola viene quindi applicata ovunque venga usato il costruttore predefinito, ad esempio variabili locali non inizializzate, variabili globali non inizializzate e variabili membro non inizializzate.

Se, d'altra parte, si vuole costruire una variabile di un tipo proiettato senza di essa a sua volta costruire un oggetto di supporto Windows Runtime (in modo che sia possibile ritardare il lavoro fino a un secondo momento), è possibile farlo. Dichiarare la variabile o il campo usando il costruttore speciale C++/WinRT std::nullptr_t (che la proiezione 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 del tipo proiettato, ad eccezione del costruttore std::nullptr_t, determinano la creazione di un oggetto Windows Runtime di supporto. Il costruttore std::nullptr_t è essenzialmente un no-op. Prevede che l'oggetto proiettato venga inizializzato in un secondo momento. Pertanto, se una classe di runtime ha o meno un costruttore predefinito, è possibile usare questa tecnica per un'inizializzazione ritardata efficiente.

Questa considerazione influisce su altre posizioni in cui si richiama il costruttore predefinito, ad esempio in vettori e mappe. Si consideri questo esempio di codice, per il quale è necessaria un'app vuota, in pacchetto (WinUI 3 in Desktop) per il progetto C++.

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

L'assegnazione crea un nuovo textBlock e quindi lo sovrascrive immediatamente con value. Ecco il rimedio.

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

Vedere anche Come il costruttore predefinito influisce sulle raccolte.

Non ritardare l'inizializzazione per errore

Prestare attenzione a non richiamare il costruttore std::nullptr_t per errore. La risoluzione dei conflitti del compilatore lo favorisce rispetto ai costruttori di factory. Si consideri, ad esempio, 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.
}

Diciamo che vogliamo costruire un Regalo che non si trova all'interno di una scatola (un regalo costruito con un GiftBox non inizializzato). Prima di tutto, esaminiamo il modo sbagliato per farlo. Sappiamo che c'è un costruttore Gift che accetta un GiftBox. Ma se siamo tentati di passare un GiftBox null (richiamando il costruttore Gift tramite l'inizializzazione uniforme, come facciamo di seguito), allora non otterremo 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) };

Quello che ottieni qui è un regalo non inizializzato. Non si ottiene un regalo con un GiftBox non inizializzato. Ecco il modo corretto per farlo.

// 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 errato, il passaggio del valore letterale nullptr fa sì che venga selezionato il costruttore con inizializzazione ritardata. Per risolvere il problema a favore del costruttore factory, il tipo del parametro deve essere GiftBox. Hai ancora la possibilità di passare esplicitamente un GiftBox con inizializzazione ritardata, come mostrato nell'esempio corretto.

Questo esempio successivo è corretto anche perché il parametro ha il tipo GiftBox e non std::nullptr_t.

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

È solo quando si passa un valore letterale nullptr che si verifica l'ambiguità.

Non copiare il costrutto per errore.

Questa cautela è simile a quella descritta nella sezione Non ritardare l'inizializzazione per errore precedente.

Oltre al costruttore di inizializzazione ritardata, la proiezione C++/WinRT inserisce anche un costruttore di copia in ogni classe di runtime. Si tratta di un costruttore a parametro singolo che accetta lo stesso tipo dell'oggetto da costruire. Il puntatore intelligente risultante punta allo stesso oggetto di backup Windows Runtime a cui punta il parametro del costruttore. Il risultato sono due oggetti smart pointer che puntano allo stesso oggetto sottostante.

Ecco una definizione di classe di runtime che verrà usata negli esempi di codice.

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

Supponiamo di voler costruire una GiftBox all'interno di una 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 consiste nel chiamare la factory di attivazione in modo esplicito.

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 viene implementata in un componente Windows Runtime

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

Note

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

Nel progetto dell'applicazione, fare riferimento al file di metadati di Windows Runtime (.winmd) del componente Windows Runtime, quindi compilare. 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 nel caso di un tipo di namespace Windows, si include un header e si costruisce il tipo proiettato usando uno dei suoi costruttori. Il codice di avvio del progetto dell'applicazione registra la classe di runtime e il costruttore del tipo proiettato chiama RoActivateInstance per attivare la classe di runtime dal componente a cui si fa riferimento.

#include <winrt/ThermometerWRC.h>

struct App : AppT<App>
{
    ThermometerWRC::Thermometer thermometer;
    ...
};

Per ulteriori dettagli, il codice e una guida dettagliata sull'utilizzo delle API implementate in un componente di Windows Runtime, vedere Componenti di Windows Runtime con C++/WinRT e Crea eventi in C++/WinRT.

Se l'API viene implementata nel progetto di utilizzo

L'esempio di codice in questa sezione è tratto dall'argomento Controlli XAML: associazione a una proprietà C++/WinRT. Per altri dettagli, codice e procedura dettagliata sull'uso di una classe di runtime implementata nello stesso progetto che lo usa, vedere questo argomento.

Un tipo usato dall'interfaccia utente XAML deve essere una classe di runtime, anche se si trova nello stesso progetto del codice XAML. Per questo scenario, si genera un tipo proiettato dai metadati di Windows Runtime della classe di runtime (.winmd). Anche in questo caso, includi un'intestazione, ma hai una scelta tra le modalità C++/WinRT versione 1.0 o versione 2.0 per costruire l'istanza della classe di runtime. Il metodo versione 1.0 usa winrt::make; Il metodo versione 2.0 è noto come costruzione uniforme. Esaminiamoli uno per uno.

Creazione tramite winrt::make

Iniziamo con il metodo predefinito (C++/WinRT versione 1.0), perché è consigliabile avere almeno familiarità con questo modello. Il tipo proiettato viene creato tramite il relativo costruttore std::nullptr_t . Tale costruttore non esegue alcuna inizializzazione, quindi devi quindi assegnare un valore all'istanza tramite la funzione helper winrt::make , passando eventuali argomenti del costruttore necessari. Una classe di runtime implementata nello stesso progetto del codice che la utilizza non deve essere registrata né istanziata tramite attivazione Windows Runtime/COM.

Per una panoramica dettagliata completa, vedi Controlli XAML; associare a una proprietà C++/WinRT. Questa sezione illustra gli estratti da tale procedura dettagliata.

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Microsoft.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 una forma ottimizzata di costruzione nota come costruzione uniforme (vedi Notizie e modifiche in C++/WinRT 2.0).

Per una procedura dettagliata completa, vedi Controlli XAML; eseguire l'associazione a una proprietà C++/WinRT. Questa sezione mostra estratti di quella procedura guidata.

Per usare la costruzione uniforme anziché winrt::make, è necessaria una fabbrica di attivazione. Un buon modo per generarne uno consiste nell'aggiungere un costruttore al file IDL.

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Microsoft.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;
        ...
    };
}
...

Quindi, nel costruttore MainPage in MainPage.cppnon è necessario il codice m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();.

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

Istanziazione e restituzione di tipi e interfacce proiettate

Ecco un esempio di come i tipi e le interfacce proiettati potrebbero apparire nel progetto che li utilizza. Tenere presente che un tipo proiettato ,ad esempio quello in questo esempio, è generato dallo strumento e non è qualcosa che si potrebbe creare 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 illustrati i diversi modi in cui è possibile creare un'istanza di un tipo proiettato. Ecco un promemoria e un riepilogo, usando 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>();
  • È possibile accedere ai membri di tutte le interfacce di un tipo proiettato.
  • È possibile restituire un tipo proiettato a un chiamante.
  • I tipi e le interfacce proiettati derivano da winrt::Windows::Foundation::IUnknown. È quindi possibile chiamare IUnknown::as su un tipo o un'interfaccia proiettati per richiedere altre interfacce proiettate, che possono poi essere usate o restituite al chiamante. La funzione as member funziona come QueryInterface.
void f(MyProject::MyRuntimeClass const& myrc)
{
    myrc.ToString();
    myrc.Close();
    IClosable iclosable = myrc.as<IClosable>();
    iclosable.Close();
}

Factory di attivazione

Il modo pratico e diretto per creare un oggetto C++/WinRT è il seguente.

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

Ma ci possono essere casi in cui potresti voler creare tu stesso la factory di attivazione e poi creare oggetti a partire da essa quando preferisci. Ecco alcuni esempi che illustrano come usare 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. In questo esempio successivo, 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 membro e tipo

Quando una funzione membro ha lo stesso nome di un tipo, esiste un'ambiguità. Le regole per la ricerca di nomi non qualificati in C++ nelle funzioni membro fanno sì che la ricerca avvenga prima nella classe e poi nei namespace. L'errore di sostituzione non è una regola di errore (SFINAE) non si applica (si applica durante la risoluzione dell'overload dei modelli di funzione). Quindi, se il nome all'interno della classe non ha senso, il compilatore non cerca una corrispondenza migliore, ma segnala semplicemente 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>() };
    }
}

Qui sopra, il compilatore ritiene che si stia passando FrameworkElement.Style() (che, in C++/WinRT, è una funzione membro) come argomento del modello a IUnknown::as. La soluzione consiste nel forzare l'interpretazione del nome Style come tipo Microsoft::UI::Xaml::Style.

struct MyPage : Page
{
    void DoWork()
    {
        // One option is to fully-qualify it.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Microsoft::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 Microsoft::UI::Xaml namespace when you did
        // "using namespace Microsoft::UI::Xaml;".
        auto style = Application::Current().Resources().
            Lookup(L"MyStyle").as<::Style>();
    }
}

La ricerca di nomi non qualificati presenta un'eccezione speciale nel caso in cui il nome sia seguito da ::, nel qual caso ignora funzioni, variabili ed enumerazioni. In questo modo è possibile eseguire operazioni come questa.

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

La chiamata a Visibility() fa riferimento al nome del membro funzione UIElement.Visibility. Ma il parametro Visibility::Collapsed segue la parola Visibility con ::e quindi il nome del metodo viene ignorato e il compilatore trova la classe enum.

API importanti