Introdução à Linguagem de Definição de Interface da Microsoft 3.0

A Linguagem de Definição de Interface da Microsoft (MIDL) 3.0 é uma sintaxe simplificada e moderna para definir tipos do Windows Runtime dentro de arquivos IDL (Interface Definition Language) (arquivos.idl). Essa nova sintaxe se sentirá familiar para qualquer pessoa com experiência com C, C++, C#e/ou Java. MIDL 3.0 é uma maneira particularmente conveniente de definir classes de runtime C++/WinRT, sendo dramaticamente mais concisa do que as versões anteriores do IDL (reduzindo os designs em dois terços de comprimento e usando padrões razoáveis para reduzir a necessidade de decorar com atributos).

Veja a aparência de MIDL 3.0; este exemplo demonstra a maioria dos elementos de sintaxe de linguagem que você provavelmente usará.

// 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.
    }
}

Observe que a sintaxe de MIDL 3.0 é especificamente e projetada exclusivamente para definindo tipos de. Você usará uma linguagem de programação diferente para implementar esses tipos. Para usar o MIDL 3.0, você precisará do SDK do Windows versão 10.0.17134.0 (Windows 10, versão 1803) (midl.exe versão 8.01.0622 ou posterior, usada com a opção /winrt).

Nota

Consulte também a referência consolidada do Windows Runtime (o sistema de tipos do Windows Runtimee arquivos de metadados do Windows).

MIDL 1.0, 2.0 e 3.0

A IDL (Interface Definition Language) começou com o sistema DCE/RPC (Distributed Computing Environment/Remote Procedure Calls). O MIDL 1.0 original é a IDL DCE/RPC com aprimoramentos para definir interfaces e coclasses COM.

Uma sintaxe MIDL 2.0 atualizada (também conhecida como MIDLRT) foi desenvolvida na Microsoft para declarar APIs do Windows Runtime para a plataforma Windows. Se você procurar na pasta do SDK do Windows %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt, verá exemplos de arquivos .idl que são gravados com a sintaxe MIDL 2.0. Essas são APIs internas do Windows Runtime, declaradas em seu formulário de ABI (interface binária de aplicativo). Esses arquivos existem principalmente para ferramentas a serem usadas– você não criará nem consumirá essas APIs nesse formulário (a menos que você esteja escrevendo um código de nível muito baixo).

Consulte também Transição para MIDL 3.0 doMIDLRT clássico.

MIDL 3.0 é uma sintaxe muito mais simples e moderna, cuja finalidade é declarar APIs do Windows Runtime. E você pode usá-lo em seus projetos, especialmente para definir classes de runtime do C++/WinRT. Os cabeçalhos, para uso do C++/WinRT, para as APIs internas do Windows Runtime fazem parte do SDK, dentro da pasta %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt.

Casos de uso para MIDL 3.0

Em geral, todas as APIs do Windows Runtime foram projetadas para estar disponíveis para todas as projeções de linguagem do Windows Runtime. Isso é feito, em parte, optando por passar exclusivamente tipos do Windows Runtime de e para APIs do Windows Runtime. Embora seja uma decisão de design válida passar uma interface COM bruta de e para uma API do Windows Runtime, isso limita os consumidores dessa API do Windows Runtime específica a aplicativos C++. A técnica pode ser vista em cenários de interoperação, por exemplo, ao interoperar entre Direct3D e XAML. Como o Direct3D está na imagem, o cenário é necessariamente restrito a aplicativos C++. Portanto, uma API que requer uma interface COM não impõe nenhuma limitação adicional além do que é inerente. Por exemplo, um aplicativo C++ pode obter um ponteiro de interface IDXGISwapChain e, em seguida, passá-lo para o método ISwapChainPanelNative::SetSwapChain. Um aplicativo C#, por exemplo, não seria capaz de obter um IDXGISwapChain para começar, portanto, ele não seria capaz de usar esse método por esse motivo. Essas exceções relacionadas à interoperabilidade residem em cabeçalhos de interoperabilidade, como windows.ui.xaml.media.dxinterop.h.

Se houver recursos ou funcionalidade de um componente COM que você deseja expor às projeções de linguagem do Windows Runtime além do C++, em seguida, você pode criar um componente do C++ Windows Runtime (WRC) que cria e usa diretamente o componente COM (como DirectX, por exemplo) e expõe uma replicação de alguns subconjuntos de seus recursos e funcionalidade na forma de uma superfície de API do Windows Runtime que usa e retorna o Windows Somente tipos de runtime. Em seguida, você pode consumir esse WRC de um aplicativo escrito em qualquer projeção de linguagem Windows Runtime.

Estrutura de definição e midl.exe de chamada da linha de comando

Os principais conceitos organizacionais em uma definição midl 3.0 são namespaces, tipos e membros. Um arquivo de origem MIDL 3.0 (um arquivo .idl) contém pelo menos um namespace, dentro do qual estão tipos e/ou namespaces subordinados. Cada tipo contém zero ou mais membros.

  • Classes, interfaces, estruturas e enumerações são tipos.
  • Métodos, propriedades, eventos e campos são exemplos de membros.

Quando você compila um arquivo de origem MIDL 3.0, o compilador (midl.exe) emite um arquivo de metadados do Windows Runtime (normalmente um arquivo de .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);
    }
}

Como o namespace de um tipo do Windows Runtime se torna parte do nome do tipo, o exemplo acima define uma classe de runtime chamada Bookstore.BookSku. Não há nenhuma maneira independente de linguagem de expressar BookSku sem expressar também o namespace.

