Présentation de Microsoft Interface Definition Language 3.0

MIDL (Microsoft Interface Definition Language) 3.0 est une syntaxe moderne simplifiée pour définir Windows Runtime types à l’intérieur des fichiers.idl IDL (Interface Definition Language). Cette nouvelle syntaxe sera familière à toute personne expérimentée avec C, C++, C# et/ou Java. MIDL 3.0 est un moyen particulièrement pratique de définir des classes runtime C++/WinRT , étant considérablement plus concises que les versions précédentes d’IDL (réduisant les conceptions de deux tiers de longueur et en utilisant des valeurs par défaut raisonnables pour réduire la nécessité de décorer avec des attributs).

Voici comment MIDL 3.0 ressemble; Cet exemple illustre la plupart des éléments de syntaxe de langage que vous utiliserez probablement.

// Photo.idl
namespace PhotoEditor
{
    delegate void RecognitionHandler(Boolean arg); // delegate type, for an event.

    runtimeclass Photo : Windows.UI.Xaml.Data.INotifyPropertyChanged // interface.
    {
        Photo(); // constructors.
        Photo(Windows.Storage.StorageFile imageFile);

        String ImageName{ get; }; // read-only property.
        Single SepiaIntensity; // read-write property.

        Windows.Foundation.IAsyncAction StartRecognitionAsync(); // (asynchronous) method.

        event RecognitionHandler ImageRecognized; // event.
    }
}

Notez que la syntaxe de MIDL 3.0 est spécifiquement et uniquement conçue pour définir des types. Vous allez utiliser un autre langage de programmation pour implémenter ces types. Pour utiliser MIDL 3.0, vous avez besoin du SDK Windows version 10.0.17134.0 (Windows 10, version 1803) (midl.exeversion 8.01.0622 ou ultérieure, utilisée avec le /winrt commutateur).

Notes

Consultez également la référence consolidée Windows Runtime (le système de type Windows Runtime et les fichiers de métadonnées Windows).

MIDL 1.0, 2.0 et 3.0

IdL (Interface Definition Language) a commencé par le système DCE/RPC (Distributed Computing Environment/Remote Procedure Calls). MidL 1.0 d’origine est DCE/RPC IDL avec des améliorations pour la définition des interfaces COM et des coclasses.

Une syntaxe MIDL 2.0 mise à jour (également appelée MIDLRT) a été développée au sein de Microsoft pour déclarer Windows Runtime API pour la plateforme Windows. Si vous recherchez dans le dossier %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt du Kit de développement logiciel (SDK) Windows, vous verrez des exemples de .idl fichiers écrits avec la syntaxe MIDL 2.0. Il s’agit d’API intégrées Windows Runtime déclarées dans leur formulaire DBI (Application Binary Interface). Ces fichiers existent principalement pour les outils à utiliser: vous ne créerez ni ne consommerez ces API sous ce formulaire (sauf si vous écrivez du code de très bas niveau).

Consultez également Transition vers MIDL 3.0 à partir de MIDLRT classique.

MIDL 3.0 est une syntaxe beaucoup plus simple et plus moderne, dont l’objectif est de déclarer Windows Runtime API. Vous pouvez également l’utiliser dans vos projets, en particulier pour définir des classes runtime C++/WinRT . Les en-têtes, à utiliser à partir de C++/WinRT, pour les API de Windows Runtime intégrées font partie du SDK, à l’intérieur du dossier %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt.

Cas d’usage pour MIDL 3.0

En général, toutes les API Windows Runtime sont conçues pour être disponibles pour toutes les projections de langage Windows Runtime. Pour ce faire, en partie, en choisissant de transmettre exclusivement des types Windows Runtime aux API Windows Runtime et à partir de celle-ci. Bien qu’il s’agit d’une décision de conception valide pour passer une interface COM brute vers et depuis une API Windows Runtime, cela limite les consommateurs de cette API Windows Runtime particulière aux applications C++. La technique peut être vue dans les scénarios d’interopérabilité, par exemple lors de l’interopérabilité entre Direct3D et XAML. Étant donné que Direct3D est dans l’image, le scénario est nécessairement limité aux applications C++. Par conséquent, une API qui nécessite une interface COM n’impose aucune limitation supplémentaire au-dessus et au-dessus de ce qui est inhérent. Par exemple, une application C++ peut obtenir un pointeur d’interface IDXGISwapChain , puis la transmettre à la méthode ISwapChainPanelNative::SetSwapChain. Une application C#, par exemple, ne serait pas en mesure d’obtenir un IDXGISwapChain pour commencer, de sorte qu’elle ne serait pas en mesure d’utiliser cette méthode pour cette raison. Ces exceptions liées à l’interopérabilité résident dans des en-têtes d’interopérabilité, tels que windows.ui.xaml.media.dxinterop.h.

S’il existe des fonctionnalités ou des fonctionnalités d’un composant COM que vous souhaitez exposer à Windows Runtime projections de langage au-delà de C++, vous pouvez créer un composant de Windows Runtime C++ (WRC) qui crée et utilise directement le composant COM (tel que DirectX, par exemple), et expose une réplication de certains sous-ensembles de ses fonctionnalités et fonctionnalités sous la forme d’un composant COM Windows Runtime surface d’API qui prend et retourne uniquement les types Windows Runtime. Vous pouvez ensuite utiliser ce WRC à partir d’une application écrite dans n’importe quelle projection de langage Windows Runtime.

Structure de définition et appel de midl.exe à partir de la ligne de commande

Les principaux concepts organisationnels d’une définition MIDL 3.0 sont des espaces de noms, des types et des membres. Un fichier source MIDL 3.0 (un .idl fichier) contient au moins un espace de noms, à l’intérieur duquel sont des types et/ou des espaces de noms subordonnés. Chaque type contient zéro ou plusieurs membres.

  • Les classes, interfaces, structures et énumérations sont des types.
  • Les méthodes, les propriétés, les événements et les champs sont des exemples de membres.

Lorsque vous compilez un fichier source MIDL 3.0, le compilateur (midl.exe) émet un fichier de métadonnées Windows Runtime (généralement un .winmd fichier).

// Bookstore.idl
namespace Bookstore
{
    runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        BookSku();
        BookSku(Single price, String authorName, String coverImagePath, String title);

        Single Price;

        String AuthorName{ get; };
        Windows.UI.Xaml.Media.ImageSource CoverImage{ get; };
        String CoverImagePath{ get; };
        String Title{ get; };

        Boolean Equals(BookSku other);
        void ApplyDiscount(Single percentOff);
    }
}

Étant donné que l’espace de noms d’un type Windows Runtime fait partie du nom du type, l’exemple ci-dessus définit une classe runtime nommée Bookstore.BookSku. Il n’existe aucun moyen indépendant du langage d’exprimer BookSku sans également exprimer l’espace de noms.

