ComWrappers의 원본 생성
.NET 8에는 ComWrappers API의 구현을 만드는 원본 생성기가 도입되었습니다. 생성기는 GeneratedComInterfaceAttribute를 인식합니다.
.NET 런타임의 기본 제공(소스 생성이 아닌) Windows 전용 COM interop 시스템은 런타임에 JIT-ed인 IL 명령 스트림인 IL 스텁을 생성하여 관리 코드에서 COM으로의 전환을 용이하게 하고 그 반대의 경우도 마찬가지입니다. 이 IL 스텁은 런타임에 생성되므로 NativeAOT 및 IL 트리밍과 호환되지 않습니다. 런타임에 스텁을 생성하면 마샬링 문제를 진단하기가 어려울 수 있습니다.
기본 제공 interop은 런타임에 코드 생성에 의존하는 ComImport
또는 DllImport
같은 특성을 사용합니다. 다음 코드에서 그 예를 볼 수 있습니다.
[ComImport]
interface IFoo
{
void Method(int i);
}
[DllImport("MyComObjectProvider")]
static nint GetPointerToComInterface(); // C definition - IUnknown* GetPointerToComInterface();
[DllImport("MyComObjectProvider")]
static void GivePointerToComInterface(nint comObject); // C definition - void GivePointerToComInterface(IUnknown* pUnk);
// Use the system to create a Runtime Callable Wrapper to use in managed code
nint ptr = GetPointerToComInterface();
IFoo foo = (IFoo)Marshal.GetObjectForIUnknown(ptr);
foo.Method(0);
...
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
IFoo foo = GetManagedIFoo();
nint ptr = Marshal.GetIUnknownForObject(foo);
GivePointerToComInterface(ptr);
ComWrappers
API를 사용하면 기본 제공 COM 시스템을 사용하지 않고 C#에서 COM과 상호 작용할 수 있지만 상당한 상용구와 직접 작성한 안전하지 않은 코드가 필요합니다. COM 인터페이스 생성기는 이 프로세스를 자동화하고 ComWrappers
를 기본 제공 COM만큼 쉽게 만들지만 트리밍 가능하고 AOT 친화적인 방식으로 제공합니다.
기본 사용법
COM 인터페이스 생성기를 사용하려면 COM에서 가져오거나 COM에 노출하려는 인터페이스 정의에 GeneratedComInterfaceAttribute 특성 및 GuidAttribute 특성을 추가합니다. 형식은 partial
로 표시되어야 하고 생성된 코드에 액세스할 수 있도록 internal
또는 public
가시성이 있어야 합니다.
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
internal partial interface IFoo
{
void Method(int i);
}
그런 다음 COM에 인터페이스를 구현하는 클래스를 노출하려면 GeneratedComClassAttribute를 구현 클래스에 추가합니다. 이 클래스는 partial
이어야 하며 internal
또는 public
이어야 합니다.
[GeneratedComClass]
internal partial class Foo : IFoo
{
public void Method(int i)
{
// Do things
}
}
컴파일 시간에 생성기는 ComWrappers API의 구현을 만들고 StrategyBasedComWrappers 형식 또는 사용자 지정 파생 형식을 사용하여 COM 인터페이스를 사용하거나 노출할 수 있습니다.
[LibraryImport("MyComObjectProvider")]
private static partial nint GetPointerToComInterface(); // C definition - IUnknown* GetPointerToComInterface();
[LibraryImport("MyComObjectProvider")]
private static partial void GivePointerToComInterface(nint comObject); // C definition - void GivePointerToComInterface(IUnknown* pUnk);
// Use the ComWrappers API to create a Runtime Callable Wrapper to use in managed code
ComWrappers cw = new StrategyBasedComWrappers();
nint ptr = GetPointerToComInterface();
IFoo foo = (IFoo)cw.GetOrCreateObjectForComInstance(ptr, CreateObjectFlags.None);
foo.Method(0);
...
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
ComWrappers cw = new StrategyBasedComWrappers();
Foo foo = new();
nint ptr = cw.GetOrCreateComInterfaceForObject(foo, CreateComInterfaceFlags.None);
GivePointerToComInterface(ptr);
마샬링 사용자 지정
COM 인터페이스 생성기는 매개 변수 마샬링을 사용자 지정하기 위해 MarshalUsingAttribute 특성 및 MarshalAsAttribute 특성의 일부 사용을 존중합니다. 자세한 내용은 MarshalUsing
특성을 사용하여 원본 생성 마샬링을 사용자 지정하고 MarshalAs
특성을 사용하여 매개 변수 마샬링을 사용자 지정하는 방법을 참조하세요. GeneratedComInterfaceAttribute.StringMarshalling 및 GeneratedComInterfaceAttribute.StringMarshallingCustomType 속성은 다른 마샬링 속성이 없는 경우 인터페이스의 모든 매개 변수 및 반환 유형 string
에 적용됩니다.
암시적 HRESULT 및 PreserveSig
C#의 COM 메서드에는 네이티브 메서드와 다른 서명이 있습니다. 표준 COM에는 오류 및 성공 상태를 나타내는 4바이트 정수 형식의 HRESULT
반환 형식이 있습니다. 이 HRESULT
반환 값은 기본적으로 C# 서명에서 숨겨지고 오류 값이 반환되면 예외로 변환됩니다. 네이티브 COM 서명의 마지막 "out" 매개 변수는 필요에 따라 C# 서명의 반환으로 변환될 수 있습니다.
예를 들어 다음 코드 조각은 C# 메서드 서명과 생성기가 유추하는 해당 네이티브 서명을 보여 줍니다.
void Method1(int i);
int Method2(float i);
HRESULT Method1(int i);
HRESULT Method2(float i, _Out_ int* returnValue);
HRESULT
를 직접 처리하려는 경우 메서드의 PreserveSigAttribute를 사용하여 생성기가 이 변환을 수행하지 않아야 함을 나타낼 수 있습니다. 다음 코드 조각은 [PreserveSig]
가 적용될 때 생성기가 예상하는 네이티브 서명을 보여 줍니다. COM 메서드는 HRESULT
를 반환해야 하므로 PreserveSig
가 있는 모든 메서드의 반환 값은 int
여야 합니다.
[PreserveSig]
int Method1(int i, out int j);
[PreserveSig]
int Method2(float i);
HRESULT Method1(int i, int* j);
HRESULT Method2(float i);
자세한 내용은 .NET interop의 암시적 메서드 서명 변환을 참조하세요.
기본 제공 COM의 비호환성 및 차이점
IUnknown
에만 해당
지원되는 유일한 인터페이스 기반은 IUnknown
입니다. InterfaceIsIUnknown 이외의 값이 있는 InterfaceTypeAttribute의 인터페이스는 소스 생성 COM에서 지원되지 않습니다. InterfaceTypeAttribute
가 없는 모든 인터페이스는 IUnknown
에서 파생된 것으로 간주됩니다. 이는 기본값이 InterfaceIsDual인 기본 제공 COM과 다릅니다.
마샬링 기본값 및 지원
원본 생성 COM에는 기본 제공 COM과는 다른 기본 마샬링 동작이 있습니다.
기본 제공 COM 시스템에서는 암시적
[In, Out]
특성이 있는 blittable 요소의 배열을 제외하고 모든 형식에 암시적[In]
특성이 있습니다. 원본에서 생성된 COM에서 blittable 요소 배열을 비롯한 모든 형식에는[In]
의미 체계가 있습니다.[In]
및[Out]
특성은 배열에서만 허용됩니다. 다른 형식에서[Out]
또는[In, Out]
동작이 필요한 경우in
및out
매개 변수 한정자를 사용합니다.
파생 인터페이스
기본 제공 COM 시스템에서 다른 COM 인터페이스에서 파생되는 인터페이스가 있는 경우 new
키워드를 사용하여 기본 인터페이스의 각 기본 메서드에 대한 섀도잉 메서드를 선언해야 합니다. 자세한 내용은 COM 인터페이스 상속 및 .NET를 참조하세요.
[ComImport]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IBase
{
void Method1(int i);
void Method2(float i);
}
[ComImport]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IDerived : IBase
{
new void Method1(int i);
new void Method2(float f);
void Method3(long l);
void Method4(double d);
}
COM 인터페이스 생성기는 기본 메서드의 섀도잉을 기대하지 않습니다. 다른 메서드에서 상속되는 메서드를 만들려면 기본 인터페이스를 C# 기본 인터페이스로 나타내고 파생된 인터페이스의 메서드를 추가하기만 하면 됩니다. 자세한 내용은 디자인 문서를 참조하세요.
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IBase
{
void Method1(int i);
void Method2(float i);
}
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IDerived : IBase
{
void Method3(long l);
void Method4(double d);
}
GeneratedComInterface
특성이 있는 인터페이스는 GeneratedComInterface
특성이 있는 하나의 기본 인터페이스에서만 상속할 수 있습니다.
어셈블리 경계를 넘어 파생된 인터페이스
.NET 8에서는 다른 어셈블리에 정의된 -attributed 인터페이스 GeneratedComInterfaceAttribute 에서 GeneratedComInterface
파생되는 특성으로 인터페이스를 정의하는 것이 지원되지 않습니다.
.NET 9 이상 버전에서 이 시나리오는 다음과 같은 제한 사항으로 지원됩니다.
- 기본 인터페이스 형식은 파생 형식과 동일한 대상 프레임워크를 대상으로 컴파일되어야 합니다.
- 기본 인터페이스 형식이 있는 경우 기본 인터페이스의 멤버를 숨겨서는 안 됩니다.
또한 다른 어셈블리에 정의된 기본 인터페이스 체인에서 생성된 가상 메서드 오프셋에 대한 변경 내용은 프로젝트가 다시 작성될 때까지 파생된 인터페이스에서 고려되지 않습니다.
참고 항목
.NET 9 이상 버전에서는 생성된 COM 인터페이스를 어셈블리 경계를 넘어 상속하여 이 기능 사용의 제한 사항과 문제를 알려줄 때 경고가 내보내집니다. 이 경고를 사용하지 않도록 설정하여 제한 사항을 인정하고 어셈블리 경계를 넘어 상속할 수 있습니다.
마샬링 API
Marshal의 일부 API는 소스 생성 COM과 호환되지 않습니다. 이러한 메서드를 ComWrappers
구현에서 해당 메서드로 대체합니다.
참고 항목
.NET