Partager via


Utiliser des API avec C++/WinRT

Cette rubrique montre comment utiliser des API C++/WinRT, qu’elles fassent partie de Windows ou qu’elles soient implémentées par un fournisseur de composants tiers ou par vous-même.

Important

Pour que les exemples de code de cette rubrique soient courts et faciles à tester, vous pouvez les reproduire en créant un projet Windows Console Application (C++/WinRT) et en copiant-collant le code. Toutefois, vous ne pouvez pas consommer des types Windows Runtime (tiers) personnalisés arbitraires à partir d’une application non empaquetée comme celle-ci. Vous pouvez consommer uniquement des types Windows comme cela.

Pour consommer des types Windows Runtime (tiers) personnalisés à partir d’une application console, vous devez attribuer une identité de package à l’application afin qu’elle puisse résoudre l’inscription des types personnalisés consommés. Pour plus d’informations, consultez Projet d’empaquetage des applications Windows.

Vous pouvez également créer un projet à partir des modèles de projet Application vide (C++/WinRT) , Application principale (C++/WinRT) ou Composant Windows Runtime (C++/WinRT) . Ces types d’applications ont déjà une identité de package.

Si l’API se trouve dans un espace de noms Windows

Il s'agit du scénario le plus courant dans lequel vous utiliserez une API Windows Runtime. Pour chaque type dans un espace de noms Windows défini dans les métadonnées, C++/WinRT définit un équivalent compatible C++ (appelé le type projeté). Le type projeté a le même nom complet que le type Windows, mais il est placé dans l'espace de noms C++ winrt à l’aide de la syntaxe C++. Par exemple, Windows::Foundation::Uri est projeté en C++/WinRT sous la forme winrt::Windows::Foundation::Uri.

Voici un exemple de code simple. Si vous souhaitez copier-coller les exemples de code suivants directement dans le fichier de code source principal d’un projet Application console Windows (C++/WinRT) , définissez d’abord Sans utiliser les en-têtes précompilés dans les propriétés du projet.

// 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’en-tête winrt/Windows.Foundation.h inclus fait partie du SDK, présent dans le dossier %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt\. Les en-têtes dans ce dossier contiennent des types d'espace de noms Windows projetés en C++/WinRT. Dans cet exemple, winrt/Windows.Foundation.h contient winrt::Windows::Foundation::Uri qui est le type projeté de la classe runtime Windows::Foundation::Uri.

Conseil

Chaque fois que vous souhaitez utiliser un type à partir d’un espace de noms Windows, incluez l’en-tête C++/WinRT correspondant à cet espace de noms. Les directives using namespace sont facultatives, mais bien pratiques.

Dans l’exemple de code ci-dessus, après l’initialisation de C++/WinRT, nous empilons-allouons une valeur du type projeté winrt::Windows::Foundation::Uri via l’un de ses constructeurs publiquement documentés (Uri(String), dans cet exemple). Pour celui-ci, le cas le plus courant, c’est généralement tout que vous avez à faire. Une fois que vous avez une valeur du type projeté C++/WinRT, vous pouvez la traiter comme s’il s’agissait d’une instance du type Windows Runtime réel, car il a les mêmes membres.

En fait, cette valeur projetée est un proxy ; il s'agit essentiellement d'un pointeur intelligent vers un objet de sauvegarde. Le ou les constructeurs de la valeur projetée appellent RoActivateInstance pour créer une instance de la classe Windows Runtime de sauvegarde (Windows.Foundation.Uri dans le cas présent) et stocker l'interface par défaut de cet objet à l’intérieur de la nouvelle valeur projetée. Comme illustré ci-dessous, vos appels aux membres de la valeur projetée sont en réalité délégués, par le biais du pointeur intelligent, à l’objet de sauvegarde ; dans lequel les changements d’état se produisent.

Le type projeté Windows::Foundation::Uri

Lorsque la valeur contosoUri se trouve hors de portée, elle se détruit et publie sa référence à l’interface par défaut. Si cette référence est la dernière référence à l'objet Windows Runtime Windows.Foundation.Uri de sauvegarde, l'objet de sauvegarde se détruit également.

Conseil

Un type projeté est un wrapper sur une classe de Windows Runtime, à des fins d’utilisation de ses API. Par exemple, une interface projetée est un wrapper sur une interface Windows Runtime.

En-têtes de projection C++/WinRT

Pour utiliser les API d'espace de noms Windows de C++/WinRT, vous incluez les en-têtes du dossier %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt. Veillez à inclure les en-têtes correspondant à chaque espace de noms que vous utilisez.

Par exemple, pour l'espace de noms Windows::Security::Cryptography::Certificates, les définitions de type C++/WinRT équivalentes se trouvent dans winrt/Windows.Security.Cryptography.Certificates.h. L'inclusion de cet en-tête vous donne accès à tous les types de l'espace noms Windows::Security::Cryptography::Certificates.