Cette classe implémente l’interface Windows.UI.Xaml.Data.INotifyPropertyChanged . Et la classe contient plusieurs membres : deux constructeurs, une propriété en lecture-écriture (Price), certaines propriétés en lecture seule (AuthorName via Title) et deux méthodes nommées Equals et ApplyDiscount. Notez l’utilisation du type Single plutôt que float. Et cette chaîne a un « S » en majuscules.

Conseil

Visual Studio offre la meilleure expérience pour compiler MIDL 3.0, au moyen de l’extension Visual Studio C++/WinRT (VSIX). Consultez la prise en charge de Visual Studio pour C++/WinRT et VSIX.

Toutefois, vous pouvez également compiler MIDL 3.0 à partir de la ligne de commande. Si le code source de cet exemple est stocké dans un fichier nommé Bookstore.idl, vous pouvez émettre la commande ci-dessous. Si nécessaire pour votre cas, vous pouvez mettre à jour le numéro de version du SDK utilisé dans la commande (qui est 10.0.17134.0).

midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" Bookstore.idl

L’outil midl.exe compile l’exemple et produit un fichier de métadonnées nommé Bookstore.winmd (par défaut, le nom du .idl fichier est utilisé).

Conseil

Si vous utilisez plusieurs fichiers IDL (pour obtenir des conseils à ce sujet, consultez les classes runtime Factoring dans les fichiers Midl (.idl),puis fusionnez tous les fichiers résultants .winmd dans un seul fichier portant le même nom que l’espace de noms racine. .winmd Ce dernier fichier sera celui que les consommateurs de vos API référenceront.

Dans ce cas, BookSku est la seule classe runtime dans l’espace de noms Bookstore . Nous avons donc enregistré une étape et nous venons de nommer le .idl fichier de l’espace de noms.

Par ailleurs, vous pouvez utiliser la where commande pour savoir où midl.exe est installé.

where midl

Si vous souhaitez utiliser les types définis dans un fichier à partir d’un .idl autre .idl fichier, vous utilisez la import directive. Pour plus d’informations et un exemple de code, consultez les contrôles XAML ; liez à une propriété C++/WinRT. Bien sûr, si vous consommez un composant intégré ou tiers, vous n’aurez pas accès au .idl fichier. Par exemple, vous pouvez utiliser l’API Win2D Windows Runtime pour le rendu graphique 2D en mode immédiat. La commande ci-dessus a utilisé le /reference commutateur pour référencer un fichier de métadonnées Windows Runtime (.winmd). Dans cet exemple suivant, nous allons réutiliser ce commutateur, en imaginant le scénario où nous avons Bookstore.winmd, mais pas Bookstore.idl.

// MVVMApp.idl
namespace MVVMApp
{
    runtimeclass ViewModel
    {
        ViewModel();
        Bookstore.BookSku BookSku{ get; };
    }
}

Si le code source de l’exemple ci-dessus est stocké dans un fichier nommé MVVMApp.idl, vous pouvez émettre la commande ci-dessous pour référencer Bookstore.winmd.

midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" /reference Bookstore.winmd MVVMApp.idl

Espaces de noms

Un espace de noms est requis. Il préfixe le nom de tous les types définis dans l’étendue du bloc d’espace de noms avec le nom de l’espace de noms. Un espace de noms peut également contenir des déclarations d’espace de noms subordonnées. Le nom des types définis dans une étendue d’espace de noms subordonné a un préfixe de tous les noms d’espaces de noms contenants.

Les exemples ci-dessous sont deux façons de déclarer la même classe Windows.Foundation.Uri (comme vous pouvez le voir, les périodes séparent les niveaux d’espaces de noms imbriqués).

namespace Windows.Foundation
{
    runtimeclass Uri : IStringable
    {
        ...
    }
}
namespace Windows
{
    namespace Foundation
    {
        runtimeclass Uri : IStringable
        {
            ...
        }
    }
}

Voici un autre exemple montrant qu’il est légal de déclarer des espaces de noms et leurs types de manière imbriquée.

namespace RootNs.SubNs1
{
    runtimeclass MySubNs1Class
    {
        void DoWork();
    }

    namespace SubNs2
    {
        runtimeclass MySubNs2Class
        {
            void DoWork();
        }
    }
}

Mais il est plus courant de fermer l’espace de noms précédent et d’en ouvrir un nouveau, comme ceci.

namespace RootNs.SubNs1
{
    runtimeclass MySubNs1Class
    {
        void DoWork();
    }
}

namespace RootNs.SubNs1.SubNs2
{
    runtimeclass MySubNs2Class
    {
        void DoWork();
    }
}

Types

Il existe deux types de données dans MIDL 3.0 : les types valeur et les types référence. Une variable d’un type valeur contient directement ses données. Une variable d’un type référence stocke une référence à ses données (une telle variable est également appelée objet).

Il est possible que deux variables de type référence référence référencent le même objet. Ainsi, une opération sur une variable affecte l’objet référencé par l’autre variable. Avec les types valeur, les variables ont chacune leur propre copie des données, et il n’est pas possible pour une opération sur l’une d’elles d’affecter l’autre.

Les types valeur de MIDL 3.0 sont divisés en types simples, types d’énumération, types de struct et types nullables.

Les types référence de MIDL 3.0 sont divisés en types de classes, types d’interface et types délégués.

Voici une vue d’ensemble du système de type de MIDL 3.0. Contrairement aux versions précédentes de MIDL, vous ne pouvez pas utiliser d’alias pour ces types.

Category Description
Types de valeur Types simples Intégrale signée : Int16, Int32, Int64
Intégrale non signée : UInt8, UInt16, UInt32, UInt64
Caractères Unicode : Char (représente un UTF-16LE ; unité de code Unicode 16 bits)
Chaînes Unicode : Chaîne
Virgule flottante IEEE : Simple, Double
Boolean : booléen
UUID 128 bits : Guid
Types d'enum Types définis par l’utilisateur de l’énumération de formulaire E {...}
Types struct Types définis par l’utilisateur du struct de formulaire S {...}
Types Nullable Extensions de tous les autres types valeur avec une valeur Null
Types référence Types de classe Classe de base ultime de tous les autres types : Object
Types définis par l’utilisateur de la classe runtime de formulaire C {...}
Types interface Types définis par l’utilisateur de l’interface de formulaire I {...}
Types délégués Types définis par l’utilisateur du délégué <de formulaire returnType> D(...)

Les sept types intégraux prennent en charge les données non signées 8 bits ; et les valeurs 16 bits, 32 bits et 64 bits dans un formulaire signé ou non signé.

Les deux types à virgule flottante, Single et Double, représentent les données à l’aide respectivement des formats IEEE 754 double précision 32 bits et 64 bits.

Le type booléen de MIDL 3.0 représente des valeurs booléennes ; true ou false.

Les caractères et les chaînes dans MIDL 3.0 contiennent des caractères Unicode. Le type Char représente une unité de code UTF-16LE ; et le type String représente une séquence d’unités de code UTF-16LE.

Le tableau suivant récapitule les types numériques de MIDL 3.0.

Category Bits Type Plage/précision
Intégrale signée 16 Int16 –32,768...32,767
32 Int32 –2,147,483,648...2,147,483,647
64 Int64 –9,223,372,036,854,775,808...9,223,372,036,854,775,807
Entier non signé 8 UInt8 0...255
16 UInt16 0...65,535
32 UInt32 0...4,294,967,295
64 UInt64 0...18,446,744,073,709,551,615
Virgule flottante 32 Unique 1,5 × 10−45 à 3,4 × 1038, précision à 7 chiffres
64 Double 5,0 × 10−324 à 1,7 × 10308, précision à 15 chiffres

Les fichiers sources MIDL 3.0 utilisent des définitions de types pour créer de nouveaux types. Une définition de type spécifie le nom et les membres du nouveau type. Ces catégories de types MIDL 3.0 sont définies par l’utilisateur.

  • types d’attributs,
  • types de struct,
  • types d’interface,
  • types runtimeclass,
  • types délégués et
  • types d’énumération.

Un type d’attribut définit un attribut Windows Runtime qui peut être appliqué à d’autres définitions de type. Un attribut fournit des métadonnées sur le type auquel l’attribut est appliqué.

Un type de struct définit une structure Windows Runtime qui contient des membres de données (champs). Les structs sont des types valeur et ne nécessitent pas d’allocation de tas. Un membre de données d’un type struct doit être un type valeur ou un type Nullable. Les types de struct ne prennent pas en charge l’héritage.

Un type d’interface définit une interface Windows Runtime, qui est un ensemble nommé de membres de fonction. Une interface peut spécifier qu’une implémentation de l’interface doit également implémenter une ou plusieurs interfaces supplémentaires (obligatoires) spécifiées. Chaque type d’interface dérive directement de l’interface Windows Runtime IInspectable.

Un type runtimeclass définit une classe Windows Runtime (classe runtime). Une classe runtime contient des membres qui peuvent être des propriétés, des méthodes et des événements.

Un type délégué définit un délégué Windows Runtime, qui représente une référence à une méthode avec une liste de paramètres particulière et un type de retour. Les délégués permettent de traiter une méthode comme une entité qui peut être passée en tant que paramètre. Un délégué est similaire au concept d’un pointeur de fonction trouvé dans d’autres langages. Contrairement aux pointeurs de fonction, les délégués sont orientés objet et de type sécurisé.

Un type d’énumération est un type distinct avec des constantes nommées. Chaque type d’énumération a un type sous-jacent implicite ; Int32 ou UInt32. L’ensemble de valeurs d’un type enum est le même que l’ensemble de valeurs du type sous-jacent.

MIDL 3.0 prend en charge trois catégories de types supplémentaires.

  • types de tableaux unidimensionnels,
  • Types de valeurs nullables et
  • type d’objet .

Vous n’avez pas besoin de déclarer un tableau unidimensionnel avant de pouvoir l’utiliser. Au lieu de cela, les types de tableaux sont construits en ajoutant des crochets à un nom de type. Par exemple, Int32[] est un tableau unidimensionnel d’Int32.

De même, les types valeur Nullable n’ont pas besoin d’être définis avant de pouvoir être utilisés. Pour chaque type de valeur non Nullable T (à l’exception de String), il existe un type Nullable Correspondant Windows.Foundation.IReference<T>, qui peut contenir la valeur nullsupplémentaire . Par exemple, Windows.Foundation.IReference<Int32> est un type qui peut contenir n’importe quel entier 32 bits ou la valeur null. Voir également IReference<T>.

Enfin, MIDL 3.0 prend en charge le type d’objet, qui correspond à l’interface IInspectable Windows Runtime. Les types de référence interface et runtimeclass dérivent conceptuellement du type Object ; le délégué ne le fait pas.

Expressions dans une valeur énumérée

Avec MIDL 3.0, vous ne pouvez utiliser qu’une expression dans la définition de la valeur des constantes nommées d’un type énuméré ; en d’autres termes, dans un initialiseur d’énumération.

Une expression est construite à partir d’opérandes et d’opérateurs. Les opérateurs d’une expression indiquent les opérations à appliquer aux opérandes. Exemples d’opérateurs : +, -, *, /, et new. Les littéraux, les champs, les variables locales et les expressions sont des exemples d’opérandes.

Quand une expression contient plusieurs opérateurs, la priorité des opérateurs contrôle l’ordre dans lequel ils sont évalués. Par exemple, l’expression x + y * z est évaluée comme x + (y * z), car l’opérateur * a une priorité plus élevée que l’opérateur +. Les opérations logiques sont plus prioritaires que les opérations au niveau du bit.

Le tableau suivant récapitule les opérateurs MIDL 3.0, qui répertorient les catégories d’opérateurs dans l’ordre de priorité le plus élevé au plus bas. Les opérateurs d’une même catégorie ont une priorité identique.

Catégorie Expression Description
Principal x++ Post-incrémentation
x-- Post-décrémentation
Unaire +x Identité
-X Négation
!x Négation logique
~x Négation d'opération de bits
++x Pré-incrémentation
--x Pré-décrémentation
Multiplicatif x * y Multiplication
x / y Division
x % y Reste
Additive x + y Ajout, concaténation de chaînes, combinaison de délégués
x – y Soustraction, suppression de délégué
Shift x << y Décalage à gauche
x >> y Décalage à droite
ET au niveau du bit x & y Entier au niveau du bit ET
Opération de bits XOR x ^ y XOR au niveau du bit entier
Opération de bits OR x | y Or au niveau du bit entier
ET logique x && y Boolean logical AND
OU logique x || y OR logique booléen

Classes

Les classes (ou classes runtime) sont les types les plus fondamentaux de MIDL 3.0. Une classe est une définition d’une agrégation de méthodes, de propriétés et d’événements dans une seule unité. Les classes prennent en charge l’héritage et le polymorphisme : mécanismes permettant aux classes dérivées d’étendre et de spécialiser les classes de base.

Vous définissez un nouveau type de classe à l’aide d’une définition de classe. Une définition de classe commence par un en-tête qui spécifie le runtimeclass mot clé, le nom de la classe, la classe de base (le cas échéant) et les interfaces implémentées par la classe. L’en-tête est suivi du corps de la classe, qui se compose d’une liste de déclarations membres écrites entre les délimiteurs { et }.

Voici une définition d’une classe simple nommée Area.

runtimeclass Area
{
    Area(Int32 width, Int32 height);

    Int32 Height;
    Int32 Width;

    static Int32 NumberOfAreas { get; };
}

Cela définit une nouvelle classe Windows Runtime nommée Area, qui a un constructeur qui prend deux paramètres Int32, deux propriétés int32 en lecture-écriture nommées Height et Width, et une propriété statique en lecture seule nommée NumberOfAreas.

Par défaut, une classe runtime est scellée et la dérivation de celle-ci est interdite. Consultez les classes de base.

Pour lier XAML à un modèle d’affichage, la classe d’exécution du modèle d’affichage doit être définie dans MIDL. Pour plus d’informations, consultez Contrôles XAML ; liaison à une propriété C++/WinRT.

Vous pouvez déclarer qu’une classe ne prend pas en charge aucune instance (et doit donc contenir uniquement des membres statiques) en préfixe la définition de classe runtime avec le static mot clé. L’ajout d’un membre non statique à la classe entraîne ensuite une erreur de compilation.

static runtimeclass Area
{
    static Int32 NumberOfAreas { get; };
}

Une classe statique est différente d’une classe vide. Voir également les classes vides.

Vous pouvez indiquer qu’une définition de classe est incomplète en préfixe la définition de classe runtime avec le partial mot clé. Toutes les définitions de classes partielles rencontrées par le compilateur sont combinées en une seule classe runtime. Cette fonctionnalité est principalement destinée aux scénarios de création XAML, où certaines des classes partielles sont générées par l’ordinateur.

Modificateur Signification
statique La classe n’a aucune instance. Par conséquent, seuls les membres statiques sont autorisés.
partial La définition de classe est incomplète.

Consultez Composition et activation pour les modificateurs avancés.

Modificateurs d’accès aux membres

Étant donné que MIDL 3.0 est un langage de définition pour décrire la surface publique des types Windows Runtime, il n’est pas nécessaire de déclarer la syntaxe explicite pour déclarer l’accessibilité publique d’un membre. Tous les membres sont implicitement publics. C’est pourquoi MIDL 3.0 ne nécessite ni n’autorise pas le mot clé (efficacement redondant). public

Classes de base

Une définition de classe peut spécifier une classe de base en suivant le nom de classe et les paramètres de type avec un signe deux-points et le nom de la classe de base. L’omission d’une spécification de classe de base est identique à celle de l’objet de type (en d’autres termes, à partir d’IInspectable).

Notes

Vos classes de modèle d’affichage ( en fait, toute classe runtime que vous définissez dans votre application) n’a pas besoin de dériver d’une classe de base.

Toute classe runtime que vous définissez dans l’application qui dérive d’une classe de base est appelée classe composable . Et ces classes composables sont soumises à certaines contraintes. Pour qu’une application puisse réussir les tests du Kit de certification des applications Windows utilisés par Visual Studio et par le Microsoft Store afin de valider les soumissions (et par conséquent, pour que l’application puisse être ingérée avec succès dans le Microsoft Store), une classe composable doit finalement dériver d’une classe de base Windows. Ce qui signifie que le type de la classe à la racine de la hiérarchie d’héritage doit provenir d’un espace de noms Windows.*.

Pour plus d’informations, consultez Contrôles XAML ; liaison à une propriété C++/WinRT.

Dans l’exemple suivant, la classe de base de Volume est Area et la classe de base de Area est Windows.UI.Xaml.DependencyObject.

unsealed runtimeclass Area : Windows.UI.Xaml.DependencyObject
{
    Area(Int32 width, Int32 height);
    Int32 Height;
    Int32 Width;
}

runtimeclass Volume : Area
{
    Volume(Int32 width, Int32 height, Int32 depth);
    Int32 Depth;
}

Notes

Ici, la zone et le volume sont définis dans le même fichier source. Pour une discussion sur les avantages et les inconvénients, consultez les classes runtime Factoring dans les fichiers Midl (.idl)..

Une classe hérite des membres de sa classe de base. L’héritage signifie qu’une classe contient implicitement tous les membres de sa classe de base, sauf pour le constructeur de la classe de base. Une classe dérivée peut ajouter des membres hérités, mais ne peut pas supprimer la définition d’un membre hérité.

Dans l’exemple précédent, le volume hérite des propriétés Height et Width de La zone. Ainsi, chaque instance de volume contient trois propriétés : Hauteur, Largeur et Profondeur.

En règle générale, les règles de résolution de type nécessitent qu’un nom de type soit complet lorsqu’il est référencé. Une exception est lorsque le type a été défini dans le même espace de noms que le type actuel. L’exemple ci-dessus fonctionne comme écrit si la zone et le volume se trouvent tous les deux dans le même espace de noms.

Interfaces implémentées

Une définition de classe peut également spécifier une liste d’interfaces que la classe implémente. Vous spécifiez les interfaces en tant que liste séparée par des virgules d’interfaces suivant la classe de base (facultative).

Dans l’exemple ci-dessous, la classe Area implémente l’interface IStringable ; et la classe Volume implémente iStringable et l’interface hypothétique IEquatable .

unsealed runtimeclass Area : Windows.Foundation.IStringable
{
    Area(Int32 width, Int32 height);
    Int32 Height;
    Int32 Width;
}

runtimeclass Volume : Area, Windows.Foundation.IStringable, IEquatable
{
    Volume(Int32 width, Int32 height, Int32 depth);
    Int32 Depth;
}

Dans MIDL, vous ne déclarez pas les membres de l’interface sur la classe. Bien sûr, vous devez déclarer et les définir sur l’implémentation réelle.

Membres

Les membres d’une classe sont des membres statiques ou des membresd’instance. Un membre statique appartient à une classe. Un membre d’instance appartient à un objet (autrement dit, une instance d’une classe).

Ce tableau montre les types de membres qu’une classe peut contenir.

Type de membre Description
Constructeurs Actions requises pour initialiser une instance de la classe ou pour initialiser la classe elle-même
Propriétés Actions associées à la lecture et à l’écriture de propriétés nommées d’une instance de la classe ou de la classe elle-même
Méthodes Calculs et actions pouvant être effectués par une instance de la classe, ou par la classe elle-même
Événements Notifications pouvant être déclenchées par une instance de la classe

Constructeurs

MIDL 3.0 prend en charge la déclaration des constructeurs d’instance. Un constructeur d’instance est une méthode qui implémente les actions requises pour initialiser une instance d’une classe. Les constructeurs peuvent ne pas être statiques.

Un constructeur est déclaré comme une méthode d’instance (mais sans type de retour) et avec le même nom que la classe contenante.

Les constructeurs d’instances peuvent être surchargés. Par exemple, la classe Test ci-dessous déclare trois constructeurs d’instances ; un avec aucun paramètre (constructeur par défaut ), celui qui prend un paramètre Int32 et un qui prend deux paramètres Doubles (constructeurs paramétrables ).

runtimeclass Test
{
    Test();
    Test(Int32 x);
    Test(Double x, Double y);
}

Pour plus d’informations sur la syntaxe des listes de paramètres, consultez Méthodes ci-dessous.

Les propriétés, méthodes et événements d’instance sont héritées. Les constructeurs d’instances ne sont pas hérités (avec une exception), et une classe n’a pas de constructeur d’instance autre que celles réellement déclarées dans la classe. Si aucun constructeur d’instance n’est fourni pour une classe, vous ne pouvez pas instancier directement la classe. Pour une telle classe, vous disposez généralement d’une méthode d’usine ailleurs qui retourne une instance de la classe.

L’exception est des classes non scellées. Une classe non scellée peut avoir un ou plusieurs constructeurs protégés.

Propriétés

Les propriétés sont conceptuellement similaires aux champs (par exemple, les champs C# ou les champs d’un struct MIDL 3.0). Les propriétés et les champs sont membres avec un nom et un type associé. Toutefois, contrairement aux champs, les propriétés ne indiquent pas les emplacements de stockage. Au lieu de cela, les propriétés ont des accesseurs qui spécifient la fonction à exécuter lorsque vous lisez ou écrivez une propriété.

Une propriété est déclarée comme le champ d’un struct, sauf que la déclaration se termine par un mot clé et/ou un getset mot clé écrit entre les délimiteurs { et }, et se termine par un point-virgule.

Une propriété qui a à la fois un get mot clé et un set mot clé est une propriété en lecture-écriture. Une propriété qui n’a qu’un get mot clé est une propriété en lecture seule. Le Windows Runtime ne prend pas en charge les propriétés en écriture seule.

Par exemple, la zone de classe, vue précédemment, contient deux propriétés en lecture-écriture nommées Height et Width.

unsealed runtimeclass Area
{
    Int32 Height { get; set; };
    Int32 Width; // get and set are implied if both are omitted.
}

La déclaration de Width omet les accolades et les getset mots clés. L’omission implique que la propriété est en lecture-écriture et est sémantiquement identique à fournir les mots clés et les get mots clés dans cet ordre ,get suivi de set.set

En outre, vous pouvez spécifier uniquement le get mot clé pour indiquer que la propriété est en lecture seule.

// Read-only instance property returning mutable collection.
Windows.Foundation.Collections.IVector<Windows.UI.Color> Colors { get; };

Le Windows Runtime ne prend pas en charge les propriétés en écriture seule. Toutefois, vous pouvez spécifier uniquement le set mot clé pour réviser une propriété en lecture seule existante dans une propriété en lecture-écriture. Prenez cette version de Zone en tant qu’exemple.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; };
}

