次の方法で共有


ComWrappers のソース生成

.NET 8 には、ComWrappers API の実装を作成するソース ジェネレーターが導入されています。 ジェネレーターは GeneratedComInterfaceAttribute を認識します。

.NET ランタイムの組み込みの (ソース生成ではない) Winodws 専用 COM 相互運用システムは、実行時に IL スタブ (JIT 化された IL 命令のストリーム) を生成して、マネージド コードから COM へと、その逆の移行をしやすくします。 この IL スタブは実行時に生成されるため、NativeAOT および IL トリミングと互換性がありません。 実行時のスタブ生成によって、マーシャリングの問題の診断が困難になる場合もあります。

組み込みの相互運用機能は、実行時のコード生成に依存する ComImportDllImport などの属性を使用します。 この例を次のコードに示します。

[ComImport]
interface IFoo
{
    void Method(int i);
}

[DllImport("MyComObjectProvider.dll")]
static nint GetPointerToComInterface();

[DllImport("MyComObjectProvider.dll")]
static void GivePointerToComInterface(nint comObject);

// 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 からインポートするか、それに対して公開するインターフェイス定義に GeneratedComInterfaceAttributeGuidAttribute 属性を追加します。 型には partial のマークを付け、生成されたコードにアクセスできるように可視性を internal または public にする必要があります。

[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
internal partial interface IFoo
{
    void Method(int i);
}

次に、インターフェイスを実装するクラスを COM に公開するために、実装するクラスに GeneratedComClassAttribute を追加します。 このクラスも partial、かつ internalpublic のいずれかである必要があります。

[GeneratedComClass]
internal partial class Foo : IFoo
{
    public void Method(int i)
    {
        // Do things
    }
}

コンパイル時に、ジェネレーターが ComWrappers API の実装を作成します。これで、StrategyBasedComWrappers 型またはカスタム派生型を使用して COM インターフェイスを使用または公開できます。

[LibraryImport("MyComObjectProvider.dll")]
static nint GetPointerToComInterface();

[LibraryImport("MyComObjectProvider.dll")]
static void GivePointerToComInterface(nint comObject);

// 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.GetOrCreateObjectForComInterface(ptr);
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);
GivePointerToComInterface(ptr);

マーシャリングをカスタマイズする

COM インターフェイス ジェネレーターは、MarshalUsingAttribute 属性と MarshalAsAttribute 属性の一部の使用法を考慮して、パラメーターのマーシャリングをカスタマイズします。 詳細については、MarshalUsing 属性を使用したソース生成マーシャリングのカスタマイズに関する記事と、MarshalAs 属性を使用したパラメーターのマーシャリングのカスタマイズに関する記事を参照してください。 他のマーシャリング属性がない場合は、インターフェイス内の型 string のすべてのパラメーターと戻り値に GeneratedComInterfaceAttribute.StringMarshallingGeneratedComInterfaceAttribute.StringMarshallingCustomType プロパティが適用されます。

暗黙的な 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 の相互運用における暗黙的なメソッド シグネチャの変換」を参照してください

組み込みの 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 属性を持つその 1 つの基底インターフェイスからのみ継承できることに注意してください。

マーシャリング API

Marshal の一部の API は、ソース生成 COM と互換性がありません。 これらのメソッドは、ComWrappers 実装上の対応するメソッドに置き換えてください。

関連項目