다음을 통해 공유


내보낸 형식 변환

업데이트: 2007년 11월

이 항목에서는 내보내기 프로세스에서 다음 형식을 변환하는 방법을 설명합니다.

  • 클래스

  • 인터페이스

  • 값 형식

  • 열거형

일반적으로 내보낸 형식은 어셈블리에 내에서 사용했던 이름을 그대로 유지하지만 관리되는 이름과 연결된 네임스페이스는 제외합니다. 예를 들어 다음 코드 샘플의 형식 A.B.IList는 내보낸 형식 라이브러리에서 IList로 변환됩니다. COM 클라이언트는 A.B.IList 대신 IList로 형식을 참조할 수 있습니다.

Namespace A
    Namespace B
        Interface IList
               …       
        End Interface
    End Namespace
End Namespace
namespace A {
    namespace B {
        interface IList {
               …
        }
    }
}

이 방법을 사용하면 네임스페이스가 다른 형식의 이름이 같을 수 있으므로 어셈블리 내에서 형식 이름이 충돌할 수도 있습니다. 내보내기 프로세스에서 이러한 충돌을 발견하는 경우 네임스페이스를 그대로 유지하여 이름이 불명확해지지 않도록 합니다. 다음 코드 샘플에서는 형식 이름이 같은 두 가지 네임스페이스를 보여 줍니다.

Namespace A
    Namespace B
        Public Class LinkedList
            Implements IList
        End Class
      
        Public Interface IList
        End Interface 
    End Namespace
End Namespace

Namespace C
    Public Interface IList
    End Interface
End Namespace
namespace A {
    namespace B {
        public class LinkedList : IList {…}
        public interface IList {…}
    }
}
   namespace C {
       public interface IList {…}
}

다음 형식 라이브러리 표현은 각 형식 이름 확인을 보여 줍니다. 또한 형식 라이브러리 이름에는 마침표를 사용할 수 없으므로 내보내기 프로세스에서 각 마침표를 밑줄로 바꿉니다.

형식 라이브러리 표현

library Widgets 
{
    […]
    coclass LinkedList 
    {
        interface A_B_IList
    };

    […]
    interface A_B_IList {…};
    […]
    interface C_IList {…};
};

또한 내보내기 프로세스는 네임스페이스와 형식 이름을 결합하여 ProgId(프로그래밍 ID)를 자동으로 생성합니다. 예를 들어, 위의 예제에서 관리되는 LinkedList 클래스에 대해 생성된 ProgId는 A.B.LinkedList입니다.

네임스페이스와 형식 이름을 결합하면 잘못된 ProgId가 생성될 수 있습니다. ProgId는 39자로 제한되어 있고 마침표 이외의 문장 부호 문자를 포함할 수 없습니다. 이러한 제한을 피하려면 내보내기 프로세스에서 자동으로 식별자를 생성하도록 하는 대신 ProgIdAttribute를 적용하여 소스 코드에서 ProgId를 지정하면 됩니다.

클래스

내보내기 프로세스는 어셈블리의 각 공용 클래스(ComVisible(false) 특성 생략)를 형식 라이브러리의 coclass로 변환합니다. 내보낸 coclass는 메서드와 속성을 모두 포함하지 않지만 관리되는 클래스의 이름을 유지하고 관리되는 클래스에 의해 명시적으로 구현되는 모든 인터페이스를 구현합니다.

다음 코드 예제에서는 IShape 인터페이스 정의 및 IShape를 구현하는 Circle 클래스를 보여 줍니다. 코드 예제 다음에는 변환된 형식 라이브러리 표현이 있습니다.

Public Interface IShape
    Sub Draw()
    Sub Move(x As Integer, y As Integer)
End Interface

Class Circle
    Implements IShape
    Sub Draw Implements IShape.Draw
    …    
    Sub Move(x As Integer, y As Integer) Implements IShape.Move
    …
    Sub Enlarge(x As Integer)
    …
End Class
public interface IShape {
    void Draw();
    void Move(int x, int y);
}
class Circle : IShape  {
    void Draw();
    void Move(int x, int y);
    void Enlarge(int x);
}

형식 라이브러리 표현

[ uuid(…), dual, odl, oleautomation ]
interface IShape : IDispatch {
    HRESULT Draw();
    HRESULT Move(int x, int y);
}

[ uuid(…) ]
coclass Circle {
    interface IShape;
}