Si vous souhaitez ensuite rendre la propriété SurfaceColor en lecture-écriture et que vous n’avez pas besoin de maintenir la compatibilité binaire avec les définitions antérieures de Zone (par exemple, la classe Area est un type dans une application que vous recompilez chaque fois), vous pouvez simplement ajouter le set mot clé à la déclaration SurfaceColor existante comme celle-ci.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; set; };
}

Si, en revanche, vous avez besoin de stabilité binaire (par exemple, la classe Area est un composant d’une bibliothèque que vous envoyez aux clients), vous ne pouvez pas ajouter le set mot clé à la déclaration de propriété existante. Cela modifie l’interface binaire de votre classe.

Dans ce cas, ajoutez le mot clé de propriété set à une définition supplémentaire de la propriété à la fin de la classe comme celle-ci.

unsealed runtimeclass Area
{
    ...
    Color SurfaceColor { get; };
    ...
    Color SurfaceColor { set; };
}

Le compilateur génère une erreur pour une propriété en écriture seule. Mais ce n’est pas ce qui est fait ici. En raison de la déclaration précédente de la propriété en lecture seule, l’ajout du mot clé set ne déclare pas une propriété en écriture seule, mais plutôt une propriété en lecture-écriture.

L’implémentation Windows Runtime d’une propriété est une ou deux méthodes d’accesseur sur une interface. L’ordre des mots clés get et set dans la déclaration de propriété détermine l’ordre des méthodes d’accesseur get et set dans l’interface de stockage.

