미리 정의된 특성(MIDL 3.0)

컴파일러 합성 인터페이스의 이름 및 인터페이스 식별자(IID)를 제어할 수 있는 미리 정의된 사용자 지정 특성이 많이 있습니다. 이러한 특성을 사용하면 세분화된 수준에서 클래스의 버전 관리 및 이진 API를 제어할 수 있습니다.

구성 요소 개발자 및/또는 라이브러리 작성자인 경우 이러한 특성을 사용하여 구성 요소가 한 버전에서 다음 버전으로 이진 안정적으로 유지되도록 할 수 있습니다.

애플리케이션 개발자인 경우 일반적으로 형식을 수정한 후 애플리케이션을 다시 컴파일하므로 이러한 특성을 사용할 필요가 없습니다.

[allowforweb] 특성

특성의 allowforweb 사용 및 용도에 대한 자세한 내용은 AllowForWebAttribute 클래스를 참조하세요.

[constructor_name] 특성

이 특성은 constructor_name 생성자 멤버를 포함하는 팩터리 인터페이스의 이름과 IID를 지정합니다. 팩터리 인터페이스에 대한 자세한 내용은 합성 인터페이스를 참조하세요.

참고

팩터리 인터페이스는 기본이 아닌 생성자가 있는 봉인된 클래스 또는 기본 및/또는 기본이 아닌 생성자가 있는 봉인되지 않은 클래스에만 적용됩니다.

아래 예제에서는 보호된 블록 생성자가 IBlockFactory 인터페이스에 배치되고 해당 인터페이스에 지정된 IID가 있습니다.

[constructor_name("Windows.UI.Xaml.Documents.IBlockFactory", 07110532-4f59-4f3b-9ce5-25784c430507)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
    ...
    protected Block();
    ...
}

따라서 클래스 설명이 인터페이스를 참조하지 않지만 클래스를 구현하는 데 필요한 경우 MIDL 3.0 컴파일러는 필요에 따라 인터페이스를 합성하고 추가합니다.

[contentproperty] 특성

이 특성은 contentpropertyContentPropertyAttribute 클래스를 나타냅니다. 예를 들면 다음과 같습니다.

// BgLabelControl.idl
namespace BgLabelControlApp
{
    [contentproperty("Content")]
    runtimeclass BgLabelControl : Windows.UI.Xaml.Controls.Control
    {
        BgLabelControl();
        static Windows.UI.Xaml.DependencyProperty LabelProperty{ get; };
        String Label;
        static Windows.UI.Xaml.DependencyProperty ContentProperty{ get; };
        IInspectable Content;
    }
}

[contract] 특성

고유한 API에서 특성을 사용하지 contract 마세요. 기본 제공 Windows API에 대해서만 의미가 있습니다.

특성은 contract 특성 형식 및/또는 멤버가 Windows 처음 도입된 Windows 10 API 계약의 이름 및 버전(확장 SDK가 있는 프로그래밍 참조)을 지정합니다(따라서 API가 Windows 일부로 전달되지 않는 경우 의미가 없음). 특성은 폼 [contract(ContractName, ContractVersion)]을 사용하고 적용할 항목 앞에 나타납니다.

[default] 특성

기본 인터페이스를 지정하지 않으면 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(...)]
    {
        ...
    }
}

[default_interface] 특성

default_interface 특성은 생성되지 않는 기본 인터페이스를 강제로 생성하는 데 사용됩니다. 아래 예제에서 StateTriggerBase에는 기본 인터페이스가 필요하지 않으므로(공용 비정적 멤버가 없으므로) 하나(IStateTriggerBase라는 이름)가 생성되는 특성만 default_interface 존재합니다.

[default_interface]
unsealed runtimeclass StateTriggerBase
{
    protected void SetActive(Boolean IsActive);
};

[default_overload] 특성

참고

이 특성은 생성자에 대해 지원되지 않습니다. 생성자를 arity로 오버로드할 수 없는 경우 이 섹션의 마지막 코드 조각에 표시된 CreateFromUriCreateFromStream 예제와 같은 오버로드된 팩터리 메서드를 정의할 수 있습니다.