각 coclass는 내보내기 프로세스에서 자동으로 생성할 수 있는 클래스 인터페이스라고 하는 다른 인터페이스 하나를 구현할 수 있습니다. 클래스 인터페이스는 원본 관리되는 클래스에서 사용할 수 있는 모든 메서드와 속성을 노출하며 그로 인해 COM 클라이언트가 클래스 인터페이스를 통해 호출하여 이 메서드와 속성에 액세스할 수 있습니다.

관리되는 클래스 정의 바로 위에 있는 GuidAttribute를 적용하여 특정 UUID(Universal Unique Identifier)를 클래스에 할당할 수 있습니다. 변환하는 동안 내보내기 프로세스는 GuidAttribute에 제공된 값을 형식 라이브러리의 UUID에 전달합니다. 그렇지 않은 경우 내보내기 프로세스는 네임스페이를 포함하여 클래스의 전체 이름을 포함하는 해시에서 UUID를 얻습니다. 전체 이름을 사용하면 지정된 네임스페이스의 지정된 이름을 가진 클래스가 항상 동일한 UUID를 생성하고 이름이 다른 두 클래스는 동일한 UUID를 생성하지 않습니다.

추상 클래스 및 공용 기본 생성자가 없는 클래스는 noncreatable 형식 라이브러리 특성으로 표시됩니다. licensed, hidden, restricted, control 등 coclass에 적용되는 다른 형식 라이브러리 특성은 설정되지 않습니다.

인터페이스

내보내기 프로세스는 관리되는 인터페이스와 동일한 메서드 및 속성을 사용하여 관리되는 인터페이스를 COM 인터페이스로 변환하지만 메서드 시그니처는 상당히 다릅니다.

인터페이스 ID

COM 인터페이스는 IID(인터페이스 식별자)를 포함하여 한 인터페이스와 다른 인터페이스를 구별합니다. GuidAttribute 특성을 적용하면 관리되는 인터페이스에 고정 IID를 할당할 수 있습니다. 이 특성을 생략하고 고정 IID를 할당하지 않으면 내보내기 프로세스에서는 변환하는 동안 자동으로 IID를 할당합니다. 런타임에서 할당된 IID는 인터페이스 내에 정의된 모든 메서드의 전체 서명과 인터페이스 이름(네임스페이스 포함)으로 구성됩니다. 관리되는 인터페이스에서 메서드의 순서를 바꾸거나 메서드 인수와 반환 형식을 변경하면 해당 인터페이스에 할당된 IID도 변경됩니다. 그러나 메서드 이름을 변경하는 것은 IID에 영향을 주지 않습니다.

런타임에 구현된 QueryInterface 메서드를 사용하면 COM 클라이언트는 고정 IID 또는 런타임에 할당된 IID를 사용하는 인터페이스를 얻을 수 있습니다. 런타임에서 생성된 IID는 해당 형식의 메타데이터 내에서 유지되지 않습니다.

인터페이스 형식

달리 지정하지 않는 한 내보내기 프로세스는 모든 관리되는 인터페이스를 형식 라이브러리의 이중 인터페이스로 변환합니다. 이중 인터페이스를 사용하면 COM 클라이언트는 초기 바인딩과 런타임에 바인딩 중에서 선택할 수 있습니다.

InterfaceTypeAttribute 특성을 인터페이스에 적용하여 해당 인터페이스를 이중 인터페이스로 노출할지, IUnknown에서 파생된 인터페이스로 노출할지 아니면 디스패치 전용 인터페이스(dispinterface)로 노출할지를 선택적으로 나타낼 수 있습니다. 내보낸 모든 인터페이스는 관리 코드의 상속 계층 구조와 관계없이 IUnknown 또는 IDispatch에서 직접 확장됩니다.

다음 코드 예제에서는 인터페이스 형식을 제어하기 위한 옵션 값을 보여 줍니다. 형식 라이브러리로 내보내고 나면 이 옵션은 코드 다음에 나오는 형식 라이브러리 표현에 나타난 결과를 생성합니다.

' Creates a Dual interface by default.
Public Interface InterfaceWithNoInterfaceType
    Sub test()
End Interface

' Creates a Dual interface explicitly.
<InterfaceType(ComInterfaceType.InterfaceIsDual)> _
Public Interface InterfaceWithInterfaceIsDual
    Sub test()
End Interface