L’accesseur get correspond à une méthode sans paramètre avec une valeur de retour du type de propriété , l’getter de propriété.

Un set accesseur correspond à une méthode avec une valeur nommée de paramètre unique, et aucun type de retour , l’élément de propriété setter.

Par conséquent, ces deux déclarations produisent des interfaces binaires différentes.

Color SurfaceColor { get; set; };
Color SurfaceColor { set; get; };
Propriétés statiques et d’instance

Comme pour les méthodes, MIDL 3.0 prend en charge les propriétés d’instance et les propriétés statiques. Les propriétés statiques sont déclarées avec le static préfixe modificateur et les propriétés d’instance sont déclarées sans elle.

Méthodes

Une méthode est un membre qui implémente un calcul ou une action qui peut être effectuée par une instance de la classe, ou par la classe elle-même. Une méthode statique est accessible via la classe. Une méthode d’instance est accessible via une instance de la classe.

Une méthode a une liste (éventuellement vide) de paramètres, qui représentent des valeurs ou des références de variable passées à la méthode. Une méthode a également un type de retour, qui spécifie le type de la valeur calculée et retournée par la méthode. Le type de retour d’une méthode est void s’il ne retourne pas de valeur.

// Instance method with no return value.
void AddData(String data);

// Instance method *with* a return value.
Int32 GetDataSize();