Essa classe implementa o interface de Windows.UI.Xaml.Data.INotifyPropertyChanged. E a classe contém vários membros: dois construtores, uma propriedade de leitura/gravação (Price), algumas propriedades somente leitura (AuthorName por meio de Title) e dois métodos, chamados Equals e ApplyDiscount. Observe o uso do tipo único em vez de float. E esse de cadeia de caracteres de tem um "S" maiúscula.

Ponta

O Visual Studio oferece a melhor experiência para compilar o MIDL 3.0, por meio da VSIX (Extensão do Visual Studio) do C++/WinRT. Consulte suporte do Visual Studio para C++/WinRT e o vsix.

Mas você também pode compilar MIDL 3.0 na linha de comando. Se o código-fonte deste exemplo for armazenado em um arquivo chamado Bookstore.idl, você poderá emitir o comando abaixo. Se necessário para seu caso, você pode atualizar o número de versão do SDK usado no comando (que é 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

A ferramenta midl.exe compila o exemplo e produz um arquivo de metadados chamado Bookstore.winmd (por padrão, o nome do arquivo .idl é usado).

Ponta

Se você usar mais de um arquivo IDL (para obter conselhos sobre isso, consulte Classes de runtime de fatoramento em arquivos Midl (.idl)), mesclar todos os arquivos .winmd resultantes em um único arquivo com o mesmo nome do namespace raiz. Esse arquivo de .winmd final será aquele que os consumidores de suas APIs referenciarão.

Nesse caso, BookSku é a única classe de runtime no namespace Bookstore, portanto, salvamos uma etapa e acabamos de nomear o arquivo para o namespace.

Aliás, você pode usar o comando where para descobrir onde midl.exe está instalado.

where midl

Se você quiser usar os tipos definidos em um arquivo .idl de um arquivo de .idl diferente, use a diretiva import. Para obter mais detalhes e um exemplo de código, consulte controles XAML; associar a uma propriedade C++/WinRT. Claro, se você estiver consumindo um componente interno ou de terceiros, não terá acesso ao arquivo .idl. Por exemplo, talvez você queira consumir o Win2D API do Windows Runtime para renderização imediata de elementos gráficos 2D. O comando acima usou a opção /reference para fazer referência a um arquivo de metadados do Windows Runtime (.winmd). Neste próximo exemplo, usaremos essa opção novamente, imaginando o cenário em que temos Bookstore.winmd, mas não Bookstore.idl.

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

Se o código-fonte do exemplo acima for armazenado em um arquivo chamado MVVMApp.idl, você poderá emitir o comando abaixo para referenciar 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

Namespaces

Um namespace é necessário. Ele prefixa o nome de todos os tipos definidos no escopo do bloco de namespace com o nome do namespace. Um namespace também pode conter declarações de namespace subordinadas. O nome dos tipos definidos em um escopo de namespace subordinado tem um prefixo de todos os nomes de namespace que contêm.

Os exemplos a seguir são duas maneiras de declarar o mesmo classe Windows.Foundation.Uri (como você pode ver, os períodos separam os níveis de namespaces aninhados).

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

Aqui está outro exemplo mostrando que é legal declarar namespaces e seus tipos de forma aninhada.

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

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

Mas é uma prática mais comum fechar o namespace anterior e abrir um novo, assim.

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

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

Tipos

Há dois tipos de tipos de dados em MIDL 3.0: tipos de valor e tipos de referência. Uma variável de um tipo de valor contém diretamente seus dados. Uma variável de um tipo de referência armazena uma referência a seus dados (essa variável também é conhecida como um objeto ).

É possível que duas variáveis de tipo de referência referenciem o mesmo objeto. Assim, uma operação em uma variável afeta o objeto referenciado pela outra variável. Com tipos de valor, cada uma das variáveis tem sua própria cópia dos dados e não é possível que uma operação em um afete a outra.

Os tipos de valor midl 3.0 são divididos ainda mais em tipos simples, tipos de enumeração, tipos de struct e tipos anuláveis.

Os tipos de referência midl 3.0 são divididos ainda mais em tipos de classe, tipos de interface e tipos de delegado.

Aqui está uma visão geral do sistema de tipos midl 3.0. Ao contrário das versões anteriores do MIDL, você não pode usar aliases para esses tipos.

Categoria Descrição
Tipos de valor Tipos simples Integral assinado: Int16, Int32, Int64
Integral não assinado: UInt8, UInt16, UInt32, UInt64
Caracteres Unicode: char (representa um UTF-16LE; uma unidade de código Unicode de 16 bits)
Cadeias de caracteres Unicode: de cadeia de caracteres
Ponto flutuante IEEE: Único, Duplo
Booliano: booliano
UUID de 128 bits: Guid
Tipos de enumeração Tipos definidos pelo usuário do formulário enumeração E {...}
Tipos de struct Tipos definidos pelo usuário do formulário struct S {...}
Tipos que permitem valor nulo Extensões de todos os outros tipos de valor com um valor de nulo
Tipos de referência Tipos de classe Classe base final de todos os outros tipos: Object
Tipos definidos pelo usuário do formulário runtimeclass C {...}
Tipos de interface Tipos definidos pelo usuário da interface do formulário I {...}
Tipos de delegado Tipos definidos pelo usuário do formulário delegado <returnType> D(...)

Os sete tipos integrais fornecem suporte para dados sem sinal de 8 bits; e valores de 16 bits, 32 bits e 64 bits no formulário assinado ou sem sinal.

Os dois tipos de ponto flutuante, formatos IEEE 754 de precisão única e de precisão única de 64 bitse IEEE 754 de precisão dupla de 32 bits, respectivamente.

O tipo de booliano de midl 3.0 representa valores boolianos; ou .

Caracteres e cadeias de caracteres em MIDL 3.0 contêm caracteres Unicode. O tipo char representa uma unidade de código UTF-16LE; e o tipo String representa uma sequência de unidades de código UTF-16LE.

A tabela a seguir resume os tipos numéricos do MIDL 3.0.

Categoria Bits Tipo Intervalo/precisão
Integral assinada 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
Integral sem sinal 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
Ponto flutuante 32 único 1,5 × 10-45 a 3,4 × 1038, precisão de 7 dígitos
64 dupla 5.0 × 10-324 a 1,7 × 10308, precisão de 15 dígitos

Os arquivos de origem MIDL 3.0 usam definições de tipo para criar novos tipos. Uma definição de tipo especifica o nome e os membros do novo tipo. Essas categorias de tipo MIDL 3.0 são definíveis pelo usuário.

  • tipos de atributo,
  • tipos de struct,
  • tipos de interface,
  • tipos de runtimeclass,
  • tipos de delegado e
  • tipos de enumeração.

Um tipo de de atributo define um atributo do Windows Runtime que pode ser aplicado a outras definições de tipo. Um atributo fornece metadados sobre o tipo ao qual o atributo é aplicado.

Um tipo struct define uma estrutura do Windows Runtime que contém membros de dados (campos). Os structs são tipos de valor e não exigem alocação de heap. Um membro de dados de um tipo de struct deve ser um tipo de valor ou um tipo anulável. Os tipos de struct não dão suporte à herança.

Um tipo de interface define uma interface do Windows Runtime, que é um conjunto nomeado de membros da função. Uma interface pode especificar que uma implementação da interface também deve implementar uma ou mais interfaces adicionais (necessárias) especificadas. Cada tipo de interface deriva diretamente da interface de IInspectable do Windows Runtime.

Um tipo de de runtimeclass define uma classe do Windows Runtime (classe de runtime). Uma classe de runtime contém membros que podem ser propriedades, métodos e eventos.

Um delegado tipo define um delegado do Windows Runtime, que representa uma referência a um método com uma lista de parâmetros específica e um tipo de retorno. Os delegados possibilitam tratar um método como uma entidade que pode ser passada como um parâmetro. Um delegado é semelhante ao conceito de um ponteiro de função encontrado em algumas outras linguagens. Ao contrário dos ponteiros de função, os delegados são orientados a objetos e são type-safe.

Um tipo de de enumeração é um tipo distinto com constantes nomeadas. Cada tipo de enumeração tem um tipo subjacente implícito; Int32 ou UInt32. O conjunto de valores de um tipo de enumeração é o mesmo que o conjunto de valores do tipo subjacente.

O MIDL 3.0 dá suporte a três categorias de tipo adicionais.

  • tipos de matriz unidimensional,
  • tipos de valor anuláveis e
  • o tipo objeto .

Você não precisa declarar uma matriz unidimensional antes de usá-la. Em vez disso, os tipos de matriz são construídos seguindo um nome de tipo com colchetes. Por exemplo, Int32[] é uma matriz unidimensional de Int32.

Da mesma forma, tipos de valor de anuláveis também não precisam ser definidos antes que possam ser usados. Para cada tipo de valor não anulável T (exceto String), há um tipo anulável correspondente Windows.Foundation.IReference<T>, que pode conter o valor adicional null. Por exemplo, Windows.Foundation.IReference<Int32> é um tipo que pode conter qualquer inteiro de 32 bits ou o valor null. Consulte também IReference<T>.

Por fim, o MIDL 3.0 dá suporte ao tipo Object, que é mapeado para a interface IInspectable do Windows Runtime. A interface e tipos de referência de runtimeclass derivam conceitualmente do tipo do objeto ; representante não.

Expressões em um valor enumerado

Com MIDL 3.0, você só pode usar uma expressão na definição do valor das constantes nomeadas de um tipo enumerado; em outras palavras, em um inicializador de enumeração.

Uma expressão é construída a partir de operandos e operadores . Os operadores em uma expressão indicam quais operações aplicar aos operandos. Exemplos de operadores incluem +, -, *, /e new. Exemplos de operandos incluem literais, campos, variáveis locais e expressões.

Quando uma expressão contém vários operadores, a precedência dos operadores controla a ordem na qual os operadores individuais são avaliados. Por exemplo, a expressão x + y * z é avaliada como x + (y * z) porque o operador * tem precedência maior que o operador +. As operações lógicas são de precedência inferior às operações bit a bit.

A tabela a seguir resume os operadores midl 3.0, listando as categorias de operador em ordem de precedência da mais alta para a mais baixa. Operadores na mesma categoria têm precedência igual.

Categoria Expressão Descrição
Primário x++ Pós-incremento
x-- Pós-decremento
Unário +x Identidade
-x Negação
!x Negação lógica
~x Negação bit a bit
++x Pré-incremento
--x Pré-decremento
Multiplicativo x * y Multiplicação
x/y Divisão
x % y Restante
Aditivo x + y Adição, concatenação de cadeia de caracteres, combinação de delegado
x – y Subtração, remoção de delegado
Turno x << y Deslocar para a esquerda
x >> y Deslocar para a direita
AND bit a bit x & y INteger bit a bit AND
XOR bit a bit x ^ y XOR bit a bit inteiro
OR bit a bit x | y INteger bit a bit OR
AND lógico x && y AND lógico booliano
OR lógico x || y OR lógico booliano

Classes

classes (ou classes de runtime) são os tipos mais fundamentais do MIDL 3.0. Uma classe é uma definição de uma agregação de métodos, propriedades e eventos em uma única unidade. As classes dão suporte a de herança e de polimorfismo — mecanismos em que classes derivadas podem estender e especializar classes base.

Você define um novo tipo de classe usando uma definição de classe. Uma definição de classe começa com um cabeçalho que especifica a palavra-chave runtimeclass, o nome da classe, a classe base (se fornecida) e as interfaces implementadas pela classe. O cabeçalho é seguido pelo corpo da classe, que consiste em uma lista de declarações de membro escritas entre os delimitadores { e }.

Aqui está uma definição de uma classe simples chamada Area.

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

    Int32 Height;
    Int32 Width;

    static Int32 NumberOfAreas { get; };
}