이 특성은 default_overload 메서드가 기본 오버로드 메서드임을 나타내는 DefaultOverloadAttribute 클래스를 나타냅니다. 이 섹션에서는 특성의 이유 및 사용 지침을 [default_overload] 안내합니다.

Windows 런타임 메서드를 자유롭게 오버로드할 수 있습니다. 즉, 각 메서드가 서로 다른 수의 인수를 사용하는 한 동일한 이름으로 여러 메서드를 정의할 수 있습니다. 유용성을 위해 끝에 새 매개 변수를 추가하고, 더 짧은 매개 변수 목록 함수의 동작은 누락된 매개 변수에 대한 (시나리오별) 기본값을 사용하여 더 긴 매개 변수 목록 함수를 호출하는 것과 동일한 것이 좋습니다.

runtimeclass Widget
{
    void Start();
    void Start(StartMode mode);
    void Start(StartMode mode, Widget parent);
}

위의 예제에서 시작 모드와 부모 위젯을 지정하는 두 개의 매개 변수를 사용하여 시작을 호출할 수 있습니다. 부모 위젯을 생략하면 기본값은 null입니다. 그리고 모드를 생략하면 일부 시나리오별 기본 모드에서 시작됩니다.

다음 예제 에서는 DeviceInformation.CreateWatcher 를 사용합니다.

runtimeclass DeviceInformation
{
    static DeviceWatcher CreateWatcher();
    static DeviceWatcher CreateWatcher(DeviceClass deviceClass);
    static DeviceWatcher CreateWatcher(String aqsFilter);
    static DeviceWatcher CreateWatcher(String aqsFilter,
                                       IIterable<String> additionalProperties);
    static DeviceWatcher CreateWatcher(String aqsFilter,
                                       IIterable<String> additionalProperties,
                                       DeviceInformationKind kind);
}

이 예제에는 5개의 오버로드가 있으며, 대부분은 각 새 오버로드가 이전 오버로드의 확장이 되도록 하는 권장 패턴을 따릅니다.

매개 변수 수가 동일한 메서드의 오버로드가 여러 개 있는 경우 컴파일러 오류가 발생합니다.

DeviceInformation.CreateWatcher의 1 매개 변수 오버로드는 Windows 데코레이팅하여 정확히 하나의 메서드를 기본 오버로드로 지정해야 합니다. Foundation.Metadata.DefaultOverloadAttribute.

일부 프로그래밍 언어가 동적으로 형식화되기 때문입니다. JavaScript와 Python은 두 가지 예입니다. 이러한 언어의 경우 오버로드 선택은 매개 변수의 수만 고려하고 형식은 고려하지 않습니다. 즉, JavaScript 호출 DeviceInformation.createWatcher(v); 이 모호합니다. vDeviceClass 또는 String으로 강제 변환해야 하나요?

모호성을 해결하려면 메서드 중 하나에 특성을 적용 [default_overload] 해야 합니다. 그러나 어떤 것을 선택합니까?

기본이 아닌 오버로드의 기능을 다른 수단(일반적으로 다른 오버로드 중 하나)에서 계속 사용할 수 있도록 기본 오버로드를 선택해야 합니다.

DeviceInformation.CreateWatcher 예제에서는 String, IIterableString<> 오버로드를 호출하고 빈 속성 목록을 전달하여 문자열 오버로드의 기능을 가져올 수 있습니다. 반면 DeviceClass 오버로드는 DeviceClass로 필터링되는 DeviceWatcher를 만드는 유일한 방법입니다.

이렇게 하면 DeviceClass 메서드가 명확한 승자가 됩니다. 따라서 해당 오버로드에 [default_overload] 특성을 적용하여 표시합니다.

