Présentation du langage de définition de l’interface Microsoft 3.0

Microsoft Interface Definition Language (MIDL) 3.0 est une syntaxe simplifiée et moderne permettant de définir des types Windows Runtime à l’intérieur des fichiers IDL (Interface Definition Language) (fichiers.idl). Cette nouvelle syntaxe se sentira 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 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 conçue pour définition de types. Vous utiliserez un autre langage de programmation pour implémenter ces types. Pour utiliser MIDL 3.0, vous aurez besoin du SDK Windows version 10.0.17134.0 (Windows 10, version 1803) (midl.exe version 8.01.0622 ou ultérieure, utilisée avec le commutateur /winrt).

Note

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

MIDL 1.0, 2.0 et 3.0

Le langage de définition d’interface (IDL) a commencé par le système DCE/RPC (Distributed Computing Environment/Remote Procedure Calls). Le MIDL 1.0 d’origine est DCE/RPC IDL avec des améliorations pour la définition des interfaces et des coclasses COM.

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

Consultez également Transition vers MIDL 3.0 à partir duMIDLRT classique.

MIDL 3.0 est une syntaxe beaucoup plus simple et plus moderne, dont l’objectif est de déclarer des API Windows Runtime. Vous pouvez également l’utiliser dans vos projets, en particulier pour définir classes runtime C++/WinRT. Les en-têtes, à utiliser à partir de C++/WinRT, pour les API 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 passer exclusivement des types Windows Runtime aux API Windows Runtime et à partir des API Windows Runtime. Bien qu’il s’agit d’une décision de conception valide de passer une interface COM brute à une API Windows Runtime et à partir d’une API Windows Runtime, cela limite les consommateurs de cette API Windows Runtime particulière à des 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 une IDXGISwapChain pointeur d’interface, puis la transmettre à la méthode ISwapChainPanelNative ::SetSwapChain. Une application C#, par exemple, ne serait pas en mesure d’obtenir un IDXGISwapChain 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 sont fonctionnalités ou fonctionnalités d’un composant COM que vous souhaitez exposer aux projections de langage Windows Runtime au-delà de C++, vous pouvez créer une composant Windows Runtime (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’une surface d’API Windows Runtime qui prend et retourne Windows Types d’exécution uniquement. Vous pouvez ensuite utiliser ce WRC à partir d’une application écrite dans toute 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 fichier .idl) 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 fichier .winmd).

// 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 exprimer également 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 unique au lieu de float . Et que chaîne a un majuscule « S ».

Pourboire

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

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 fichier .idl est utilisé).

Pourboire

Si vous utilisez plusieurs fichiers IDL (pour obtenir des conseils à ce sujet, consultez classes runtime Factoring dans les fichiers Midl (.idl)), puis fusionnez tous les fichiers .winmd résultants dans un seul fichier portant le même nom que l’espace de noms racine. Ce dernier fichier .winmd 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 d’appeler le fichier de l’espace de noms.

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

where midl

Si vous souhaitez utiliser les types définis dans un fichier .idl d’un autre fichier .idl, vous utilisez la directive import. Pour plus d’informations et un exemple de code, consultez 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 fichier .idl. Par exemple, vous pouvez utiliser la l’API Win2D Windows Runtime pour le rendu graphique 2D en mode immédiat. La commande ci-dessus a utilisé le commutateur /reference pour référencer un fichier de métadonnées Windows Runtime (.winmd). Dans cet exemple suivant, nous allons utiliser à nouveau ce commutateur, 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és. 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 types de données dans MIDL 3.0 : les types valeur et les types de 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érencent le même objet. Par conséquent, 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 de 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 MIDL 3.0. Contrairement aux versions précédentes de MIDL, vous ne pouvez pas utiliser d’alias pour ces types.