Il arrive qu'un en-tête d'espace noms comprenne des parties d'en-têtes d'espaces noms apparentés, mais vous ne devez pas vous fier à ce détail de mise en œuvre. Incluez explicitement les en-têtes des espaces de noms que vous utilisez.

Par exemple, la méthode Certificate::GetCertificateBlob renvoie une interface Windows::Storage::Streams::IBuffer. Avant d'appeler la méthode Certificate::GetCertificateBlob, vous devez inclure le winrt/Windows.Storage.Streams.hfichier d'en-tête de l'espace de noms pour vous assurer que vous pouvez recevoir et utiliser le fichier Windows::Storage::Streams::IBuffer renvoyé.

Oublier d'inclure les en-têtes d'espace de noms requis avant d'utiliser des types dans cet espace de noms est une source fréquente d'erreurs de compilation.

Accès aux membres via l’objet, via une interface ou via l’ABI

Avec la projection C++/WinRT, la représentation d’exécution d’une classe Windows Runtime se limite aux interfaces ABI sous-jacentes. Toutefois, pour votre commodité, vous pouvez coder en fonction des classes comme prévu par leur auteur. Par exemple, vous pouvez appeler la méthode ToString d’un Uri comme s’il s’agissait d’une méthode de la classe (il s'agit en réalité d'une méthode sur l'interface IStringable séparée).

WINRT_ASSERT est une définition de macro qui s’étend à _ASSERTE.

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

Cette commodité est obtenue via une requête pour l’interface appropriée. Mais vous gardez toujours le contrôle. Vous pouvez choisir d'abandonner un peu de cet aspect pratique pour y gagner un peu en performances en récupérant l’interface IStringable vous-même et en l'utilisant directement. Dans l’exemple de code ci-dessous, vous obtenez un pointeur d’interface IStringable réel en cours d’exécution (via une requête à usage unique). Après cela, votre appel à ToString est direct et permet d’éviter tout autre appel à QueryInterface.

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

Vous pouvez choisir cette technique si vous savez que vous allez appeler plusieurs méthodes sur la même interface.

Par ailleurs, si vous souhaitez accéder aux membres du niveau ABI, vous le pouvez. L’exemple de code ci-dessous montre comment procéder et vous trouverez plus de détails et d’exemples de code dans Interopérabilité entre C++/WinRT et 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.
}

Initialisation différée

Dans C++/WinRT, chaque type projeté possède un constructeur std::nullptr_t C++/WinRT spécial. À l’exception de ce dernier, tous les constructeurs de type projeté, y compris le constructeur par défaut, entraînent la création d’un objet Windows Runtime de sauvegarde et vous donnent un pointeur intelligent vers celui-ci. Par conséquent, cette règle s’applique à tous les emplacements où le constructeur par défaut est utilisé. Cela inclut notamment les variables locales non initialisées, les variables globales non initialisées et les variables de membre non initialisées.

En revanche, si vous souhaitez créer une variable d’un type projeté sans qu’il construise à son tour un objet Windows Runtime de sauvegarde (de sorte que vous pouvez reporter cette tâche à plus tard), vous le pouvez. Déclarez votre variable ou votre champ à l’aide du constructeur std::nullptr_t C++/WinRT spécial (que la projection C++/WinRT injecte dans chaque classe runtime). Nous utilisons ce constructeur spécial avec m_gamerPicBuffer dans l’exemple de code ci-dessous.

#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();
}

Tous les constructeurs du type projeté, sauf le constructeur std::nullptr_t, entraînent la création d’un objet Windows Runtime de stockage. Le constructeur std::nullptr_t est essentiellement un no-op. Il attend que l’objet projeté soit initialisé à un moment ultérieur. Par conséquent, qu'une classe runtime ait un constructeur par défaut ou non, vous pouvez utiliser cette technique pour obtenir une initialisation différée efficace.

Cette considération affecte d'autres endroits où vous invoquez le constructeur par défaut, comme dans les vecteurs et les cartes. Prenons cet exemple de code, pour lequel vous aurez besoin d'un projet Blank App (C++/WinRT) .

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

L’assignation crée un nouveau TextBlock et le remplace aussitôt par value. Voici la solution.

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

Consultez également Comment le constructeur par défaut affecte-t-il les collections ?.

Ne pas retarder l’initialisation par erreur

Veillez à ne pas appeler le constructeur std::nullptr_t par erreur. La fonctionnalité de résolution de conflits du compilateur le rend prioritaire face aux constructeurs de fabrique. Par exemple, prenons deux définitions de classe runtime.

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

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