runtimeclass DeviceInformation
{
    static DeviceWatcher CreateWatcher();
    [default_overload]
    static DeviceWatcher CreateWatcher(DeviceClass deviceClass);
    static DeviceWatcher CreateWatcher(String aqsFilter);
    static DeviceWatcher CreateWatcher(String aqsFilter,
                                       IIterable<String> additionalProperties);
    static DeviceWatcher CreateWatcher(String aqsFilter,
                                       IIterable<String> additionalProperties,
                                       DeviceInformationKind kind);
}

충돌하는 모든 오버로드가 다른 오버로드에서 사용할 수 없는 고유한 기능을 제공하기 때문에 승자가 없는 경우 어떻게 해야 할까요?

runtimeclass Widget
{
    static Widget Create(Uri source);
    static Widget Create(IInputStream source);
}

가상 위젯 개체는 Uri에서 만들거나 입력 스트림에서 만들 수 있습니다. 하나를 기본 오버로드로 표시하면 동적으로 형식화된 프로그래밍 언어는 다른 언어에 대한 액세스 권한을 완전히 잃게 됩니다.

이 문제를 해결하려면 두 버전이 오버로드되지 않도록 서로 다른 이름을 지정합니다.

runtimeclass Widget
{
    static Widget CreateFromUri(Uri source);
    static Widget CreateFromStream(IInputStream source);
}

이제 모든 언어에서 두 만들기 패턴을 모두 사용할 수 있습니다.

메서드 오버로드클래스 기반 프로젝션도 참조하세요.

[interface_name] 특성

이 특성은 interface_name 클래스의 인스턴스 멤버를 포함하는 인터페이스의 이름과 IID를 지정합니다. 기본적으로 컴파일러는 메서드에 사용하는 것과 동일한 고유 번호 매기기 알고리즘을 사용하여 인터페이스 이름을 할당합니다.

아래 interface_name 예제에서 runtimeclass에 적용된 특성은 인터페이스에 할당되지 않은 클래스의 모든 인스턴스 멤버를 포함하는 인터페이스의 이름과 IID를 지정합니다. 따라서 LineHeight, LineStackingStrategy, MarginTextAlignmentIBlock 인터페이스의 멤버입니다.

그러나 HorizontalTextAlignment 는 해당 멤버를 포괄하는 특성 때문에 IBlock2 인터페이스의 interface_name 멤버입니다.

[interface_name("Windows.UI.Xaml.Documents.IBlock", 4bce0016-dd47-4350-8cb0-e171600ac896)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
    ...
    Double LineHeight;
    Windows.UI.Xaml.LineStackingStrategy LineStackingStrategy;
    Windows.UI.Xaml.Thickness Margin;
    Windows.UI.Xaml.TextAlignment TextAlignment;

    [interface_name("Windows.UI.Xaml.Documents.IBlock2", 5ec7bdf3-1333-4a92-8318-6caedc12ef89)]
    {
        Windows.UI.Xaml.TextAlignment HorizontalTextAlignment;
    }
    ...
}

특성을 사용하여 interface_name 인터페이스를 강제로 생성할 수도 있습니다. 아래 예제에서 StateTriggerBase 에는 IStateTriggerBase가 필요하지 않으며 생성되는 특성만 interface_name 존재합니다.

[interface_name("Windows.UI.Xaml.IStateTriggerBase", 48b20698-af06-466c-8052-93666dde0e49)]
unsealed runtimeclass StateTriggerBase
{
    protected void SetActive(Boolean IsActive);
};

따라서 클래스 설명이 인터페이스를 참조하지 않지만 클래스를 구현하는 데 필요한 경우 MIDL 3.0 컴파일러는 필요에 따라 인터페이스를 합성하고 추가합니다.

불필요하게 사용하는 default_interface 경우 MIDL 3.0은 인터페이스를 추가로 생성하고 기본값으로 만듭니다.

[method_name] 특성

모든 Windows 런타임 인터페이스에는 동등한 ABI(Application Binary Interface) 인터페이스가 있습니다. ABI 인터페이스를 사용하려면 모든 멤버에 고유한 이름이 있어야 합니다. MIDL 3.0에는 멤버에 이름이 없거나 고유한 이름이 없는 두 가지 경우가 있습니다.

  • 생성자 및
  • 두 개 이상의 오버로드된 메서드.