Catégorie Description
Types 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 ; une unité de code Unicode 16 bits)
Chaînes Unicode : chaîne
Virgule flottante IEEE : unique , double
Boolean : booléen
UUID 128 bits : guid
Types d’énumération Types définis par l’utilisateur du formulaire énumération E {...}
Types de structs Types définis par l’utilisateur du formulaire struct S {...}
Types Nullable Extensions de tous les autres types valeur avec une valeur null
Types de référence Types de classes Classe de base ultime de tous les autres types : Object
Types définis par l’utilisateur du formulaire runtimeclass C {...}
Types d’interface Types définis par l’utilisateur du formulaire interface I {...}
Types délégués Types définis par l’utilisateur du formulaire délégué <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, unique et double, représentent respectivement les données utilisant les formats IEEE 754 à double précision 32 bits et 64 bits.

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

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.

Catégorie Bribes 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
Intégrale non signée 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 type 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 structs,
  • types d’interface,
  • types runtimeclass,
  • types délégués et
  • types d’énumération.

Un attribut type 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 struct type 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.

Une interface type 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 IInspectable Windows Runtime.

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 délégué type définit un délégué Windows Runtime, qui représente une référence à une méthode avec une liste de paramètres et un type de retour particulier. 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 de 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 enum 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 d’énumération est identique à 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 Object.

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 suivant un nom de type avec crochets. Par exemple, Int32[] est un tableau unidimensionnel de Int32.

De même, types de valeurs nullables n’ont pas besoin d’être définis avant qu’ils ne puissent ê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 supplémentaire null. 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 objet , qui est mappé à l’interface IInspectable Windows Runtime. L’interface et runtimeclass de référence dérivent conceptuellement du type object  ; 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 de opérandes et opérateurs. Les opérateurs d’une expression indiquent les opérations à appliquer aux opérandes. Par exemple, les opérateurs incluent +, -, *, /et new. Les exemples d’opérandes incluent des littéraux, des champs, des variables locales et des expressions.

Lorsqu’une expression contient plusieurs opérateurs, la priorité des opérateurs contrôle l’ordre dans lequel les opérateurs individuels 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, répertoriant les catégories d’opérateurs dans l’ordre de priorité le plus élevé au plus bas. Les opérateurs de la même catégorie ont une priorité égale.

Catégorie Expression Description
Primaire x++ Post-incrément
x-- Décrémentation post-décrémentation
Unaire +x Identité
-x Négation
!x Négation logique
~x Négation au niveau du bit
++x Pré-incrémentation
--x Décrémentation préalable
Multiplicatif x * y Multiplication
x/ y Division
x % y Reste
Additif x + y Addition, Concaténation de chaînes, combinaison de délégués
x – y Soustraction, suppression de délégués
Période de travail x << y Maj vers la gauche
x >> y Maj vers la droite
AND au niveau du bit x & y Entier ET au niveau du bit
XOR au niveau du bit x ^ y XOR au niveau du bit entier
OR au niveau du bit x | y Or au niveau du bit entier
AND logique x && y AND logique booléen
OR logique x || y OR logique booléen

Classes

Classes (ou classes runtime) sont les plus fondamentales des types 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 d’héritage et polymorphisme , mécanismes permettant d’étendre et de spécialise r les classes dérivées .

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 mot clé runtimeclass, 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 n’est pas autorisée. Consultez classes de base.

Pour lier XAML à un modèle d’affichage, la classe runtime du modèle d’affichage doit être définie dans MIDL. Consultez contrôles XAML ; liez à une propriété C++/WinRT pour plus d’informations.

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

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

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

Vous pouvez indiquer qu’une définition de classe est incomplète en préfixant la définition de classe runtime avec le mot clé partial. 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.
partiel La définition de classe est incomplète.

Consultez de composition et d’activation pour les modificateurs avancés.

Modificateurs d’accès aux membres