// Instance method accepting/returning a runtime class.
// Notice that you don't say "&" nor "*" for reference types.
BasicClass MergeWith(BasicClass other);

// Asynchronous instance methods.
Windows.Foundation.IAsyncAction UpdateAsync();
Windows.Foundation.IAsyncOperation<Boolean> TrySaveAsync();

// Instance method that returns a value through a parameter.
Boolean TryParseInt16(String input, out Int16 value);

// Instance method that receives a reference to a value type.
Double CalculateArea(ref const Windows.Foundation.Rect value);

// Instance method accepting or returning a conformant array.
void SetBytes(UInt8[] bytes);
UInt8[] GetBytes();

// instance method that writes to a caller-provided conformant array
void ReadBytes(ref UInt8[] bytes);

La signature d’une méthode doit être unique dans la classe dans laquelle la méthode est déclarée. La signature d’une méthode se compose du nom de la méthode, des types de ses paramètres et/ou du nombre de ses paramètres. La signature d’une méthode n’inclut pas le type de retour.

Modificateurs de visibilité des méthodes

Une méthode peut avoir l’un des deux modificateurs de visibilité facultatifs lorsque la méthode est présente dans une classe dérivée.

Le modificateur substituable indique que cette méthode peut être remplacée par une méthode (portant le même nom et la même signature) appartenant à une sous-classe.

Le modificateur protégé indique que cette méthode est accessible uniquement par les membres d’une classe dérivée ultérieurement.

Surcharge de méthode

La surcharge de méthode permet à plusieurs méthodes de la même classe d’avoir le même nom tant que leurs paramètres diffèrent en nombre (en d’autres termes, les méthodes ont une arité différente).

runtimeclass Test
{
    static void F();
    static void F(Double x);
    static void F(Double x, Double y);
}

Notes

Toutes les méthodes portant le même nom doivent avoir une arité différente. C’est parce que les langages de programmation faiblement typés ne prennent pas en charge la surcharge par type.