Isso define uma nova classe do Windows Runtime chamada Area, que tem um construtor que usa dois parâmetros de Int32 , dois int32 propriedades de leitura/gravação denominadas Height e Widthe uma propriedade somente leitura estática chamada NumberOfAreas.

Por padrão, uma runtimeclass é selada e a derivação dela não é permitida. Consulte classes base.

Para associar XAML a um modelo de exibição, a classe de runtime do modelo de exibição precisa ser definida em MIDL. Consulte controles XAML; associar a uma propriedade C++/WinRT para obter mais detalhes.

Você pode declarar que uma classe não dá suporte a instâncias (e, consequentemente, deve conter apenas membros estáticos) prefixando a definição de classe de runtime com a palavra-chave static. Adicionar um membro não estático à classe causa um erro de compilação.

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

Uma classe estática é diferente de uma classe vazia. Veja também classes vazias.

Você pode indicar que uma definição de classe está incompleta prefixando a definição de classe de runtime com a palavra-chave partial. Todas as definições parciais de classe encontradas pelo compilador são combinadas em uma única classe de runtime. Esse recurso é principalmente para cenários de criação XAML, em que algumas das classes parciais são geradas por computador.

Modificador Significado
estático A classe não tem instâncias. Consequentemente, somente membros estáticos são permitidos.
parcial A definição de classe está incompleta.

