Advanced topics, and shorthand(高级主题和速记)

简写

如果在未指定命名空间的情况下使用参数化类型,则 MIDL 3.0 编译器将在 Windows 中查找它。地基命名空间。 在实践中,这意味着您可以使用以下速记形式。

短版本 长版本
IIterable<T> Windows。Iiterable < T>
Iiterator < T> Windows。Iiterator < T>
IKeyValuePair<K, V> Windows。IKeyValuePair < K,V>
IMap<K, V> Windows。Foundation 集合。 IMap < K,V>
Imapchangedeventargs < K> Windows。Imapchangedeventargs < K>
IMapView<K, V> Windows。IMapView < K,V>
IObservableMap < K,V> Windows。IObservableMap < K,V>
IObservableVector < T> Windows。IObservableVector < T>
IVector<T> Windows。IVector < T>
IVectorView<T> Windows。IVectorView < T>
MapChangedEventHandler < K,V> Windows。MapChangedEventHandler < K,V>
VectorChangedEventHandler < T> Windows.Foundation.Collections.VectorChangedEventHandler<T>

此机制不适用于Windows。Foundation命名空间。 例如,必须将全名写入Windows。IAsyncAction

重载

重载方法和构造函数的默认行为是将数值后缀追加到接口中的第二个和后续重载的 ABI 名称。

[contract(Windows.Foundation.UniversalApiContract, 1)]
runtimeclass Sample
{
    // ABI name is "DoSomething"
    void DoSomething();

    // ABI name is "DoSomething2"
    void DoSomething(Int32 intensity);

    [contract(Windows.Foundation.UniversalApiContract, 2)]
    {
        // ABI name is "DoSomething" (new interface)
        void DoSomething(Int32 intensity, String label);
    }
}

此默认命名不符合建议的 API 设计准则,因此请使用 [method_name] 特性覆盖它。

[contract(Windows.Foundation.UniversalApiContract, 1)]
runtimeclass Sample
{
    void DoSomething();

    [method_name("DoSomethingWithIntensity")]
    void DoSomething(Int32 intensity);

    [contract(Windows.Foundation.UniversalApiContract, 2)]
    {
        [method_name("DoSomethingWithIntensityAndLabel")]
        void DoSomething(Int32 intensity, String label);
    }
}

实现非 exclusiveto 接口

从接口派生您的运行时类会自动声明该接口的成员。 不要重新声明它们。 如果执行此操作,则 MIDL 3.0 编译器假设要实现一个单独的方法 M () ,该方法可隐藏接口中的一个方法。

interface I
{
    void M();
}

runtimeclass C : I
{
    // Don't redeclare M(). It's automatically inherited from interface I.
    // void M();
}

指定默认接口

如果未指定默认接口,则 MIDL 3.0 编译器将选择第一个实例接口。 若要重写此选择,请在要作为默认接口的接口之前插入属性 [default]

// Declaring an external interface as the default
runtimeclass C : [default]I { ... }

// Declaring a specific exclusiveto interface as the default.
// This is very unusual.
runtimeclass C
{
    ...

    [default][interface_name(...)]
    {
        ...
    }
}

向后兼容性特性

如果要将 MIDL 1.0 或 MIDL 2.0 转换为 MIDL 3.0 (还请参阅 从经典 MIDLRT) 过渡到 midl 3.0 ,然后需要自定义通常自动生成的功能,使自动生成的值与现有值匹配。

  • 若要指定接口的名称和 UUID,请使用 [interface_name("fully.qualified.name", UUID)] 特性。
  • 若要指定工厂接口的名称和 UUID,请使用 [constructor_name("fully.qualified.name", UUID)] 特性。
  • 若要指定静态接口的名称和 UUID,请使用 [static_name("fully.qualified.name", UUID)] 特性。
  • 若要指定输出参数的名称,请使用 [return_name("name")] 特性。
  • 若要指定方法的名称,请使用 [method_name("name")] 特性。

constructor_namestatic_name 属性的 "UUID" 部分 interface_name 是可选的。 如果省略,则 MIDL 将自动生成 IID。