Paramètres

Les paramètres sont utilisés pour transmettre des valeurs ou des références de variable à une méthode. Un paramètre décrit un emplacement avec un type et un nom, et éventuellement un mot clé modificateur. Un argument est une valeur réelle passée dans cet emplacement de l’appelant de la méthode à l’appelé.

Les paramètres d’une méthode obtiennent leur valeur à partir de l’argument spécifique spécifié lorsque la méthode est appelée. La façon dont les arguments sont passés entre l’appelant et l’appelé dépend du type du paramètre. Par défaut, tous les paramètres sont des paramètres d’entrée, c’est-à-dire qu’ils sont marshalés de l’appelant vers l’appelé uniquement. Les mots clés refde modificateur, ref constet out peuvent être ajoutés pour modifier la direction par défaut du marshaling entre l’appelant et l’appelé, et créer des paramètres de sortie. Cependant, tous les mots clés ne sont pas valides avec tous les types de paramètres; les combinaisons valides sont détaillées ci-dessous.

Important

Le Common Language Runtime (CLR) comporte des concepts et des mots clés de modification qui peuvent sembler similaires à ceux décrits dans cette section. Toutefois, dans la pratique, ceux-ci ne sont pas liés, et l’effet de ces modificateurs est spécifique à la conception et au fonctionnement de la Windows Runtime.

Les types valeur sont des paramètres d’entrée implicite et, par défaut, une copie de l’argument est passée de l’appelant à l’appelé. Les paramètres de valeur peuvent être transformés en paramètres de sortie avec le out mot clé ; dans ce cas, l’argument est marshalé à la place à partir de l’appelé uniquement vers l’appelant.

runtimeclass Test
{
    static void Divide(Int32 x, Int32 y, out Int32 result, out Int32 remainder);
}

En tant qu’optimisation spéciale des performances, les types de struct (et aucun autre type), qui sont normalement passés par valeur en tant que copie complète, peuvent être passés par pointeur vers le struct immuable. Cette opération est obtenue avec le ref const mot clé (nonconst ref) qui marque le paramètre de struct comme paramètre d’entrée, mais indique au marshaleur de passer un pointeur au stockage du struct, au lieu de passer une copie complète du struct. Notez toutefois que le struct est immuable; le pointeur est conceptuellement un pointeur const. Il n’y a pas de boxe impliquée. Il s’agit d’un choix pratique lors de l’acceptation d’une valeur aussi grande qu’une matrice4x4, par exemple.

runtimeclass Test
{
    static Boolean IsIdentity(ref const Windows.Foundation.Numerics.Matrix4x4 m);
}

Les types référence sont également des paramètres d’entrée implicite, ce qui signifie que l’appelant est chargé d’allouer l’objet et de lui transmettre une référence en tant qu’argument ; toutefois, étant donné que l’argument est une référence à l’objet, les modifications apportées à cet objet par l’appelé sont observées par l’appelant après l’appel. Vous pouvez également définir un type référence comme paramètre de sortie avec le out mot clé. Dans ce cas, les rôles sont inversés ; l’appelé est celui qui alloue l’objet et le retourne à l’appelant. Là encore, les ref mots clés ne peuvent pas être utilisés en général avec des types référence (voir l’exception ci-dessous).

runtimeclass Test
{
    static void CreateObjectWithConfig(Config config, out MyClass newObject);
}

Le tableau suivant récapitule le comportement des mots clés de marshaling pour les paramètres de valeur et les paramètres de référence :

Comportement Alloué par Mot clé Types Remarques
Paramètre d’entrée Appelant (aucun) Tous les types Comportement par défaut
ref const Struct uniquement Optimisation des performances
Paramètre de sortie Appelé out Tous les types

Windows Runtime prend en charge les types de tableaux, dont le comportement en tant que paramètre est un peu différent. Un tableau est une structure de données qui contient un certain nombre de variables stockées séquentiellement et accessibles via un index. Les variables contenues dans un tableau, également appelées éléments du tableau, sont du même type et ce type est appelé type d’élément du tableau.

MIDL 3.0 prend en charge les déclarations d’un tableau unidimensionnel.

Un paramètre de tableau est un type référence et, comme tous les types de référence, est par défaut un paramètre d’entrée. Dans ce cas, l’appelant alloue le tableau à l’appelé, qui peut lire ses éléments, mais ne peut pas les modifier (lecture seule). Il s’agit du modèle de tableau de passe . Vous pouvez également utiliser le modèle de tableau de remplissage en ajoutant le ref mot clé au paramètre . Dans cette configuration, le tableau est toujours alloué par l’appelant, mais est conceptuellement un paramètre de sortie dans le sens où l’appelé remplira les valeurs des éléments de tableau. Enfin, le dernier modèle est le tableau de réception où (comme tous les paramètres de référence de sortie), l’appelé alloue et initialise l’argument avant qu’il ne soit retourné à l’appelant.

runtimeclass Test
{
    // Pass array pattern: read-only array from caller to callee
    void PassArray(Int32[] values);

    // Fill array pattern: caller allocates array for callee to fill
    void FillArray(ref Int32[] values);

    // Receive array pattern: callee allocates and fill an array returned to caller
    void ReceiveArray(out Int32[] values);
}

Le tableau suivant récapitule le comportement des tableaux et de leurs éléments :

Modèle de tableau Mot clé Alloué par Accès des éléments par appelé
« Pass array » (aucun) Appelant Lecture seule
« Tableau de remplissage » ref Appelant Écriture seule
« Tableau de réception » out Appelé Lecture-écriture

Pour plus d’informations sur l’utilisation des paramètres de tableau de style C, également appelés tableaux conformes, avec C++/WinRT, consultez paramètres de tableau.

Méthodes statiques et d’instance

Une méthode déclarée avec un static modificateur préfixé est une méthode statique. Une méthode statique n’a pas accès à une instance spécifique et peut donc accéder directement à d’autres membres statiques de la classe.

Une méthode déclarée sans static modificateur est une méthode d’instance. Une méthode d’instance a accès à une instance spécifique et peut accéder aux membres statiques et d’instance de la classe.

La classe Entity suivante a des membres statiques et d’instance.

runtimeclass Entity
{
    Int32 SerialNo { get; };
    static Int32 GetNextSerialNo();
    static void SetNextSerialNo(Int32 value);
}

Chaque instance d’entité contient son propre numéro de série (et probablement d’autres informations qui ne sont pas affichées ici). En interne, le constructeur d’entité (qui est comme une méthode d’instance) initialise la nouvelle instance avec le numéro de série disponible suivant.

La propriété SerialNo fournit l’accès au numéro de série de l’instance sur laquelle vous appelez la méthode get de propriété .

Les méthodes statiques GetNextSerialNo et SetNextSerialNo peuvent accéder au membre statique de numéro de série disponible interne de la classe Entity .

Méthodes substituables et protégées