Consulte de composição e ativação para modificadores avançados.

Modificadores de acesso de membro

Como MIDL 3.0 é uma linguagem de definição para descrever a superfície pública dos tipos do Windows Runtime, não há necessidade de sintaxe explícita para declarar a acessibilidade pública de um membro. Todos os membros são implicitamente públicos. É por isso que MIDL 3.0 não requer nem permite a palavra-chave (efetivamente redundante) public.

Classes base

Uma definição de classe pode especificar uma classe base seguindo o nome da classe e os parâmetros de tipo com dois-pontos e o nome da classe base. Omitir uma especificação de classe base é o mesmo que derivar do tipo Object (em outras palavras, de IInspectable).

Nota

Suas classes de modelo de exibição, na verdade, qualquer classe de runtime que você definir em seu aplicativo não precisam derivar de uma classe base.

Qualquer classe de runtime definida no aplicativo que faz derivar de uma classe base é conhecida como uma classe composável. E há restrições em torno de classes composáveis. Para que um aplicativo passe o o Kit de Certificação de Aplicativos do Windows testes usados pelo Visual Studio e pela Microsoft Store para validar envios (e, portanto, para que o aplicativo seja ingerido com êxito na Microsoft Store), uma classe composável deve, em última análise, derivar de uma classe base do Windows. O que significa que a classe na raiz da hierarquia de herança deve ser um tipo originário em um namespace do Windows.* .

Consulte controles XAML; associar a uma propriedade C++/WinRT para obter mais detalhes.

No próximo exemplo, a classe base de Volume é de Área e a classe base de Area é 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;
}

Nota

Aqui, de Área e de Volume são definidos no mesmo arquivo de origem. Para obter uma discussão sobre os prós e contras, consulte Classes de runtime de fatoramento em arquivos midl (.idl).

Uma classe herda os membros de sua classe base. Herança significa que uma classe contém implicitamente todos os membros de sua classe base, exceto os construtores da classe base. Uma classe derivada pode adicionar novos membros àqueles herdados, mas não pode remover a definição de um membro herdado.

No exemplo anterior, de Volume herda as propriedades de Altura e Largura do Area. Portanto, cada instância volume contém três propriedades: Height, Widthe Depth.

Em geral, as regras de resolução de tipo exigem que um nome de tipo seja totalmente qualificado quando referenciado. Uma exceção é quando o tipo foi definido no mesmo namespace que o tipo atual. O exemplo acima funciona como escrito se de Área e Volume estiverem no mesmo namespace.

Interfaces implementadas

Uma definição de classe também pode especificar uma lista de interfaces que a classe implementa. Especifique as interfaces como uma lista separada por vírgulas de interfaces após a classe base (opcional).

No exemplo a seguir, a classe Area implementa a interface IStringable; e a classe Volume implementa IStringable e a hipotética interface de 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;
}

No MIDL, você não declara os membros da interface na classe. É claro que você precisa declarar e defini-los na implementação real.

Membros

Os membros de uma classe são membros estáticos ou membros da instância . Um membro estático pertence a uma classe. Um membro de instância pertence a um objeto (ou seja, uma instância de uma classe).

Esta tabela mostra os tipos de membro que uma classe pode conter.

Tipo de membro Descrição
Construtores Ações necessárias para inicializar uma instância da classe ou inicializar a própria classe
Propriedades Ações associadas à leitura e gravação de propriedades nomeadas de uma instância da classe ou da própria classe
Métodos Cálculos e ações que podem ser executadas por uma instância da classe ou pela própria classe
Eventos Notificações que podem ser geradas por uma instância da classe

Construtores