' Creates an IUnknown interface (not dispatch).
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface InterfaceWithInterfaceIsIUnknown
    Sub test()
End Interface

' Creates a Dispatch-only interface (dispinterface).
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface InterfaceWithInterfaceIsIDispatch
    Sub test()
End Interface
// Creates a Dual interface by default.
public interface InterfaceWithNoInterfaceType {
    void test();
}
// Creates a Dual interface explicitly.
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface InterfaceWithInterfaceIsDual {
    void test();
}
// Creates an IUnknown interface (not dispatch).
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface InterfaceWithInterfaceIsIUnknown {
    void test();
}
// Creates a Dispatch-only interface(dispinterface).
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface InterfaceWithInterfaceIsIDispatch {
    void test();
}

형식 라이브러리 표현

[ odl, uuid(…), dual, oleautomation ]
interface InterfaceWithNoInterfaceType : IDispatch {
    HRESULT test();
};
[ odl, uuid(…), dual, oleautomation ]
interface InterfaceWithInterfaceIsDual : IDispatch {
     HRESULT test();
};
[ odl, uuid(…), oleautomation ]
interface InterfaceWithInterfaceIsIUnknown : IUnknown {
     HRESULT test();
};
[ uuid(…) ]
dispinterface InterfaceWithInterfaceIsIDispatch {
     properties:
     methods:
         void test();
};

내보내기 프로세스 동안 대부분의 인터페이스는 odloleautomation 형식 라이브러리 특성으로 표시되고 (Dispinterfaces는 예외) 이중 인터페이스는 dual 형식 라이브러리 특성으로 표시됩니다. 이중 인터페이스는 IDispatch 인터페이스에서 파생되지만 메서드를 위해 vtable 슬롯을 노출하기도 합니다.

클래스 인터페이스

클래스 인터페이스에 대한 전체 설명 및 사용상의 권장 사항을 보려면 클래스 인터페이스 소개를 참조하십시오. 내보내기 프로세스는 관리 코드에서 명시적으로 정의된 인터페이스 없이 관리되는 클래스를 대신하여 이 인터페이스를 자동으로 생성할 수 있습니다. COM 클라이언트는 클래스 메서드에 직접 액세스할 수 없습니다.

다음 코드 예제에서는 기본 클래스와 파생 클래스를 보여 줍니다. 두 클래스 모두 명시적 인터페이스를 구현하지 않습니다. 내보내기 프로세스는 두 관리되는 클래스에 클래스 인터페이스를 제공합니다.

Public Class BaseClassWithClassInterface
    Private Shared StaticPrivateField As Integer
    Private PrivateFld As Integer
   
    Private Property PrivateProp() As Integer
        Get
            Return 0
        End Get
        Set
        End Set
    End Property
   
    Private Sub PrivateMeth()
        Return
    End Sub 
    Friend Shared StaticInternalField As Integer
    Friend InternalFld As Integer
   
    Friend Property InternalProp() As Integer
        Get
            Return 0
        End Get
        Set
        End Set
    End Property
   
    Friend Sub InternalMeth()
        Return
    End Sub 
    Public Shared StaticPublicField As Integer
    Public PublicFld As Integer
   
    Public Property PublicProp() As Integer
        Get
            Return 0
        End Get
        Set
        End Set
    End Property
   
    Public Sub PublicMeth()
        Return
    End Sub
End Class
 
Public Class DerivedClassWithClassInterface
    Inherits BaseClassWithClassInterface
   
    Public Sub Test()
        Return
    End Sub
End Class
public class BaseClassWithClassInterface {
    private  static int  StaticPrivateField;
    private  int  PrivateFld;
    private  int  PrivateProp{get{return 0;} set{;}}
    private  void PrivateMeth() {return;}

    internal static int  StaticInternalField;
    internal int  InternalFld;
    internal int  InternalProp{get{return 0;} set{;}}
    internal void InternalMeth() {return;}

    public   static int  StaticPublicField;
    public   int  PublicFld;
    public   int  PublicProp{get{return 0;} set{;}}
    public   void PublicMeth() {return;}
}

public class DerivedClassWithClassInterface : BaseClassWithClassInterface {
    public void Test() {return;}
}

형식 라이브러리 표현