Toutes les méthodes d’un type Windows Runtime sont effectivement virtuelles. Lorsqu’une méthode virtuelle est appelée, le type au moment de l’exécution de l’instance pour laquelle cet appel prend place détermine l’implémentation de méthode à appeler.

Une méthode peut être substituée dans une classe dérivée. Lorsqu’une déclaration de méthode d’instance inclut un overridable modificateur, la méthode peut être remplacée par des classes dérivées. Si une classe dérivée remplace réellement une méthode de classe de base substituable est déterminée par l’implémentation ; il n’est pas présent dans les métadonnées. Si une classe dérivée redeclare une méthode dans la classe de base, elle déclare une nouvelle méthode qui se trouve à côté de la méthode de classe dérivée, plutôt que de la remplacer.

Lorsqu’une déclaration de méthode d’instance inclut un protected modificateur, la méthode est visible uniquement pour les classes dérivées.

Événements

Une déclaration d’événement est un membre qui spécifie qu’une classe est une source d’événement. Une telle source d’événement fournit des notifications à tout destinataire qui implémente un délégué (une méthode avec une signature spécifique).

Vous déclarez un événement à l’aide du event mot clé, suivi du nom du type délégué (qui décrit la signature de méthode requise), suivi du nom de l’événement. Voici un exemple d’événement qui utilise un type délégué existant à partir de la plateforme.

runtimeclass Area
{
    ...
    event Windows.UI.Xaml.WindowSizeChangedEventHandler SizeChanged;
    ...
}

Une déclaration d’événement ajoute implicitement deux méthodes à la classe : une méthode add , qu’un client appelle pour ajouter un gestionnaire d’événements à la source et une méthode remove , qu’un client appelle pour supprimer un gestionnaire d’événements précédemment ajouté. Voici d’autres exemples.

// Instance event with no meaningful payload.
event Windows.Foundation.TypedEventHandler<BasicClass, Object> Changed;

// Instance event with event parameters.
event Windows.Foundation.TypedEventHandler<BasicClass, BasicClassSaveCompletedEventArgs> SaveCompleted;

// Static event with no meaningful payload.
static event Windows.Foundation.EventHandler<Object> ResetOccurred;

// Static event with event parameters.
static event Windows.Foundation.EventHandler<BasicClassDeviceAddedEventArgs> DeviceAdded;

Par convention, deux paramètres sont toujours passés à un gestionnaire d’événements Windows Runtime : l’identité de l’expéditeur et un objet d’arguments d’événement. L’expéditeur est l’objet qui a déclenché l’événement ou null pour les événements statiques. Si l’événement n’a pas de charge utile significative, les arguments d’événement sont un objet dont la valeur est Null.

Délégués

Un type délégué spécifie une méthode avec une liste de paramètres particulière et un type de retour. Une instance unique d’un événement peut contenir n’importe quel nombre de références aux instances de son type délégué. La déclaration est similaire à celle d’une méthode membre régulière, sauf qu’elle existe en dehors d’une classe runtime et qu’elle est précédée du delegate mot clé.

Un délégué permet de traiter les méthodes comme des entités qui peuvent être affectées à des variables et passées en tant que paramètres. Les délégués sont similaires au concept de pointeurs de fonction trouvés dans d’autres langages. Toutefois, contrairement aux pointeurs de fonction, les délégués sont orientés objet et de type sécurisé.

Si nous ne voulons pas utiliser le type de délégué WindowSizeChangedEventHandler à partir de la plateforme, nous pouvons définir notre propre type délégué.

delegate void SizeChangedHandler(Object sender, Windows.UI.Core.WindowSizeChangedEventArgs args);

Une instance de notre type délégué SizeChangedHandler peut référencer n’importe quelle méthode qui accepte deux arguments ( un Object et un WindowSizeChangedEventArgs) et retourne void. Une fois que nous avons abordé les structs, vous serez également en mesure de remplacer le paramètre WindowSizeChangedEventArgs par un type d’arguments d’événement propre.

Une propriété intéressante et utile d’un délégué est qu’il ne connaît pas ou ne se soucie pas de la classe de la méthode qu’il référence; tout cela importe, c’est que la méthode référencée a les mêmes paramètres et le même type de retour que le délégué.

Vous pouvez éventuellement attribuer une déclaration de délégué avec [uuid(...)].

Consultez également Les délégués qui retournent HRESULT.

Structures

Un struct est une structure de données qui peut contenir des membres de données (champs). Mais, contrairement à une classe, un struct est un type valeur.

Les structures sont particulièrement utiles pour les petites structures de données qui ont une sémantique par rapport à leurs valeurs. Les nombres complexes ou les points d’un système de coordonnées sont de bons exemples de structs. L’utilisation de structs plutôt que de classes pour les petites structures de données peut faire une grande différence dans le nombre d’allocations de mémoire qu’une application effectue.

Utilisons un exemple pour contraster les classes et les structs. Voici une version de Point en tant que classe.

runtimeclass Point
{
    Point(Int32 x, Int32 y);
    Int32 x;
    Int32 y;
}

Ce programme C# crée et initialise un tableau de 100 instances de Point. Avec Point implémenté en tant que classe, 101 objets distincts sont instanciés : un pour l’objet tableau lui-même ; et un pour chacun des 100 éléments Point .

class Test
{
    static Test()
    {
        Point[] points = new Point[100];
        for (Int32 i = 0; i < 100; ++i) points[i] = new Point(i, i);
    }
}

Une alternative plus performante consiste à faire de Point un struct, au lieu d’une classe.

struct Point
{
    Int32 x;
    Int32 y;
};

À présent, un seul objet est instancié : l’objet tableau lui-même. Les éléments Point sont stockés en ligne à l’intérieur du tableau ; une disposition de mémoire que les caches de processeur sont en mesure d’utiliser pour un effet puissant.

La modification d’un struct est un changement cassant binaire. Par conséquent, les structs implémentés dans le cadre de Windows lui-même ne sont pas modifiés une fois introduits.

Interfaces

Une interface définit un contrat qui peut être implémenté par des classes. Une interface peut contenir des méthodes, des propriétés et des événements, comme les classes.

Contrairement à une classe, une interface ne fournit pas d’implémentations des membres qu’elle définit. Il spécifie simplement les membres qui doivent être fournis par n’importe quelle classe qui implémente l’interface.

Les interfaces peuvent nécessiter une classe qui implémente l’interface pour implémenter également d’autres interfaces. Dans l’exemple suivant, l’interface IComboBox nécessite que toute classe implémentant IComboBox, implémente également ITextBox et IListBox. En outre, une classe implémentant IComboBox doit également implémenter IControl. C’est parce que ITextBox et IListBoxl’exigent.

interface IControl
{
    void Paint();
}

interface ITextBox requires IControl
{
    void SetText(String text);
}

interface IListBox requires IControl
{
    void SetItems(String[] items);
}

interface IComboBox requires ITextBox, IListBox
{
    ...
}