O MIDL 3.0 dá suporte à declaração de construtores de instância. Um construtor de instância é um método que implementa as ações necessárias para inicializar uma instância de uma classe. Os construtores podem não ser estáticos.

Um construtor é declarado como um método de instância (mas sem tipo de retorno) e com o mesmo nome da classe que contém.

Construtores de instância podem ser sobrecarregados. Por exemplo, a classe Test abaixo declara três construtores de instância; um sem parâmetros (o construtor padrão), um que usa um parâmetro int32 e outro que usa dois parâmetros double (construtores de parametrizados).

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

Para obter detalhes sobre a sintaxe das listas de parâmetros, consulte Métodos abaixo.

As propriedades, os métodos e os eventos da instância são herdados. Construtores de instância não são herdados (com uma exceção) e uma classe não tem construtores de instância diferentes daqueles declarados na classe. Se nenhum construtor de instância for fornecido para uma classe, você não poderá instanciar diretamente a classe. Para essa classe, normalmente você teria um método de fábrica em outro lugar que retorna uma instância da classe.

A exceção é classes não seladas. Uma classe não selada pode ter um ou mais construtores protegidos.

Propriedades

Propriedades são conceitualmente semelhantes aos campos (por exemplo, campos C#; ou campos de um struct MIDL 3.0). Propriedades e campos são membros com um nome e um tipo associado. No entanto, ao contrário dos campos, as propriedades não denotam locais de armazenamento. Em vez disso, as propriedades têm acessadores que especificam a função a ser executada quando você lê ou grava uma propriedade.

Uma propriedade é declarada como um campo de struct, exceto que a declaração termina com uma palavra-chave get e/ou uma palavra-chave set escrita entre os delimitadores { e }, e terminando em ponto e vírgula.

Uma propriedade que tem uma palavra-chave get e uma palavra-chave set é uma propriedade de leitura/gravação. Uma propriedade que tem apenas uma palavra-chave get é uma propriedade somente leitura. O Windows Runtime não dá suporte a propriedades somente gravação.

Por exemplo, a classe Area, vista anteriormente, contém duas propriedades de leitura/gravação chamadas Height e Width.

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

A declaração de Width omite as chaves e as palavras-chave get e set. A omissão implica que a propriedade é leitura-gravação e é semanticamente idêntica ao fornecimento das palavras-chave get e set nessa ordem,get, seguidas por set.

Além disso, você pode especificar apenas a palavra-chave get para indicar que a propriedade é somente leitura.

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

O Windows Runtime não dá suporte a propriedades somente gravação. Mas você pode especificar apenas a palavra-chave set para revisar uma propriedade somente leitura existente em uma propriedade de leitura/gravação. Veja esta versão do Area como exemplo.

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

Se você quiser, posteriormente, tornar a propriedade SurfaceColor leitura/gravação e não precisar manter a compatibilidade binária com definições anteriores de Area (por exemplo, a classe Area é um tipo em um aplicativo que você recompila todas as vezes), então você pode simplesmente adicionar a palavra-chave à declaração SurfaceColor existente como esta.

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

Se, por outro lado, você precisar de estabilidade binária (por exemplo, a classe Area é um componente em uma biblioteca que você envia aos clientes), então você não pode adicionar a palavra-chave set à declaração de propriedade existente. Isso altera a interface binária para sua classe.

Nesse caso, adicione a propriedade set palavra-chave a uma definição adicional da propriedade no final da classe como esta.

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

O compilador produz um erro para uma propriedade somente gravação. Mas não é isso que está sendo feito aqui. Devido à declaração anterior da propriedade como somente leitura, a adição da palavra-chave set não declara uma propriedade somente gravação, mas sim uma propriedade de leitura/gravação.

A implementação do Windows Runtime de uma propriedade é um ou dois métodos acessadores em uma interface. A ordem das palavras-chave get e set na declaração de propriedade determina a ordem dos métodos do acessador get e set na interface de backup.

O acessador get corresponde a um método sem parâmetros com um valor retornado do tipo de propriedade— o getter de propriedade.

Um acessador set corresponde a um método com um único parâmetro chamado valor, e nenhum tipo de retorno, o setter de propriedade.

Consequentemente, essas duas declarações produzem interfaces binárias diferentes.

Color SurfaceColor { get; set; };
Color SurfaceColor { set; get; };
Propriedades estáticas e de instância

Semelhante aos métodos, MIDL 3.0 dá suporte a propriedades de instância e propriedades estáticas. As propriedades estáticas são declaradas com o modificador static prefixado e as propriedades da instância são declaradas sem ele.

Métodos

Um método é um membro que implementa uma computação ou ação que pode ser executada por uma instância da classe ou pela própria classe. Um método estático é acessado por meio da classe. Um método de instância é acessado por meio de uma instância da classe.

Um método tem uma lista (possivelmente vazia) de parâmetros , que representam valores ou referências variáveis passadas para o método. Um método também tem um tipo de retorno , que especifica o tipo do valor computado e retornado pelo método. O tipo de retorno de um método será void se ele não retornar um valor.

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

A assinatura de um método deve ser exclusiva na classe na qual o método é declarado. A assinatura de um método consiste no nome do método, nos tipos de seus parâmetros e/ou no número de seus parâmetros. A assinatura de um método não inclui o tipo de retorno.

Modificadores de visibilidade do método

Um método pode ter um dos dois modificadores de visibilidade opcionais quando o método está presente em uma classe derivada.

O modificador substituível afirma que esse método pode ser substituído por um método (com o mesmo nome e assinatura) pertencente a uma subclasse.

O modificador protegido afirma que esse método só é acessível por membros em uma classe derivada posteriormente.

Sobrecarga de método

O método sobrecarga permite que vários métodos na mesma classe tenham o mesmo nome, desde que seus parâmetros diferem em número (em outras palavras, os métodos têm diferentes arity).

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

Nota

Todos os métodos com o mesmo nome devem terde aridade diferentes. Isso ocorre porque as linguagens de programação com tipo fraco não dão suporte à sobrecarga por tipo.

Parâmetros

Parâmetros são usados para passar valores ou referências de variável para um método. Um parâmetro descreve um slot com um tipo e um nome e, opcionalmente, uma palavra-chave modificadora. Um argumento é um valor real passado nesse slot do chamador do método para o destinatário do chamador.

Os parâmetros de um método obtêm seu valor do argumento específico especificado quando o método é invocado. A maneira como os argumentos são passados entre o chamador e o receptor depende do tipo do parâmetro. Por padrão, todos os parâmetros são parâmetros de entrada, ou seja, eles são empacotados do chamador apenas para o receptor. As palavras-chave do modificador ref, ref conste out podem ser adicionadas para modificar a direção padrão do marshaling entre o chamador e o receptor e criar parâmetros de saída . No entanto, nem todas as palavras-chave são válidas com todos os tipos de parâmetro; as combinações válidas são detalhadas abaixo.

Importante

O CLR (Common Language Runtime) tem conceitos e palavras-chave modificadoras que podem parecer semelhantes aos descritos nesta seção. No entanto, na prática, eles não estão relacionados e o efeito desses modificadores é específico para o design e o funcionamento do Windows Runtime.

Os tipos de valor são implicitamente parâmetros de entradae, por padrão, uma cópia do argumento é passada do chamador para o chamador. Parâmetros de valor podem ser transformados em parâmetros de saída com a palavra-chave out; nesse caso, o argumento é marshaled, em vez disso, do destinatário do chamador somente para o chamador.

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

Como uma otimização de desempenho especial, os tipos de struct (e nenhum outro tipo), que normalmente são passados por valor como uma cópia completa, podem ser feitos para serem passados por ponteiro para o struct imutável. Isso é obtido com a palavra-chave ref const (nãoconst ref), que marca o parâmetro struct como um parâmetro de entrada, mas instrui o marshaler a passar um ponteiro para o armazenamento do struct, em vez de passar uma cópia completa do struct. No entanto, observe que o struct é imutável; o ponteiro é conceitualmente um ponteiro const . Não há boxe envolvido. Essa é uma opção prática ao aceitar um valor tão grande quanto um Matrix4x4, por exemplo.

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

Os tipos de referência também são parâmetros de entrada implicitamente, o que significa que o chamador é responsável por alocar o objeto e passar uma referência a ele como argumento; no entanto, como o argumento é uma referência ao objeto, as modificações nesse objeto pelo receptor são observadas pelo chamador após a chamada. Como alternativa, um tipo de referência pode ser feito um parâmetro de saída com a palavra-chave out. Nesse caso, as funções são invertidas; o receptor é aquele que aloca o objeto e o retorna para o chamador. Novamente, as palavras-chave ref não podem ser usadas em geral com tipos de referência (consulte exceção abaixo).

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

A tabela a seguir resume o comportamento das palavras-chave de marshaling para parâmetros de valor e parâmetros de referência:

Comportamento Alocado por Palavra-chave Tipos Observações
Parâmetro de entrada Chamador (nenhum) Todos os tipos Comportamento padrão
ref const Somente struct Otimização de desempenho
Parâmetro de saída Receptor de chamada out Todos os tipos

O Windows Runtime dá suporte a tipos de matriz, cujo comportamento como parâmetro é um pouco diferente. Uma matriz é uma estrutura de dados que contém várias variáveis armazenadas sequencialmente e acessadas por meio de um índice. As variáveis contidas em uma matriz, também chamada de elementos da matriz, são todas do mesmo tipo e esse tipo é chamado de tipo de elemento da matriz.

O MIDL 3.0 dá suporte a declarações de uma matriz unidimensional.

Um parâmetro de matriz é um tipo de referência e, como todos os tipos de referência, é por padrão um parâmetro de entrada. Nesse caso, o chamador aloca a matriz para o receptor, que pode ler seus elementos, mas não pode modificá-los (somente leitura). Isso é chamado de padrão de de matriz de passagem . Como alternativa, a matriz de preenchimento padrão pode ser usada adicionando a palavra-chave ref ao parâmetro; nessa configuração, a matriz ainda é alocada pelo chamador, mas é conceitualmente um parâmetro de saída no sentido de que o receptor preencherá os valores dos elementos da matriz. Por fim, o último padrão é a matriz de recebimento em que (como todos os parâmetros de referência de saída) o destinatário está alocando e inicializando o argumento antes de ser retornado ao chamador.

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

A tabela a seguir resume o comportamento de matrizes e seus elementos:

Padrão de matriz Palavra-chave Alocado por Acesso de elementos por destinatário
"Matriz de passagem" (nenhum) Chamador Somente leitura
"Matriz de preenchimento" ref Chamador Somente gravação
"Matriz de recebimento" out Receptor de chamada Leitura/gravação

Para obter mais informações sobre como usar parâmetros de matriz de estilo C, também conhecidos como matrizes compatíveis, com C++/WinRT, consulte parâmetros de matriz.

Métodos estáticos e de instância

Um método declarado com um modificador static prefixado é um método estático . Um método estático não tem acesso a uma instância específica e, portanto, só pode acessar diretamente outros membros estáticos da classe.

Um método declarado sem um modificador de static é um método de instância . Um método de instância tem acesso a uma instância específica e pode acessar membros estáticos e de instância da classe.

A classe Entity a seguir tem membros estáticos e de instância.

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

Cada instância Entity contém seu próprio número de série (e, presumivelmente, algumas outras informações que não são mostradas aqui). Internamente, o construtor Entity (que é como um método de instância) inicializa a nova instância com o próximo número de série disponível.

A propriedade SerialNo fornece acesso ao número de série da instância na qual você invoca a propriedade obter método.

Os métodos estáticos GetNextSerialNo e SetNextSerialNo podem acessar o número de série próximo disponível interno membro estático da classe entidade .

Métodos substituíveis e protegidos

Todos os métodos em um tipo do Windows Runtime são efetivamente virtuais. Quando um método virtual é invocado, o tipo de tempo de execução da instância para a qual essa invocação ocorre determina a implementação do método real a ser invocada.

Um método pode ser substituído em uma classe derivada. Quando uma declaração de método de instância inclui um modificador overridable, o método pode ser substituído por classes derivadas. Se uma classe derivada realmente substitui um método de classe base substituível é determinado pela implementação; não está presente nos metadados. Se uma classe derivada redeclare um método na classe base, ele declara um novo método que se encontra ao lado do método de classe derivada, em vez de substituí-lo.

Quando uma declaração de método de instância inclui um modificador protected, o método fica visível apenas para classes derivadas.

Eventos

Um evento declaração é um membro que especifica que uma classe é uma fonte de evento. Essa fonte de evento fornece notificações a qualquer destinatário que implemente um delegado (um método com uma assinatura específica).

Você declara um evento usando a palavra-chave event, seguido pelo nome do tipo delegado (que descreve a assinatura de método necessária), seguido pelo nome do evento. Aqui está um evento de exemplo que usa um tipo de delegado existente da plataforma.

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

Uma declaração de evento adiciona implicitamente dois métodos à classe: uma adicionar método, que um cliente chama para adicionar um manipulador de eventos à origem e uma remover método, que um cliente chama para remover um manipulador de eventos adicionado anteriormente. Aqui estão mais exemplos.

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

Por convenção, dois parâmetros são sempre passados para um manipulador de eventos do Windows Runtime: a identidade do remetente e um objeto de argumentos de evento. O remetente é o objeto que gerou o evento ou nulo para eventos estáticos. Se o evento não tiver conteúdo significativo, os argumentos do evento serão um objeto cujo valor é nulo.

Delegados

Um tipo delegado especifica um método com uma lista de parâmetros específica e um tipo de retorno. Uma única instância de um evento pode conter qualquer número de referências a instâncias de seu tipo delegado. A declaração é semelhante à de um método de membro regular, exceto que ela existe fora de uma classe de runtime e é prefixada com a palavra-chave delegate.

Um delegado possibilita tratar métodos como entidades que podem ser atribuídas a variáveis e passadas como parâmetros. Os delegados são semelhantes ao conceito de ponteiros de função encontrados em algumas outras linguagens. Mas, ao contrário dos ponteiros de função, os delegados são orientados a objetos e são type-safe.

Se não quisermos usar o WindowSizeChangedEventHandler tipo delegado da plataforma, poderemos definir nosso próprio tipo de delegado.

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

Uma instância do nosso tipo delegado SizeChangedHandler pode referenciar qualquer método que usa dois argumentos (um objeto e um WindowSizeChangedEventArgs) e retorna nulo. Depois de discutirmos structs, você também poderá substituir o parâmetro WindowSizeChangedEventArgs por um args de evento tipo próprio.

Uma propriedade interessante e útil de um delegado é que ele não sabe ou se importa com a classe do método que ele faz referência; tudo o que importa é que o método referenciado tem os mesmos parâmetros e tipo de retorno que o delegado.

Opcionalmente, você pode atribuir uma declaração de delegado com [uuid(...)].

Consulte também Delegados retornandoHRESULT .

Structs

Um struct é uma estrutura de dados que pode conter membros de dados (campos). Mas, ao contrário de uma classe, um struct é um tipo de valor.

Os structs são particularmente úteis para estruturas de dados pequenas que têm semântica de valor. Números complexos ou pontos em um sistema de coordenadas são bons exemplos de structs. O uso de structs em vez de classes para estruturas de dados pequenas pode fazer uma grande diferença no número de alocações de memória executadas por um aplicativo.

Vamos usar um exemplo para contrastar classes e structs. Aqui está uma versão do Point primeiro como uma classe .

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

Este programa C# cria e inicializa uma matriz de 100 instâncias de Point. Com Point implementado como uma classe, 101 objetos separados são instanciados: um para o próprio objeto de matriz; e um para cada um dos 100 elementos de ponto de .

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

Uma alternativa mais eficaz é tornar Point um struct, em vez de uma classe.

struct Point
{
    Int32 x;
    Int32 y;
};

Agora, apenas um objeto é instanciado — o próprio objeto de matriz. Os elementos Point são armazenados na linha dentro da matriz; uma disposição de memória que os caches de processador são capazes de usar para um efeito poderoso.

Alterar um struct é uma alteração de quebra binária. Portanto, os structs implementados como parte do próprio Windows não são alterados uma vez introduzidos.

Interfaces

Uma interface define um contrato que pode ser implementado por classes. Uma interface pode conter métodos, propriedades e eventos, assim como classes.

Ao contrário de uma classe, uma interface não fornece implementações dos membros definidos por ela. Ele apenas especifica os membros que devem ser fornecidos por qualquer classe que implemente a interface.

As interfaces podem exigir uma classe que implementa a interface para também implementar outras interfaces. No exemplo a seguir, a interface IComboBox exige que qualquer classe que implemente IComboBox também implemente ITextBox e IListBox. Além disso, uma classe que implementa IComboBox também deve implementar IControl. Isso ocorre porque ITextBox e IListBox exigem .

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
{
    ...
}

Uma classe pode implementar zero ou mais interfaces. No próximo exemplo, a classe EditBox implementa IControl e IDataBound.

interface IDataBound
{
    void Bind(Binder b);
}

runtimeclass EditBox : IControl, IDataBound
{
}

Para tipos do Windows Runtime na plataforma Windows, uma interface é definida se os desenvolvedores que consomem esses tipos devem implementar a interface. Outro caso de uso para definir uma interface é quando várias classes de runtime implementam a interface e os desenvolvedores que consomem essas classes de runtime acessarão diferentes tipos de objeto genericamente (e, portanto, polimorficamente) por meio dessa interface comum.

Nota

Pense duas vezes sobre como usar a palavra-chave requires em MIDL 3.0. Isso pode levar a designs confusos, especialmente quando o controle de versão é levado em conta.

Enumerações

Um tipo de enumeração (ou tipo enumerado ou enumeração) é um tipo de valor distinto com um conjunto de constantes nomeadas. O exemplo a seguir define e usa um tipo de enumeração chamado Color com três valores constantes: Red, Greene Blue.

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

Cada tipo de enumeração tem um tipo integral correspondente chamado tipo subjacente do tipo enumerado. O tipo subjacente de uma enumeração é Int32 ou UInt32.

O Windows Runtime dá suporte a dois tipos de enumerações: enumerações de normais e sinalizadores enumerações. Uma enumeração do tipo normal expressa um conjunto de valores exclusivos; enquanto um dos sinalizadores representa um conjunto de valores boolianos. Para habilitar operadores bit a bit para uma enumeração de sinalizadores, o compilador MIDL 3.0 gera sobrecargas de operador C++.

Uma enumeração de sinalizadores tem o atributo [flags] aplicado. Nesse caso, o tipo subjacente da enumeração é UInt32. Quando o atributo [flags] não está presente (um enumeração normal), o tipo subjacente da enumeração é Int32. Não é possível declarar uma enumeração como qualquer outro tipo.

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

O formato de armazenamento de um tipo de enumeração e o intervalo de valores possíveis são determinados por seu tipo subjacente. O conjunto de valores que um tipo de enumeração pode assumir não é limitado por seus membros enumerados declarados.

O exemplo a seguir define um tipo de enumeração chamado Alignment, com um tipo subjacente de Int32.

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

Como também é verdadeiro para C e C++, uma enumeração MIDL 3.0 pode incluir uma expressão constante que especifica o valor do membro (como visto acima). O valor constante de cada membro de enumeração deve estar no intervalo do tipo subjacente da enumeração. Quando uma declaração de membro de enumeração não especifica explicitamente um valor, o membro recebe o valor zero (se for o primeiro membro no tipo de enumeração) ou o valor do membro enumerado anterior textuamente mais um.

O exemplo a seguir define um tipo de enumeração chamado Permissions, com um tipo subjacente de UInt32.

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

Atributos

Tipos, membros e outras entidades no código-fonte MIDL 3.0 dão suporte a modificadores que controlam determinados aspectos de seu comportamento. Por exemplo, a acessibilidade de um método é controlada usando o modificador de acesso protected. O MIDL 3.0 generaliza esse recurso de modo que os tipos de informações declarativas definidos pelo usuário possam ser anexados a entidades do programa e recuperados em tempo de execução dos metadados.

Os programas especificam essas informações declarativas adicionais definindo e usando atributos .

O próximo exemplo define um atributo HelpAttribute, que pode ser colocado em entidades do programa para fornecer links para a documentação associada. Como você pode ver, um atributo é essencialmente um tipo de struct, portanto, ele não tem um construtor e contém apenas membros de dados.

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

Um atributo pode ser aplicado dando seu nome, juntamente com quaisquer argumentos, dentro de colchetes pouco antes da declaração associada. Se o nome de um atributo terminar em Attribute, essa parte do nome poderá ser omitida quando o atributo for referenciado. Por exemplo, o atributo HelpAttribute pode ser usado assim.

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

Você pode aplicar o mesmo atributo a várias declarações usando um bloco de escopo seguindo o atributo. Ou seja, um atributo imediatamente seguido por chaves em torno das declarações às quais o atributo se aplica.

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

Os atributos implementados como parte do próprio Windows geralmente estão no namespace Windows.Foundation.

Conforme mostrado no primeiro exemplo, você usa o atributo [attributeusage(<target>)] na definição de atributo. Os valores de destino válidos são target_all, target_delegate, target_enum, target_event, target_field, target_interface, target_method, target_parameter, target_property, target_runtimeclasse target_struct. Você pode incluir vários destinos dentro dos parênteses, separados por vírgulas.

Outros atributos que você pode aplicar a um atributo são [allowmultiple] e [attributename("<name>")].

Tipos parametrizados

O exemplo a seguir produz erro MIDL2025: [msg]syntax error [context]: expecting > or, near ">>".

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

Em vez disso, insira um espaço entre os dois > caracteres para que o par de caracteres de fechamento de modelo não seja interpretado incorretamente como um operador de deslocamento à direita.

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

O exemplo a seguir produz erro MIDL2025: [msg]syntax error [context]: expecting > or, near "[". Isso ocorre porque é inválido usar uma matriz como um argumento de tipo de parâmetro para uma interface parametrizada.

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

Para a solução, consulte Retornando uma matriz de forma assíncrona.