[odl,uuid(…), hidden, dual, nonextensible, oleautomation]
interface _BaseClassWithClassInterface : IDispatch {
     [id(00000000),propget]   HRESULT ToString([out, retval] BSTR* p);
     [id(0x60020001)]         HRESULT Equals([in] VARIANT obj, 
                                [out, retval] VARIANT_BOOL* p);
     [id(0x60020002)]         HRESULT GetHashCode([out,retval] long* p);
     [id(0x60020003)]         HRESULT GetType([out, retval] _Type** p);
     [id(0x60020004),propget] HRESULT PublicProp([out,retval] long* p);
     [id(0x60020004),propput] HRESULT PublicProp([in] long p);
     [id(0x60020006)]         HRESULT PublicMeth();
     [id(0x60020007),propget] HRESULT PublicFld([out, retval]long* p);
     [id(0x60020007),propput] HRESULT PublicFld([in] long p);
};
[odl,uuid(…), hidden, dual, nonextensible, oleautomation]
interface _DerivedClassWithClassInterface : IDispatch {
     [id(00000000),propget]   HRESULT ToString([out, retval] BSTR* p);
     [id(0x60020001)]         HRESULT Equals([in] VARIANT obj, 
                                [out, retval] VARIANT_BOOL* p);
     [id(0x60020002)]         HRESULT GetHashCode([out,retval] long* p);
     [id(0x60020003)]         HRESULT GetType([out, retval] _Type** p);
     [id(0x60020004),propget] HRESULT PublicProp([out,retval] long* p);
     [id(0x60020004),propput] HRESULT PublicProp([in] long p);
     [id(0x60020006)]         HRESULT PublicMeth();
     [id(0x60020007),propget] HRESULT PublicFld([out, retval]long* p);
     [id(0x60020007),propput] HRESULT PublicFld([in] long p);
     [id(0x60020008)]         HRESULT Test();
}

내보낸 클래스 인터페이스는 다음과 같은 특징을 가집니다.

  • 각 클래스 인터페이스는 관리되는 클래스의 이름을 유지하지만 앞에 밑줄이 추가됩니다. 이전에 정의한 인터페이스 이름과 인터페이스 이름이 충돌하면 새 이름에 밑줄과 증분값이 추가됩니다. 예를 들어 _ClassWithClassInterface에 사용할 수 있는 다음 이름은 _ClassWithClassInterface_2가 됩니다.

  • 내보내기 프로세스는 항상 새 IID(인터페이스 식별자)를 생성합니다. 클래스 인터페이스의 IID를 명시적으로 설정할 수 없습니다.

  • 기본적으로 두 클래스 인터페이스 모두 IDispatch 인터페이스에서 파생됩니다.

  • 이 인터페이스는 ODL, dual, hidden, nonextensible 및 oleautomation 특성을 가집니다.

  • 두 인터페이스는 기본 클래스(System.Object)의 모든 공용 멤버를 가집니다.

  • 클래스의 전용 또는 내부 멤버를 포함하지 않습니다.

  • 각 멤버에는 자동으로 고유 DispId가 할당됩니다. DispIdAttribute를 클래스 멤버에 적용하여 DispId를 명시적으로 설정할 수 있습니다.

  • 메서드 시그니처는 HRESULT를 반환하기 위해 변환되고 [out, retval] 매개 변수를 사용합니다.

  • 속성과 필드는 [propget], [propput], [propputref]로 변환됩니다.

기본 인터페이스

COM에는 기본 인터페이스라는 개념이 있습니다. 기본 인터페이스 멤버는 Visual Basic 같은 런타임에 바인딩된 언어에서 클래스 멤버로 간주되지만 .NET Framework에서는 클래스 스스로 멤버를 가질 수 있으므로 기본 인터페이스가 필요하지 않습니다. 그러나 클래스를 COM에 노출하는 경우 해당 클래스에 기본 인터페이스가 있으면 클래스를 훨씬 쉽게 사용할 수 있습니다.

관리되는 클래스를 coclass로서 형식 라이브러리로 내보내는 경우 인터페이스는 일반적으로 해당 클래스의 기본 인터페이스로 식별됩니다. 인터페이스가 형식 라이브러리에서 기본 인터페이스로 식별되지 않는 경우 대부분의 COM 응용 프로그램은 첫 번째로 구현된 인터페이스를 해당 coclass의 기본 인터페이스로 간주합니다.

