Advanced 主題和速記

縮寫

如果您在未指定命名空間的情況下使用參數型別,則 MIDL 3.0 編譯器會在 Windows 中尋找它。基礎. 集合命名空間。 在實務上,這表示您可以使用下列速記。

簡短版本 完整版本
IIterable<T> Windows。Iiterable < T>
Iiterator < T> Windows。Iiterator < T>
Inputiterator Windows。Inputiterator
IMap < K、V> Windows。基礎. 集合. 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。VectorChangedEventHandler < T>

這種機制並不適用于Windows。基礎命名空間。 例如,您必須撰寫 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.exe 根據) 轉換到 midl 3.0 ,您將需要自訂通常自動產生的專案,讓自動產生的值符合現有的值。

  • 若要指定介面的名稱和 UUID,請使用 [interface_name("fully.qualified.name", UUID)] 屬性。
  • 若要指定 factory 介面的名稱和 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);
   }
}

空白類別

雖然這種使用方式有點模糊,但有時候必須撰寫空白類別 (沒有成員) 的類別,或是空的 factory 類別。 這種情況的常見範例是使用 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 編譯器,則編譯器發出的列舉會宣告為範圍列舉, (列舉類別) 。 請勿針對公用類型使用範圍列舉。

撰寫和啟用

如需可 組合 類別的詳細資訊,請參閱 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 的委派不明確。 這可以是傳統的 (前置 MIDL 3.0) 委派的宣告,名義上會傳回 void (其中 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 參數都有一個屬性,再加上另一個傳回值的屬性 (if not 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 處理。