Nous souhaitons créer un Gift qui ne se trouve pas dans une boîte (un Gift construit avec un GiftBox non initialisé). Commençons par examiner la mauvaise façon de procéder. Nous savons qu’il existe un constructeur Gift qui accepte un GiftBox. Mais si nous sommes tentés de passer un GiftBox ayant une valeur null (en appelant le constructeur Gift à l’aide d’une initialisation uniforme, comme nous le faisons ci-dessous), nous n’obtiendrons pas le résultat souhaité.

// 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) };

Vous obtenez ici un Gift non initialisé. Vous n’obtenez pas un Gift avec un GiftBox non initialisé. Voici la bonne façon de procéder.

// 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 }) };

Dans l’exemple incorrect, le passage d’un littéral nullptr est résolu en faveur du constructeur à initialisation tardive. Pour permettre une résolution en faveur du constructeur de fabrique, le type du paramètre doit être celui d’un GiftBox. Vous pouvez toujours passer un GiftBox à initialisation tardive de manière explicite, comme indiqué dans le bon exemple.

L’exemple suivant est également correct, car le paramètre a le type GiftBox et non std::nullptr_t.

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

L’ambiguïté se pose uniquement quand vous passez un littéral nullptr.

N’utilisez pas de constructeur de copie par erreur.

Cette mise en garde est similaire à celle décrite dans la section Ne pas retarder l’initialisation par erreur ci-dessus.

En plus du constructeur à initialisation tardive, la projection C++/WinRT injecte également un constructeur de copie dans chaque classe runtime. Il s’agit d’un constructeur à un seul paramètre qui accepte le même type que l’objet construit. Le pointeur intelligent résultant pointe vers le même objet Windows Runtime de stockage que celui vers lequel pointe son paramètre de constructeur. Il en résulte deux objets pointeurs intelligents qui pointent vers le même objet de stockage.

Voici une définition de classe runtime que nous allons utiliser dans les exemples de code.

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

Supposons que nous souhaitions construire un GiftBox dans un GiftBox plus grand.

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) };

La bonne façon de procéder consiste à appeler explicitement la fabrique d’activation.

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) };

Si l’API est implémentée dans un composant Windows Runtime

Cette section s’applique, que vous ayez créé le composant vous-même ou qu’il provienne d’un fournisseur.

Notes

Pour plus d’informations sur l’installation et l’utilisation de l’extension VSIX (Visual Studio Extension) C++/WinRT et du package NuGet (qui fournissent ensemble la prise en charge des modèles et des builds de projet), consultez Prise en charge de Visual Studio pour C++/WinRT.

Dans votre projet d’application, référencez le fichier de métadonnées Windows Runtime (.winmd) du composant Windows Runtime et lancez le processus de génération. Lors de la build, l’outil cppwinrt.exe génère une bibliothèque C++ standard qui décrit complètement, ou projette, la surface de l’API pour le composant. En d’autres termes, la bibliothèque générée contient les types projetés pour le composant.

Ensuite, comme pour un type d’espace de noms Windows, incluez un en-tête et construisez le type projeté via l’un de ses constructeurs. Le code de démarrage de votre projet d’application inscrit la classe runtime, et le constructeur du type projeté appelle RoActivateInstance pour activer la classe runtime à partir du composant référencé.

#include <winrt/ThermometerWRC.h>

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

Pour obtenir plus d’informations, du code ainsi que la procédure de consommation d’API implémentées dans un composant Windows Runtime, consultez Composants Windows Runtime avec C++/WinRT et Créer des événements en C++/WinRT.

Si l’API est implémentée dans le projet d’utilisation

L’exemple de code présenté dans cette section est extrait de la rubrique Contrôles XAML - Liaison à une propriété C++/WinRT. Consultez cette rubrique pour obtenir plus d’informations, du code et la procédure de consommation d’une classe runtime implémentée dans le même projet que celui qui la consomme.

Un type qui est utilisé à partir de l’interface utilisateur XAML doit être une classe runtime, même s’il est dans le même projet que le code XAML. Pour ce scénario, vous allez générer un type projeté à partir des métadonnées Windows Runtime de la classe runtime (.winmd). Là encore, vous allez inclure un en-tête, mais cette fois, vous avez le choix entre les façons de construire l’instance de la classe runtime de C++/WinRT version 1.0 ou 2.0. La méthode de la version 1.0 utilise winrt::make ; la méthode de la version 2.0 est appelée construction uniforme. Examinons chacune de ces méthodes.

Construction à l’aide de winrt::make

Commençons par la méthode par défaut (C++/WinRT version 1.0), car il est recommandé d’être au moins familiarisé avec ce modèle. Vous construisez le type projeté par le biais de son constructeur std::nullptr_t. Ce constructeur n’effectuant aucune initialisation, vous devez ensuite affecter une valeur à l’instance via la fonction d’assistance winrt::make, en transmettant tous les arguments constructeur nécessaires. Une classe runtime implémentée dans le même projet que le code d’utilisation n’a pas besoin d’être inscrite, ni instanciée via l’activation de Windows Runtime/COM.