다음 코드 예제에서, 내보내기 프로세스는 클래스 인터페이스가 없는 관리되는 클래스를 변환하고 첫 번째로 구현된 인터페이스를 내보낸 형식 라이브러리에서 기본 인터페이스로 표시합니다. 코드 예제 다음에는 변환된 클래스의 형식 라이브러리 표현이 있습니다.

<ClassInterface(ClassInterfaceType.None)> _
Public Class ClassWithNoClassInterface
    Implements IExplicit
    Implements IAnother
    Sub M()
    …
End Class
[ClassInterface(ClassInterfaceType.None)]
public class ClassWithNoClassInterface : IExplicit, IAnother {
    void M();   
}

형식 라이브러리 표현

coclass ClassWithNoClassInterface {
    [default] IExplicit; 
    IAnother;
}

클래스가 명시적으로 구현하는 인터페이스에 상관없이 내보내기 프로세스는 항상 클래스 인터페이스를 클래스의 기본 인터페이스로 표시합니다. 다음 예제에서는 두 개의 클래스가 나옵니다.

<ClassInterface(ClassInterfaceType.AutoDispatch)> _
Public Class ClassWithAutoDispatch
    Implements IAnother
    Sub M()
    …
End Class 
 
<ClassInterface(ClassInterfaceType.AutoDual)> _
Public Class ClassWithAutoDual
    Implements IAnother
    Sub M()
    …
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class ClassWithAutoDispatch : IExplicit, IAnother {
    void M();   
}

[ClassInterface(ClassInterfaceType.AutoDual)]
public class ClassWithAutoDual : IExplicit, IAnother {
    void M();   
}

형식 라이브러리 표현

// ClassWithAutoDispatch: IDispatch
coclass ClassWithAutoDispatch {
    [default] _ClassWithAutoDispatch;
    interface _Object;
    IExplicit;
    IAnother;
}
interface _ClassWithAutoDual {…}

coclass ClassWithAutoDual {
    [default] _ClassWithAutoDual; 
    IExplicit;
    IAnother;
}

값 형식

System.Value를 확장하는 값 형식은 형식 정의와 함께 C 스타일 구조체로서 형식 라이브러리로 내보냅니다. 구조체 멤버의 레이아웃은 형식에 적용되는 StructLayoutAttribute 특성을 사용하여 제어됩니다. 값 형식의 필드만 내보냅니다. 값 형식에 메서드가 있는 경우 COM에서는 이 메서드에 액세스할 수 없습니다.

예를 들면 다음과 같습니다.

[StructLayout(LayoutKind.Sequential)]
public struct Point {
    int x;
    int y;
    public void SetXY(int x, int y){ 
        this.x = x;
        this.y = y;
    }
};

다음 예제와 같이 Point 값 형식은 point 형식 정의로서 COM으로 내보냅니다.

typedef 
[uuid(…)]
struct tagPoint {
    short x;
    short y;
} Point;

변환 프로세스는 형식 정의에서 SetXY 메서드를 제거합니다.

열거형

내보내기 프로세스는 고유한 멤버 이름 지정을 위해 변경된 멤버 이름을 사용하여 관리되는 열거형을 형식 라이브러리에 열거형으로 추가합니다. 각 멤버 이름의 고유성을 보장하기 위해 Tlbexp.exe는 내보내기 프로세스 동안 각 멤버에 대한 열거형 이름 앞에 밑줄을 추가합니다. 예를 들어 다음 샘플 열거형은 형식 라이브러리 표현 집합을 만듭니다.

Enum DaysOfWeek {
    Sunday = 0;
    Monday;
    Tuesday;
    …
};

형식 라이브러리 표현

enum DaysOfWeek {
    DaysOfWeek_Sunday = 0;
    DaysOfWeek_Monday;
    DaysOfWeek_Tuesday;
    …
};

런타임은 관리되는 열거형 멤버의 범위를 해당 멤버가 속한 열거형으로 지정합니다. 예를 들어 위의 예제의 경우 DaysOfWeek 열거형의 Sunday에 대한 모든 참조는 DaysOfWeek 열거형을 사용하여 한정해야 하며 DaysOfWeek.Sunday 대신 Sunday를 참조할 수는 없습니다. 고유한 멤버 이름 지정은 COM 열거형의 요구 사항입니다.

참고 항목

개념

내보낸 어셈블리 변환

내보낸 모듈 변환

내보낸 멤버 변환

내보낸 매개 변수 변환

기타 리소스

어셈블리를 형식 라이브러리로 변환 요약