Comme 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 une 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 pas ni n’autorise pas le mot clé (effectivement 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 la même que la dérivation du type Object (en d’autres termes, à partir de IInspectable).

Note

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

Toute classe runtime que vous définissez dans l’application que dérive d’une classe de base est appelée classe de composable. Et il existe des contraintes autour des classes composables. Pour qu’une application passe le kit de certification des applications Windows tests utilisés par Visual Studio et par le Microsoft Store pour valider les soumissions (et par conséquent, pour que l’application soit ingérée avec succès dans le Microsoft Store), une classe composable doit finalement dériver d’une classe de base Windows. Cela signifie que la classe à la racine même de la hiérarchie d’héritage doit être un type provenant d’un espace de noms Windows.* .

Consultez contrôles XAML ; liez à une propriété C++/WinRT pour plus d’informations.

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

Note

Ici, zone et volume sont définis dans le même fichier source. Pour une discussion sur les avantages et les inconvénients, consultez 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, à l’exception des constructeurs de la classe de base. Une classe dérivée peut ajouter de nouveaux membres à ceux qu’il hérite, mais elle ne peut pas supprimer la définition d’un membre hérité.

Dans l’exemple précédent, Volume hérite des propriétés Height et Width deZone . Ainsi, chaque instance Volume contient trois propriétés : Height, Widthet Depth.

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 Area et 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 sous la forme d’une 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 IEquatable hypothétique.

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. Vous devez bien sûr les déclarer et les définir sur l’implémentation réelle.

Membres

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

Ce tableau affiche 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éthode Calculs et actions qui peuvent être effectués par une instance de la classe ou par la classe elle-même
Épreuves 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 conteneur.

Les constructeurs d’instance peuvent être surchargés. Par exemple, la classe de test ci-dessous déclare trois constructeurs d’instances ; un avec aucun paramètre (le constructeur par défaut ), celui qui prend un paramètre Int32 et l’autre qui accepte deux paramètres Double (constructeurs paramétrés).

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és. Les constructeurs d’instance ne sont pas hérités (à une exception près) et une classe n’a pas de constructeurs d’instance autres que ceux réellement déclarés 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 de fabrique 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

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 désignent pas les emplacements de stockage. Au lieu de cela, les propriétés ont 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é get et/ou un mot clé set écrit entre les délimiteurs { et }, et se terminant par un point-virgule.

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

Par exemple, la classe Area, 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 mots clés get et set. L’omission implique que la propriété est en lecture-écriture et est sémantiquement identique à fournir les mots clés get et set dans cet ordre —get, suivi de set.

En outre, vous pouvez spécifier uniquement le mot clé get 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; };

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

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

Si vous souhaitez par la suite 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 Area (par exemple, la classe Area est un type dans une application que vous recompilez à chaque fois), vous pouvez simplement ajouter le mot clé à la déclaration SurfaceColor existante.

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

Si, d’autre part, vous 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 mot clé set à la déclaration de propriété existante. Cela modifie l’interface binaire de votre classe.

Dans ce cas, ajoutez la propriété set mot clé à 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é ( getter de propriété).

Un accesseur set correspond à une méthode avec un paramètre unique nommé valeur, et aucun type de retour , le jeu de propriétés.

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 préfixe static modificateur, et les propriétés d’instance sont déclarées sans elle.

Méthode

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 variables 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 substitué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 n’est accessible que par les membres d’une classe dérivée ultérieurement.

Surcharge de méthode

La méthode surcharge 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 des arité différentes).

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

Note

Toutes les méthodes portant le même nom doivent avoir des arité différentes. Cela est dû au fait que les langages de programmation faiblement typés ne prennent pas en charge la surcharge par type.

Paramètres

Paramètres sont utilisés pour transmettre des valeurs ou des références de variables à une méthode. Un paramètre décrit un emplacement avec un type et un nom, et éventuellement un mot clé de 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 paramètres d’entrée, c’est-à-dire qu’ils sont marshalés de l’appelant vers l’appelé uniquement. Les mots clés de modificateur ref, 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 . Toutefois, 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) a des concepts et des mots clés de modificateur 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 Windows Runtime.

