다음을 통해 공유


COM 상호 운용에 대한 .NET 형식 한정

COM에 .NET 형식 노출

어셈블리의 형식을 COM 애플리케이션에 노출하려는 경우 디자인 타임에 COM interop의 요구 사항을 고려합니다. 다음 지침을 준수하면 관리되는 형식(클래스, 인터페이스, 구조 및 열거형)이 COM 형식과 원활하게 통합됩니다.

  • 클래스는 인터페이스를 명시적으로 구현해야 합니다.

    COM interop은 클래스의 모든 멤버와 해당 기본 클래스의 멤버를 포함하는 인터페이스를 자동으로 생성하는 메커니즘을 제공하지만 명시적 인터페이스를 제공하는 것이 훨씬 낫습니다. 자동으로 생성된 인터페이스를 클래스 인터페이스라고합니다. 지침은 클래스 인터페이스 소개를 참조하세요.

    VISUAL Basic, C#, C++를 사용하여 IDL(인터페이스 정의 언어) 또는 해당 언어를 사용하는 대신 코드에 인터페이스 정의를 통합할 수 있습니다. 구문 세부 정보는 언어 설명서를 참조하세요.

  • 관리되는 형식은 공용이어야 합니다.

    어셈블리의 공용 형식만 등록되어 형식 라이브러리로 내보냅니다. 따라서 공용 형식만 COM에 표시됩니다.

    관리되는 형식은 COM에 노출되지 않을 수 있는 다른 관리 코드에 기능을 노출합니다. 예를 들어 매개 변수가 있는 생성자, 정적 메서드 및 상수 필드는 COM 클라이언트에 노출되지 않습니다. 또한 런타임이 데이터를 형식 내/외부로 마샬링하면 데이터가 복사되거나 변환될 수 있습니다.

  • 메서드, 속성, 필드 및 이벤트는 public이어야 합니다.

    공용 형식의 멤버도 COM에 표시되어야 하는 경우 공용이어야 합니다. 를 적용하여 공용 형식의 어셈블리, 공용 형식 또는 공용 멤버의 표시 유형을 제한할 ComVisibleAttribute수 있습니다. 기본적으로 모든 공용 형식 및 멤버가 표시됩니다.

  • 형식에는 COM에서 활성화할 공용 매개 변수 없는 생성자가 있어야 합니다.

    관리되는 공용 형식은 COM에 표시됩니다. 그러나 공용 매개 변수가 없는 생성자(인수가 없는 생성자)가 없으면 COM 클라이언트는 형식을 만들 수 없습니다. COM 클라이언트는 다른 수단으로 활성화된 경우에도 형식을 사용할 수 있습니다.

  • 형식은 추상적일 수 없습니다.

    COM 클라이언트와 .NET 클라이언트 모두 추상 형식을 만들 수 없습니다.

COM으로 내보내면 관리되는 형식의 상속 계층이 평면화됩니다. 버전 관리 환경과 관리되지 않는 환경 간에도 버전 관리가 다릅니다. COM에 노출되는 형식은 다른 관리되는 형식과 버전 관리 특성이 다릅니다.

.NET에서 COM 형식 사용

.NET에서 COM 형식을 사용하려는 경우 Tlbimp.exe(형식 라이브러리 가져오기)와 같은 도구를 사용하지 않으려면 다음 지침을 따라야 합니다.

  • 인터페이스에는 적용된 인터페이스가 ComImportAttribute 있어야 합니다.
  • 인터페이스에는 COM 인터페이스에 GuidAttribute 대한 인터페이스 ID가 적용되어야 합니다.
  • 인터페이스는 InterfaceTypeAttribute 이 인터페이스의 기본 인터페이스 형식(IUnknownIDispatch또는IInspectable)을 지정하기 위해 적용되어야 합니다.
    • 기본 옵션은 기본 형식 IDispatch 을 사용하고 선언된 메서드를 인터페이스의 예상 가상 함수 테이블에 추가하는 것입니다.
    • .NET Framework만 기본 형식 IInspectable지정을 지원합니다.