Une classe peut implémenter zéro ou plusieurs interfaces. Dans l’exemple suivant, la classe EditBox implémente IControl et IDataBound.

interface IDataBound
{
    void Bind(Binder b);
}

runtimeclass EditBox : IControl, IDataBound
{
}

Pour Windows Runtime types dans la plateforme Windows, une interface est définie si les développeurs qui consomment ces types sont censés implémenter l’interface. Un autre cas d’usage pour définir une interface est que plusieurs classes runtime implémentent l’interface, et les développeurs qui consomment ces classes runtime accèdent à différents types d’objets de manière générique (et donc polymorphe) via cette interface commune.

Notes

Réfléchissez deux fois à l’utilisation du requires mot clé dans MIDL 3.0. Il peut entraîner des conceptions désordonnée, en particulier lorsque le contrôle de version est pris en compte.

Énumérations

Un type d’énumération (ou type énuméré ou énumération) est un type valeur distinct avec un ensemble de constantes nommées. L’exemple suivant définit et utilise un type d’énumération nommé Color avec trois valeurs constantes : Rouge, Vert et Bleu.

enum Color
{
    Red,
    Green,
    Blue, // Trailing comma is optional, but recommended to make future changes easier.
};

Chaque type d’énumération a un type intégral correspondant appelé type sous-jacent du type d’énumération. Le type sous-jacent d’une énumération est Int32 ou UInt32.

Le Windows Runtime prend en charge deux types d’enums : les énumérations normales et les enums d’indicateurs. Une énumération du type normal exprime un ensemble de valeurs exclusives ; alors qu’un des types d’indicateurs représente un ensemble de valeurs booléennes. Pour activer les opérateurs au niveau du bit pour une énumération d’indicateurs, le compilateur MIDL 3.0 génère des surcharges d’opérateurs C++.

Un enum d’indicateurs a l’attribut [flags] appliqué. Dans ce cas, le type sous-jacent de l’énumération est UInt32. Lorsque l’attribut [flags] n’est pas présent (énumération normale), le type sous-jacent de l’énumération est Int32. Il n’est pas possible de déclarer une énumération comme tout autre type.

[flags]
enum SetOfBooleanValues
{
    None   = 0x00000000,
    Value1 = 0x00000001,
    Value2 = 0x00000002,
    Value3 = 0x00000004,
};

Le format de stockage et la plage de valeurs possibles d’un type enum sont déterminés par son type sous-jacent. L’ensemble de valeurs qu’un type d’énumération peut prendre n’est pas limité par ses membres d’énumération déclarés.

L’exemple suivant définit un type d’énumération nommé Alignment, avec un type sous-jacent d’Int32.

enum Alignment
{
    Left = -1,
    Center = 0,
    Right = 1
};

Comme c’est également le cas pour C et C++, une énumération MIDL 3.0 peut inclure une expression constante qui spécifie la valeur du membre (comme illustré ci-dessus). La valeur constante de chaque membre d’énumération doit se trouver dans la plage du type sous-jacent de l’énumération. Lorsqu’une déclaration de membre d’énumération ne spécifie pas explicitement de valeur, le membre reçoit la valeur zéro (s’il s’agit du premier membre dans le type d’énumération), ou la valeur du membre d’énumération précédent de texte plus un.

L’exemple suivant définit un type d’énumération nommé Permissions, avec un type sous-jacent UInt32.

[flags]
enum Permissions
{
    None = 0x0000,
    Camera = 0x0001,
    Microphone = 0x0002
};

Attributs

Les types, les membres et d’autres entités dans le code source MIDL 3.0 prennent en charge les modificateurs qui contrôlent certains aspects de leur comportement. Par exemple, l’accessibilité d’une méthode est contrôlée à l’aide du modificateur d’accès protected . MIDL 3.0 généralise cette fonctionnalité afin que les types définis par l’utilisateur d’informations déclaratives puissent être attachés aux entités de programme et récupérés au moment de l’exécution à partir des métadonnées.

Les programmes spécifient ces informations déclaratives supplémentaires en définissant et en utilisant les attributs.

L’exemple suivant définit un attribut HelpAttribute , qui peut être placé sur des entités de programme pour fournir des liens vers leur documentation associée. Comme vous pouvez le voir, un attribut est essentiellement un type de struct. Il n’a donc pas de constructeur et ne contient que des membres de données.

[attributeusage(target_runtimeclass, target_event, target_method, target_property)]
attribute HelpAttribute
{
    String ClassUri;
    String MemberTopic;
}

Un attribut peut être appliqué en donnant son nom, ainsi que tous les arguments, entre crochets juste avant la déclaration associée. Si le nom d’un attribut se termine par l’attribut, cette partie du nom peut être omise lorsque l’attribut est référencé. Par exemple, l’attribut HelpAttribute peut être utilisé comme suit.

[Help("https://docs.contoso.com/.../BookSku", "BookSku class")]
runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
    [Help("https://docs.contoso.com/.../BookSku_Title", "Title method")]
    String Title;
}

Vous pouvez appliquer le même attribut à plusieurs déclarations à l’aide d’un bloc d’étendue suivant l’attribut. Autrement dit, un attribut immédiatement suivi d’accolades entourant les déclarations auxquelles l’attribut s’applique.

runtimeclass Widget
{
    [Help("https://docs.contoso.com/.../Widget", "Widget members")]
    {
        void Display(String text);
        void Print();
        Single Rate;
    }
}

Les attributs implémentés dans le cadre de Windows lui-même se trouvent généralement dans l’espace de noms Windows.Foundation .

Comme indiqué dans le premier exemple, vous utilisez l’attribut [attributeusage(<target>)] sur votre définition d’attribut. Les valeurs cibles valides sont target_all, , , target_delegatetarget_enum, target_event, target_field, target_propertytarget_parametertarget_structtarget_interfacetarget_methodtarget_runtimeclass, Vous pouvez inclure plusieurs cibles entre parenthèses, séparées par des virgules.

D’autres attributs que vous pouvez appliquer à un attribut sont [allowmultiple] et [attributename("<name>")].

Types paramétrables

L’exemple ci-dessous génère l’erreur MIDL2025 : [msg]syntax error [context] : en attente > ou, près de «>> ».

Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String>> RetrieveCollectionAsync();

Au lieu de cela, insérez un espace entre les deux > caractères afin que la paire de caractères de fermeture de modèle ne soit pas mal interprétée comme un opérateur de décalage droit.

Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String> > RetrieveCollectionAsync();

L’exemple ci-dessous génère l’erreur MIDL2025 : [msg]syntax error [context] : en attente > ou, près de « [ ». Cela est dû au fait qu’il n’est pas valide d’utiliser un tableau comme argument de type de paramètre pour une interface paramétrable.

Windows.Foundation.IAsyncOperation<Int32[]> RetrieveArrayAsync();

Pour la solution, consultez Renvoi asynchrone d’un tableau.