Pour connaître la procédure pas-à-pas complète, consultez Contrôles XAML - Liaison à une propriété C++/WinRT. Cette section présente des extraits de cette procédure pas à pas.

// 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>();
    ...
}

Construction uniforme

Avec C++ /WinRT version 2.0 et les versions ultérieures, vous disposez d’une forme optimisée de construction, connue sous le nom de construction uniforme (consultez Nouveautés et changements dans C++/WinRT 2.0.).

Pour connaître la procédure pas-à-pas complète, consultez Contrôles XAML - Liaison à une propriété C++/WinRT. Cette section présente des extraits de cette procédure pas à pas.

Pour utiliser la construction uniforme au lieu de winrt::make, vous avez besoin d’une fabrique d’activation. Ajouter un constructeur à votre IDL constitue un bon moyen d’en générer une.

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

Ensuite, dans MainPage.h, déclarez et initialisez m_mainViewModel en une seule étape, comme indiqué ci-dessous.

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

Puis, dans le constructeur MainPage dans MainPage.cpp, le code m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>(); n’est pas nécessaire.

Pour obtenir plus d’informations sur la construction uniforme et des exemples de code, consultez Accepter la construction uniforme et l’accès direct à l’implémentation.

Instanciation et retour des types et interfaces projetés

Voici un exemple de ce à quoi peuvent ressembler les types et interfaces projetés dans votre projet d’utilisation. Rappelez-vous qu'un type projeté (tel que celui de cet exemple), est généré par un outil, et n'est pas un élément que vous auriez créé vous-même.

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

MyRuntimeClass est un type projeté ; les interfaces projetées incluent IMyRuntimeClass, IStringable et IClosable. Cette rubrique vous a montré les différentes façons dont vous pouvez instancier un type projeté. Voici un résumé et un rappel utilisant MyRuntimeClass comme exemple.

// 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>();
  • Vous pouvez accéder aux membres de toutes les interfaces d’un type projeté.
  • Vous pouvez retourner un type projeté à un appelant.
  • Les types et interfaces projetés dérivent de winrt::Windows::Foundation::IUnknown. Par conséquent, vous pouvez appeler IUnknown::as sur un type ou une interface projetés pour rechercher d’autres interfaces projetées, que vous pouvez également utiliser ou retourner à un appelant. La fonction membre as fonctionne comme QueryInterface.
void f(MyProject::MyRuntimeClass const& myrc)
{
    myrc.ToString();
    myrc.Close();
    IClosable iclosable = myrc.as<IClosable>();
    iclosable.Close();
}

Fabriques d'activation

Une manière pratique et directe de créer un objet C++/WinRT se présente comme suit.

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

Mais vous devez parfois créer la fabrique d'activation vous-même, puis créer des objets à partir de celle-ci à votre convenance. Voici quelques exemples de la manière de procéder, en utilisant le modèle de fonction 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");

Les classes dans les deux exemples ci-dessus sont des types provenant d’un espace de noms Windows. Dans l’exemple suivant, ThermometerWRC::Thermometer est un type personnalisé implémenté dans un composant Windows Runtime.

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

Ambiguïtés membre/type

Lorsqu’une fonction membre a le même nom qu’un type, il y a une ambiguïté. Pour C++, la recherche de noms non qualifiés dans les fonctions membres est effectuée dans la classe avant les espaces de noms en raison des règles appliquées. La règle Substitution failure is not an error (L’échec de substitution n’est pas une erreur, ou SFINAE) ne s’applique pas (elle s’applique lors de la résolution de surcharge des modèles de fonction). Par conséquent, si le nom à l’intérieur de la classe n’a pas de sens, le compilateur ne continue pas à rechercher une meilleure correspondance : il signale simplement une erreur.

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>() };
    }
}

Ci-dessus, le compilateur pense que vous transmettez FrameworkElement.Style() (qui est une fonction membre dans C++/WinRT) à IUnknown::as comme un paramètre de modèle. La solution consiste à forcer le nom Style à être interprété comme étant de type 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 recherche de noms non qualifiés possède une exception spéciale. Si le nom est suivi de ::, les fonctions, les variables et les valeurs enum sont ignorées. Cela vous permet d’effectuer des opérations similaires à ce qui suit.

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

L’appel de Visibility() correspond au nom de la fonction membre UIElement.Visibility. Toutefois, le paramètre Visibility::Collapsed suit le mot Visibility avec ::. Le nom de la méthode est alors ignoré et le compilateur recherche la classe enum.

API importantes