[contract(Windows.Foundation.UniversalApiContract, 1)]
[interface_name("ISample", ceb27355-f772-407c-9540-6467a7199bc7)]
[constructor_name("ISampleFactory", 863B201F-BC7B-471E-A066-6425E8E639EC)]
[static_name("ISampleStatics", 07254c86-3b01-4e24-b52b-14e832c15483)]
runtimeclass Sample
{
    [method_name("CreateWithIntensity")]
    Sample(Int32 intensity);

    static Boolean ShowConfigurationUI();

    [return_name("count")]
    Int32 GetCount();

    [constructor_name("ISampleFactory2", FEA29CEC-7768-41DE-9A46-CAAAA4622588)]
    [static_name("ISampleStatics2", 191235b5-a7b5-456f-86ea-abd1a735c6ab)]
    [interface_name("ISample2", d870ed2e-915a-48a2-ad17-c05efa123db7)]
    [contract(Windows.Foundation.UniversalApiContract, 2)]
    {
        [method_name("CreateWithIntensityAndLabel")]
        Sample(Int32 intensity, String label);

        static Boolean IsSupported();

        [return_name("success")]
        Boolean TrySomething();
    }
}

如果 xxx_name 你的注释混乱,MIDL 3.0 编译器不会发出警告。 例如,下面的示例在编译时不会出错,即使没有要放置在接口中的 interface_name 实例成员。 如果存在该 interface_name 属性,则会生成一个名为 ISampleFactory2 的空接口。

[contract(Windows.Foundation.UniversalApiContract, 1)]
[interface_name("ISample", ceb27355-f772-407c-9540-6467a7199bc7)]
runtimeclass Sample
{
    [return_name("count")]
    Int32 GetCount();

    // !WRONG! Should be constructor_name.
    [interface_name("ISampleFactory2", FEA29CEC-7768-41DE-9A46-CAAAA4622588)]
    [contract(Windows.Foundation.UniversalApiContract, 2)]
    {
        // MIDL will autogenerate ISampleFactory since there is no [constructor_name]
        Sample(Int32 intensity);
   }
}

空类

虽然这种用法有点难懂,但有时需要创作一个空类 (没有成员) 或空工厂类的类。 对于 EventArgs 类,会发生这种情况的一个常见示例。 如果引入了某个事件,有时不需要对该事件进行参数 (事件发出的事件不需要) 的其他上下文。 我们的 API 设计准则强烈建议提供 EventArgs 类,使类能够在将来添加新的事件参数。 不过,请考虑此空类。

runtimeclass MyEventsEventArgs
{
}

该类产生此错误。

error MIDL5056 : [msg]a runtime class without a default attribute cannot be used as a parameter. Runtime classes must have methods or be flagged as marker classes if they are used as a parameter [context]: Windows.Widgets.MyEventsEventArgs [ RuntimeClass 'Windows.Widgets.MyEventsEventArgs' ( Parameter 'result' ) ]

有多种方法可以解决此问题。 最简单的方法是使用 [default_interface] 属性表达,缺少方法是有意的,而不是编写错误。 下面是操作方法。

// An empty runtime class needs a [default_interface] tag to indicate that the 
// emptiness is intentional.
[default_interface] 
runtimeclass MyEventsEventArgs
{
}

解决方法的另一种方法是通过 [interface_name] 属性。 如果 MIDL 在不具有普通方法的类上遇到 [interface_name] (或) 没有正常方法的已进行版本控制的块,则会为该块生成一个空接口。 同样,如果 [static_name] 在类或版本控制的块上存在或 [constructor_name] 属性,但没有静态 (或构造函数) ,则它将为该静态接口或构造函数生成一个空接口。

请注意不要混淆空类和静态类。 虽然 (空类的实例,但不能) 的实例,但不能具有静态类的实例。

空接口

空接口 (也称为标记接口) 必须指定显式 [uuid(...)]

// An empty interface must specify an explicit [uuid] to ensure uniqueness.
[uuid("94569FA9-D3BB-4D01-BF7C-B8E1D8F8B30C")]
[contract(Windows.Foundation.UniversalApiContract, 1)]
interface ISomethingMarker
{
}

如果忘记,则会产生此错误。

error MIDL4010 : [msg]Cannot find the guid attribute of an interface or a delegate. [context]Windows.Widgets.ISomethingMarker

自动生成的 Uuid 是接口内容的哈希,但如果为空接口执行了此操作,则所有标记接口最终都将具有相同的 UUID。

作用域枚举

如果将命令开关传递 /enum_class 给 MIDL 3.0 编译器,则编译器发出的枚举将声明为 (enum 类) 的范围枚举。 不要对公共类型使用范围枚举。

撰写和激活

有关可 组合 类的详细信息,请参阅 XAML 控件; 绑定到 c + +/WinRT 属性

您可以指定 unsealed runtimeclass 以创建可组合的类。 此外,还可以指定 unsealed runtimeclass unsealed 以指示该类是使用 COM 聚合还是常规激活。 对于具有公共构造函数的基类,这一点非常重要。