이러한 지침은 일반적인 시나리오에 대한 최소 요구 사항을 제공합니다. 더 많은 사용자 지정 옵션이 존재하며 Interop 특성 적용에 설명되어 있습니다.

.NET에서 COM 인터페이스 정의

.NET 코드가 특성이 ComImportAttribute 있는 인터페이스를 통해 COM 개체에서 메서드를 호출하려고 할 때 호출할 네이티브 코드를 결정하기 위해 인터페이스의 .NET 정의를 형성하기 위해 가상 함수 테이블(vtable 또는 vftable이라고도 함)을 빌드해야 합니다. 이 프로세스는 복잡합니다. 다음 예제에서는 몇 가지 간단한 사례를 보여 줍니다.

다음과 같은 몇 가지 방법으로 COM 인터페이스를 고려합니다.

struct IComInterface : public IUnknown
{
    STDMETHOD(Method)() = 0;
    STDMETHOD(Method2)() = 0;
};

이 인터페이스의 경우 다음 표에서는 가상 함수 테이블 레이아웃에 대해 설명합니다.

IComInterface 가상 함수 테이블 슬롯 메서드 이름
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2

각 메서드는 선언된 순서대로 가상 함수 테이블에 추가됩니다. 특정 순서는 C++ 컴파일러에 의해 정의되지만 오버로드가 없는 간단한 경우 선언 순서는 테이블의 순서를 정의합니다.

다음과 같이 이 인터페이스에 해당하는 .NET 인터페이스를 선언합니다.

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid(/* The IID for IComInterface */)]
interface IComInterface
{
    void Method();
    void Method2();
}

기본 InterfaceTypeAttribute 인터페이스를 지정합니다. 다음과 같은 몇 가지 옵션을 제공합니다.

ComInterfaceType 기본 인터페이스 형식 특성이 지정된 인터페이스의 멤버에 대한 동작
InterfaceIsIUnknown IUnknown 가상 함수 테이블에는 먼저 멤버 IUnknown가 있는 다음, 선언 순서대로 이 인터페이스의 멤버가 있습니다.
InterfaceIsIDispatch IDispatch 멤버는 가상 함수 테이블에 추가되지 않습니다. 을 통해 IDispatch서만 액세스할 수 있습니다.
InterfaceIsDual IDispatch 가상 함수 테이블에는 먼저 멤버 IDispatch가 있는 다음, 선언 순서대로 이 인터페이스의 멤버가 있습니다.
InterfaceIsIInspectable IInspectable 가상 함수 테이블에는 먼저 멤버 IInspectable가 있는 다음, 선언 순서대로 이 인터페이스의 멤버가 있습니다. .NET Framework에서만 지원됩니다.

COM 인터페이스 상속 및 .NET

이 인터페이스를 사용하는 ComImportAttribute COM interop 시스템은 인터페이스 상속과 상호 작용하지 않으므로 일부 완화 단계를 수행하지 않는 한 예기치 않은 동작이 발생할 수 있습니다.

특성을 사용하는 System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute COM 원본 생성기는 인터페이스 상속과 상호 작용하므로 예상대로 동작합니다.

C++의 COM 인터페이스 상속

C++에서 개발자는 다음과 같이 다른 COM 인터페이스에서 파생되는 COM 인터페이스를 선언할 수 있습니다.

struct IComInterface : public IUnknown
{
    STDMETHOD(Method)() = 0;
    STDMETHOD(Method2)() = 0;
};

struct IComInterface2 : public IComInterface
{
    STDMETHOD(Method3)() = 0;
};

이 선언 스타일은 호환성이 손상되는 변경이 될 기존 인터페이스를 변경하지 않고 COM 개체에 메서드를 추가하는 메커니즘으로 정기적으로 사용됩니다. 이 상속 메커니즘은 다음과 같은 가상 함수 테이블 레이아웃을 생성합니다.