이러한 경우 MIDL 3.0 컴파일러는 필요에 따라 고유한 멤버 이름을 합성합니다.

기본적으로 컴파일러는 ABI 인터페이스의 동등한 메서드에 대해 생성자 메서드에 className>, <className2>, <className3> 등의 이름을 < 할당합니다. 즉, 사용되지 않는 가장 작은 정수 숫자 접미사(2에서 증가)가 추가되어 생성자 메서드 이름을 고유하게 만듭니다.

마찬가지로 오버로드된 메서드의 경우 일련의 오버로드의 첫 번째 메서드(어휘 순서)의 경우 컴파일러는 해당 ABI 인터페이스 메서드에 원래 메서드 이름을 사용합니다. 이후 오버로드는 원래 이름에 사용되지 않는 가장 작은 정수 숫자 접미사(2에서 증가)를 추가하여 고유하게 만들어집니다.

예를 들어 다음 IDL은 DoWork의 오버로드 3개와 DoWork3이라는 다른 메서드의 두 오버로드를 선언합니다.

void DoWork(Int32 x);
void DoWork3(Int32 x);
void DoWork(Int32 x, Int32 y);
void DoWork(Int32 x, Int32 y, Int32 z);
void DoWork3(Int32 x, Int32 y);

기본적으로( DoWork3 이름이 이미 사용되었기 때문에) 컴파일러는 DoWork 의 세 오버로드에 이름을 지정합니다.

  • Dowork
  • DoWork2
  • DoWork4.

DoWork3DoWork 오버로드가 아닙니다. 기본적으로 컴파일러는 DoWork3 의 두 오버로드에 이름을 지정합니다.

  • DoWork3
  • DoWork32.

그러면 vtable 순서로 함수가 다음과 같이 표시됩니다.

  • Dowork
  • DoWork3
  • DoWork2
  • DoWork4
  • DoWork32

특성을 사용하여 컴파일러의 기본 이름 할당을 재정의할 method_name 수 있습니다.

다음 예제에서는 Block 기본 생성자 멤버에 CreateInstance라는 이름을 사용하도록 컴파일러에 지시합니다.

unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
    ...
    [method_name("CreateInstance")] protected Block();
    ...
}

[static_name] 특성

이 특성은 static_name 클래스의 정적 멤버를 포함하는 인터페이스의 이름 및 IID를 지정합니다.

다음 예제 static_name 에서 런타임 클래스에 적용된 특성은 인터페이스에 할당되지 않은 클래스의 모든 정적 멤버를 포함하는 인터페이스의 이름 및 IID를 지정합니다. 따라서 LineHeightProperty, LineStackingStrategyProperty, MarginPropertyTextAlignmentPropertyIBlockStatics 인터페이스의 멤버입니다.

그러나 HorizontalTextAlignmentProperty 는 해당 멤버를 포함하는 특성 때문에 IBlockStatics2 인터페이스의 static_name 멤버입니다.

[static_name("Windows.UI.Xaml.Documents.IBlockStatics", f86a8c34-8d18-4c53-aebd-91e610a5e010)]
...
unsealed runtimeclass Block : Windows.UI.Xaml.Documents.TextElement
{
    ...
    static Windows.UI.Xaml.DependencyProperty LineHeightProperty{ get; };
    static Windows.UI.Xaml.DependencyProperty LineStackingStrategyProperty{ get; };
    static Windows.UI.Xaml.DependencyProperty MarginProperty{ get; };
    static Windows.UI.Xaml.DependencyProperty TextAlignmentProperty{ get; };

    [static_name("Windows.UI.Xaml.Documents.IBlockStatics2", af01a4d6-03e3-4cee-9b02-2bfc308b27a9)]
    {
        static Windows.UI.Xaml.DependencyProperty HorizontalTextAlignmentProperty{ get; };
    }
    ...
}