Les types valeur sont implicitement paramètres d’entrée, 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 mot clé out ; dans ce cas, l’argument est marshalé à la place de l’appelé vers l’appelant uniquement.

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. Cela est obtenu avec le mot clé ref const (pasconst 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 lorsque vous acceptez une valeur aussi importante qu’une Matrix4x4, par exemple.

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

Les types de 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 effectuer un paramètre de sortie avec le mot clé out. Dans ce cas, les rôles sont inversés ; l’appelé est celui qui alloue l’objet et le renvoie à l’appelant. Là encore, les mots clés ref ne peuvent pas être utilisés en général avec des types de 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 Visiteur (aucun) Outre Comportement par défaut
ref const Struct uniquement Optimisation des performances
Paramètre de sortie Appelé out Outre

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 de 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). C’est ce que l’on appelle le modèle de du tableau de passe. Vous pouvez également utiliser le modèle tableau de remplissage en ajoutant le 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 du tableau. Enfin, le dernier modèle est la 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 aux éléments par appelé
« Pass array » (aucun) Visiteur Lecture seule
« Tableau de remplissage » ref Visiteur É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 modificateur de static préfixé est une méthode statique . Une méthode statique n’a aucun 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 modificateur de static 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 possède à la fois des membres statiques et d’instance.

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

Chaque instance Entity contient son propre numéro de série (et probablement d’autres informations qui ne sont pas affichées ici). En interne, l’entité Entity constructeur (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 permet d’accéder au numéro de série de l’instance sur laquelle vous appelez la propriété obtenir méthode.

Les méthodes statiques GetNextSerialNo et SetNextSerialNo peuvent accéder au numéro de série interne prochain numéro de série disponible membre statique 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 d’exécution de l’instance pour laquelle cet appel a lieu détermine l’implémentation réelle de la 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 modificateur overridable, la méthode peut être substitué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 modificateur protected, la méthode est visible uniquement pour les classes dérivées.

Épreuves

Un événement déclaration 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 mot clé event, suivi du nom de 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 ajouter méthode, qu’un client appelle pour ajouter un gestionnaire d’événements à la source et une supprimer méthode, 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 aucune charge utile significative, les arguments d’événement sont un Object 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 et un type de retour particulier. Une instance unique d’un événement peut contenir n’importe quel nombre de références à des 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 mot clé delegate.

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 de 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 une WindowSizeChangedEventArgs), et retourne void. Une fois que nous avons discuté structs, vous pourrez également remplacer le paramètre WindowSizeChangedEventArgs par un argument d’événement type de votre choix.

Une propriété intéressante et utile d’un délégué est qu’il ne sait pas ou ne s’intéresse pas à 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 délégués retournant HRESULT.

Structs

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

Les structs sont particulièrement utiles pour les petites structures de données qui ont une sémantique de valeur. 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 effectuées par une application.

Utilisons un exemple pour contraster les classes et les structs. Voici une version de Point d’abord en tant quede 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 à rendre Point un struct, au lieu d’une classe.

struct Point
{
    Int32 x;
    Int32 y;
};

À présent, un seul objet est instancié : l’objet de 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 exiger 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 les deux ITextBox et IListBox nécessitent il.

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 les types Windows Runtime 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 le moment où 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 polymorphes) via cette interface commune.

Note

Réfléchissez deux fois à l’utilisation du mot clé requires dans MIDL 3.0. Cela 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.

Windows Runtime prend en charge deux types d’énumérations : énumérations normales et indicateurs énumérations. Une énumération du type normal exprime un ensemble de valeurs exclusives ; tandis qu’un des 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++.

Une énumération 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 n’importe quel 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 d’énumération 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 de 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 indiqué 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 de 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 de UInt32 .

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

Attributs

Les types, membres et autres entités du 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 attributs.

L’exemple suivant définit un attribut HelpAttribute, qui peut être placé sur les 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 contient uniquement 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_delegate, target_enum, target_event, target_field, target_interface, target_method, target_parameter, target_property, target_runtimeclasset target_struct. Vous pouvez inclure plusieurs cibles entre parenthèses, séparées par des virgules.

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

Types paramétrables

L’exemple ci-dessous génère erreur MIDL2025 : [msg]syntaxe error [context] : s’attendre à > 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 erreur MIDL2025 : [msg]syntax error [context] : attendez > 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 Retour d’un tableau de manière asynchrone.