IComInterface 가상 함수 테이블 슬롯 메서드 이름
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 가상 함수 테이블 슬롯 메서드 이름
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
5 IComInterface2::Method3

따라서 .에서 정의된 메서드를 쉽게 호출할 수 IComInterface 있습니다 IComInterface2*. 특히 기본 인터페이스에서 메서드를 호출할 때는 기본 인터페이스에 대한 포인터를 QueryInterface 가져오는 호출이 필요하지 않습니다. 또한 C++는 암시적 변환 IComInterface2*IComInterface*허용합니다. 이 변환은 잘 정의되어 있으며 다시 호출 QueryInterface 하지 않도록 할 수 있습니다. 따라서 C 또는 C++에서 원하지 않는 경우 기본 형식으로 가져오기 위해 호출 QueryInterface 할 필요가 없으므로 성능이 향상될 수 있습니다.

비고

WinRT 인터페이스는 이 상속 모델을 따르지 않습니다. .NET에서 -based COM interop 모델과 [ComImport]동일한 모델을 따르도록 정의되어 있습니다.

를 사용한 인터페이스 상속 ComImportAttribute

.NET에서 인터페이스 상속처럼 보이는 C# 코드는 실제로 인터페이스 상속이 아닙니다. 다음 코드를 고려합니다.

interface I
{
    void Method1();
}
interface J : I
{
    void Method2();
}

이 코드는 "구현"J 이라고 말하지 않습니다. I 코드는 실제로 "구현 J 하는 모든 형식도 구현 I해야 합니다."라고 말합니다. 이러한 차이는 인터페이스 상속을 인체 공학적 기반 interop으로 ComImportAttribute만드는 기본 디자인 결정으로 이어집니다. 인터페이스는 항상 자체 인터페이스로 간주됩니다. 인터페이스의 기본 인터페이스 목록은 지정된 .NET 인터페이스에 대한 가상 함수 테이블을 결정하기 위한 계산에 영향을 주지 않습니다.

따라서 이전 C++ COM 인터페이스 예제와 동일한 자연 그대로 다른 가상 함수 테이블 레이아웃이 발생합니다.

C# 코드:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    void Method3();
}

가상 함수 테이블 레이아웃:

IComInterface 가상 함수 테이블 슬롯 메서드 이름
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 가상 함수 테이블 슬롯 메서드 이름
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface2::Method3

이러한 가상 함수 테이블은 C++ 예제와 다르므로 런타임에 심각한 문제가 발생합니다. .NET ComImportAttribute 에서 이러한 인터페이스의 올바른 정의는 다음과 같습니다.

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    new void Method();
    new void Method2();
    void Method3();
}

메타데이터 수준에서 IComInterface2 는 구현하지 않지만 구현 IComInterfaceIComInterface2 도 구현 IComInterface해야 하므로 지정합니다. 따라서 기본 인터페이스 형식의 각 메서드를 다시 선언해야 합니다.

(.NET 8 이상)을 GeneratedComInterfaceAttribute 사용한 인터페이스 상속

C# 인터페이스 상속을 COM 인터페이스 상속으로 구현하여 GeneratedComInterfaceAttribute 트리거되는 COM 원본 생성기를 사용하면 가상 함수 테이블이 예상대로 배치됩니다. 이전 예제를 사용하는 경우 .NET System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute 에서 이러한 인터페이스의 올바른 정의는 다음과 같습니다.

[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    void Method3();
}

기본 인터페이스의 메서드는 다시 선언할 필요가 없으며 다시 선언하면 안 됩니다. 다음 표에서는 결과 가상 함수 테이블에 대해 설명합니다.

IComInterface 가상 함수 테이블 슬롯 메서드 이름
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 가상 함수 테이블 슬롯 메서드 이름
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
5 IComInterface2::Method3

보듯이 이러한 테이블은 C++ 예제와 일치하므로 이러한 인터페이스가 올바르게 작동합니다.

참고하십시오