解释错误消息

error MIDL2025: [msg]syntax error [context]: expecting > or, near ">>"

如果写入 IAsyncOperation<IVectorView<Something>>>> 则被解释为右移运算符。 若要解决此情况,请在两个大于号 IAsyncOperation<IVectorView<Something> > 之间添加一个空格。

error MIDL2025: [msg]syntax error [context]: expecting . near ","

如果指定了不存在的协定,可能是因为键入了错误,则会发生此错误。

[contract(Windows.Foundation.UniversalApiContact, 5)]
                                         ^^^^^^^ typo
error MIDL5082: [msg]the version qualifying an enum's field cannot be less than the version of the enum itself

此错误消息不仅针对错误消息中的原因生成,而且在您尝试将枚举的字段放入不同的协定中时也是如此。 使枚举的字段属于同一协定的不同版本是合法的,但它们不能完全在不同的协定中。

error MIDL5161: [msg]Invalid method parameter name [context]: Parameter 'result' (or 'operation' or 'value')

参数名称 resultoperation 在方法中保留。 参数名称 value 在构造函数中保留。

error MIDL5023: [msg]the arguments to the parameterized interface are not valid

检查您的接口名称拼写是否正确。

不要在单个接口内混合使用 MIDL 2.0 和 MIDL 3。0

每个接口和运行时类都必须是完全 MIDL 2.0 或完全 MIDL 3.0。 从 MIDL 2.0 运行时类引用 MIDL 3.0 接口 合法的。

如果尝试混合使用 MIDL 2.0 和 MIDL 3.0,则编译器会将整个实体视为 MIDL 2.0,这会导致编译器错误。 如果你打算使用 MIDL 3.0,则你可能会遇到意外的 "MIDL 2.0" 语法问题。

interface ICollapsible
{
    void Collapse();

    boolean IsCollapsed { get; } // WRONG!
 // ^^^^^^^ Lowercase "boolean" is MIDL 2.0.

    Boolean IsCollapsed { get; } // RIGHT!
 // ^^^^^^^ Uppercase "Boolean" is MIDL 3.0.
};

委托返回 HRESULT

返回 HRESULT 的委托是不明确的。 它可以是通常返回 void (的委托的典型 (预 MIDL 3.0) 声明,其中 HRESULT 用于传播异常) ,也可能是通常返回 HRESULT的委托的新式 (MIDL 3.0) 声明。

编译器通过查看声明的其他部分来解析多义性。 例如,如果用经典语法声明参数,则假定声明为经典。 如果用新式语法声明参数,则假定声明为新式声明。

delegate HRESULT AmbiguousDelegate(INT32 value, RuntimeClassName* r);
  • 参数使用经典语法,因此假定为典型声明。
  • 新式等效项是 delegate void AmbiguousDelegate(Int32 value, RuntimeClassName r);
delegate HRESULT AmbiguousDelegate(Int32 value, RuntimeClassName r);
  • 参数使用新式语法,因此假定为新式声明。
  • 典型的等效项是 delegate HRESULT AmbiguousDelegate(Int32 value, RuntimeClassName* r, [out, retval] HRESULT* result);

有时,参数列表不足以解析多义性。 例如,空参数列表,或仅包含枚举的参数列表在经典和新式语法中都是合法的。 在这种情况下,MIDL 3.0 编译器默认为经典。

delegate HRESULT AmbiguousDelegate(MyEnum e);
  • 解释为典型委托,其中委托通常返回 void,而 HRESULT 用于传播异常。
  • 如果确实需要返回 HRESULT的委托,则需要使用经典语法: delegate HRESULT AmbiguousDelegate(MyEnum e, [out, retval] HRESULT* result);

幸运的是,有一个通常返回 HRESULT的委托是罕见的。

JavaScript 和 Visual Basic 中的输出参数

有关输出参数的背景信息,请参阅 参数

JavaScript 使用与 out 大多数语言不同的参数投影方法。 如果方法的返回类型为 void,并且它具有单个 out 参数,则 out 方法返回参数。 否则,该方法将返回单个对象;该对象具有每个 out 参数的属性,以及返回值的另一个属性,如果不是 void) ,则 (。 在下面的示例中,方法调用返回的 JavaScript 对象具有一个名为 result的属性,另一个名为 " 余数" 的属性。

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

Visual Basic 不支持-仅支持 out 参数。 带 out 参数的方法由 Visual Basic ByRef 视为。