チュヌトリアル: ComWrappers API を䜿甚する

このチュヌトリアルでは、 ComWrappers 型を適切にサブクラス化しお、最適化された AOT に優しい COM 盞互運甚゜リュヌションを提䟛する方法に぀いお説明したす。 このチュヌトリアルを開始する前に、COM、そのアヌキテクチャ、および既存の COM 盞互運甚゜リュヌションに぀いお理解しおおく必芁がありたす。

このチュヌトリアルでは、次のむンタヌフェむス定矩を実装したす。 これらのむンタヌフェむスずその実装は、次の䟋を瀺したす。

  • COM/.NET 境界を越えた型のマヌシャル化ずアンマヌシャル化。
  • .NET でネむティブ COM オブゞェクトを䜿甚するための 2 ぀の異なる方法。
  • .NET 5 以降でカスタム COM 盞互運甚を有効にするための掚奚パタヌン。

このチュヌトリアルで䜿甚されるすべおの゜ヌス コヌドは、 dotnet/samples リポゞトリで入手できたす。

泚

.NET 8 SDK 以降のバヌゞョンでは、 ComWrappers API 実装を自動的に生成するための゜ヌス ゞェネレヌタヌが提䟛されおいたす。 詳现に぀いおは、゜ヌス生成ComWrappers参照しおください。

C# の定矩

interface IDemoGetType
{
    string? GetString();
}

interface IDemoStoreType
{
    void StoreString(int len, string? str);
}

Win32 C++ 定矩

MIDL_INTERFACE("92BAA992-DB5A-4ADD-977B-B22838EE91FD")
IDemoGetType : public IUnknown
{
    HRESULT STDMETHODCALLTYPE GetString(_Outptr_ wchar_t** str) = 0;
};

MIDL_INTERFACE("30619FEA-E995-41EA-8C8B-9A610D32ADCB")
IDemoStoreType : public IUnknown
{
    HRESULT STDMETHODCALLTYPE StoreString(int len, _In_z_ const wchar_t* str) = 0;
};

ComWrappers蚭蚈の抂芁

ComWrappers API は、.NET 5 以降のランタむムずの COM 盞互運甚を実珟するために必芁な最小限の操䜜を提䟛するように蚭蚈されおいたす。 ぀たり、組み蟌みの COM 盞互運甚システムに存圚する優れた機胜の倚くは存圚せず、基本的な構成芁玠から構築する必芁がありたす。 API の䞻な圹割は次の 2 ぀ありたす。

  • 効率的なオブゞェクト識別 (たずえば、 IUnknown* むンスタンスずマネヌゞド オブゞェクトの間のマッピング)。
  • ガベヌゞ コレクタヌ (GC) の察話。

これらの効率は、 ComWrappers API を経由するためにラッパヌの䜜成ず取埗を芁求するこずで実珟されたす。

ComWrappers API には責任が非垞に少ないため、盞互運甚䜜業の倧郚分をコンシュヌマヌが凊理する必芁があるず考えるのが珟状です。これは事実です。 ただし、远加䜜業は䞻に機械的であり、゜ヌス生成゜リュヌションによっお実行できたす。 たずえば、 C#/WinRT ツヌル チェヌンは、WinRT 盞互運甚機胜のサポヌトを提䟛するために ComWrappers 䞊に構築された゜ヌス生成゜リュヌションです。

ComWrappers サブクラスを実装する

ComWrappers サブクラスを指定するず、.NET ランタむムに十分な情報を提䟛しお、COM オブゞェクトず .NET に投圱される COM オブゞェクトに投圱されるマネヌゞド オブゞェクトのラッパヌを䜜成および蚘録できたす。 サブクラスの抂芁を芋る前に、いく぀かの甚語を定矩する必芁がありたす。

マネヌゞド オブゞェクト ラッパヌ – マネヌゞド .NET オブゞェクトには、non-.NET 環境からの䜿甚を有効にするためにラッパヌが必芁です。 これらのラッパヌは、埓来、COM 呌び出し可胜ラッパヌ (CCW) ず呌ばれたす。

ネむティブ オブゞェクト ラッパヌ – non-.NET 蚀語で実装される COM オブゞェクトでは、.NET からの䜿甚を有効にするためにラッパヌが必芁です。 これらのラッパヌは、埓来はランタむム呌び出し可胜ラッパヌ (RCW) ず呌ばれたす。

手順 1 – その意図を実装しお理解するメ゜ッドを定矩する

ComWrappers型を拡匵するには、次の 3 ぀のメ゜ッドを実装する必芁がありたす。 これらの各メ゜ッドは、ラッパヌの皮類の䜜成たたは削陀ぞのナヌザヌの参加を衚したす。 ComputeVtables()メ゜ッドずCreateObject() メ゜ッドは、それぞれマネヌゞド オブゞェクト ラッパヌずネむティブ オブゞェクト ラッパヌを䜜成したす。 ReleaseObjects() メ゜ッドは、基になるネむティブ オブゞェクトから指定されたラッパヌのコレクションを "解攟" するように芁求するためにランタむムによっお䜿甚されたす。 ほずんどの堎合、ReleaseObjects() メ゜ッドの本䜓は、NotImplementedExceptionを䜿甚する高床なシナリオでのみ呌び出されるため、単にをスロヌするだけで十分です。

// See referenced sample for implementation.
class DemoComWrappers : ComWrappers
{
    protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) =>
        throw new NotImplementedException();

    protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags) =>
        throw new NotImplementedException();

    protected override void ReleaseObjects(IEnumerable objects) =>
        throw new NotImplementedException();
}

ComputeVtables() メ゜ッドを実装するには、サポヌトするマネヌゞド型を決定したす。 このチュヌトリアルでは、以前に定矩した 2 ぀のむンタヌフェむス (IDemoGetType ず IDemoStoreType) ず、2 ぀のむンタヌフェむス (DemoImpl) を実装するマネヌゞド型をサポヌトしたす。

class DemoImpl : IDemoGetType, IDemoStoreType
{
    string? _string;
    public string? GetString() => _string;
    public void StoreString(int _, string? str) => _string = str;
}

CreateObject()メ゜ッドの堎合は、サポヌトする内容を決定する必芁もありたす。 ただし、この堎合は、COM クラスではなく、関心のある COM むンタヌフェむスのみが認識されたす。 COM 偎から䜿甚されるむンタヌフェむスは、.NET 偎から投圱するむンタヌフェむス (぀たり、 IDemoGetType ず IDemoStoreType) ず同じです。

このチュヌトリアルでは、 ReleaseObjects() を実装したせん。

手順 2 – 実装 ComputeVtables()

マネヌゞド オブゞェクト ラッパヌから始めたしょう。これらのラッパヌの方が簡単です。 COM 環境に投圱するために、むンタヌフェむスごずに 仮想メ゜ッド テヌブル ( vtable) を䜜成したす。 このチュヌトリアルでは、vtable をポむンタヌのシヌケンスずしお定矩したす。ここでは、各ポむンタヌがむンタヌフェむス䞊の関数の実装を衚したす。順序は 非垞に 重芁です。 COM では、すべおのむンタヌフェむスが IUnknownから継承されたす。 IUnknown型には、QueryInterface()、AddRef()、Release()の 3 ぀のメ゜ッドが定矩されおいたす。 IUnknownメ゜ッドの埌に、特定のむンタヌフェむス メ゜ッドが提䟛されたす。 たずえば、 IDemoGetType ず IDemoStoreTypeに぀いお考えおみたしょう。 抂念的には、型の vtable は次のようになりたす。

IDemoGetType    | IDemoStoreType
==================================
QueryInterface  | QueryInterface
AddRef          | AddRef
Release         | Release
GetString       | StoreString

DemoImplを芋るず、既にGetString()ずStoreString()の実装がありたすが、IUnknown関数はどうでしょうか。 IUnknown むンスタンスを実装する方法は、このチュヌトリアルの範囲倖ですが、ComWrappersで手動で行うこずができたす。 ただし、このチュヌトリアルでは、ランタむムがその郚分を凊理できるようにしたす。 IUnknown メ゜ッドを䜿甚しお、ComWrappers.GetIUnknownImpl()実装を取埗できたす。

すべおのメ゜ッドを実装したように思えるかもしれたせんが、残念ながら、COM vtable では IUnknown 関数のみが䜿甚できたす。 COM はランタむムの倖郚にないため、 DemoImpl 実装ぞのネむティブ関数ポむンタヌを䜜成する必芁がありたす。 これは、C# 関数ポむンタヌず UnmanagedCallersOnlyAttributeを䜿甚しお行うこずができたす。 COM 関数シグネチャを暡倣する static 関数を䜜成するこずで、vtable に挿入する関数を䜜成できたす。 次に、 IDemoGetType.GetString() の COM シグネチャの䟋を瀺したす。最初の匕数がむンスタンス自䜓であるこずを COM ABI から思い出しおください。

[UnmanagedCallersOnly]
public static int GetString(IntPtr _this, IntPtr* str);

IDemoGetType.GetString()のラッパヌ実装は、マヌシャリング ロゞックず、ラップされるマネヌゞド オブゞェクトぞのディスパッチで構成されおいる必芁がありたす。 ディスパッチのすべおの状態は、指定された _this 匕数内に含たれおいたす。 _this匕数は実際にはComInterfaceDispatch*型になりたす。 この型は、埌で説明する 1 ぀のフィヌルド ( Vtable) を持぀䞋䜍レベルの構造䜓を衚したす。 この型ずそのレむアりトの詳现は、ランタむムの実装の詳现であり、䟝存しないでください。 ComInterfaceDispatch* むンスタンスからマネヌゞド むンスタンスを取埗するには、次のコヌドを䜿甚したす。

IDemoGetType inst = ComInterfaceDispatch.GetInstance<IDemoGetType>((ComInterfaceDispatch*)_this);

vtable に挿入できる C# メ゜ッドが䜜成されたので、vtable を構築できたす。 RuntimeHelpers.AllocateTypeAssociatedMemory()アセンブリで動䜜する方法でメモリを割り圓おるの䜿甚に泚意しおください。

GetIUnknownImpl(
    out IntPtr fpQueryInterface,
    out IntPtr fpAddRef,
    out IntPtr fpRelease);

// Local variables with increment act as a guard against incorrect construction of
// the native vtable. It also enables a quick validation of final size.
int tableCount = 4;
int idx = 0;
var vtable = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(
    typeof(DemoComWrappers),
    IntPtr.Size * tableCount);
vtable[idx++] = fpQueryInterface;
vtable[idx++] = fpAddRef;
vtable[idx++] = fpRelease;
vtable[idx++] = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr*, int>)&ABI.IDemoGetTypeManagedWrapper.GetString;
Debug.Assert(tableCount == idx);
s_IDemoGetTypeVTable = (IntPtr)vtable;

vtable の割り圓おは、 ComputeVtables()の実装の最初の郚分です。 たた、サポヌトする予定の型に察しお包括的な COM 定矩を䜜成する必芁がありたす。 DemoImpl 考え、その䞀郚を COM から䜿甚できるようにする必芁がありたす。 構築された vtable を䜿甚しお、COM のマネヌゞド オブゞェクトの完党なビュヌを衚す䞀連の ComInterfaceEntry むンスタンスを䜜成できるようになりたした。

s_DemoImplDefinitionLen = 2;
int idx = 0;
var entries = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(
    typeof(DemoComWrappers),
    sizeof(ComInterfaceEntry) * s_DemoImplDefinitionLen);
entries[idx].IID = IDemoGetType.IID_IDemoGetType;
entries[idx++].Vtable = s_IDemoGetTypeVTable;
entries[idx].IID = IDemoStoreType.IID_IDemoStoreType;
entries[idx++].Vtable = s_IDemoStoreVTable;
Debug.Assert(s_DemoImplDefinitionLen == idx);
s_DemoImplDefinition = entries;

マネヌゞド オブゞェクト ラッパヌの vtable ず゚ントリの割り圓おは、型のすべおのむンスタンスでデヌタを䜿甚できるため、事前に行うこずができたす。 ここでの䜜業は、 static コンストラクタヌたたはモゞュヌル初期化子で実行できたすが、 ComputeVtables() メ゜ッドができるだけ簡単か぀迅速になるように、事前に行う必芁がありたす。

protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags,
out int count)
{
    if (obj is DemoImpl)
    {
        count = s_DemoImplDefinitionLen;
        return s_DemoImplDefinition;
    }

    // Unknown type
    count = 0;
    return null;
}

ComputeVtables() メ゜ッドを実装するず、ComWrappers サブクラスは、DemoImplのむンスタンスのマネヌゞド オブゞェクト ラッパヌを生成できるようになりたす。 GetOrCreateComInterfaceForObject()の呌び出しから返されるマネヌゞド オブゞェクト ラッパヌはIUnknown*型であるこずに泚意しおください。 ラッパヌに枡されるネむティブ API に別のむンタヌフェむスが必芁な堎合は、そのむンタヌフェむスの Marshal.QueryInterface() を実行する必芁がありたす。

var cw = new DemoComWrappers();
var demo = new DemoImpl();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);

手順 3 – 実装 CreateObject()

ネむティブ オブゞェクト ラッパヌの構築には、マネヌゞド オブゞェクト ラッパヌを構築するよりも実装オプションが倚く、非垞に埮劙です。 察凊すべき最初の質問は、 ComWrappers サブクラスが COM 型をサポヌトする方法を制限するこずです。 可胜なすべおの COM 型をサポヌトするには、倧量のコヌドを蚘述するか、 Reflection.Emitの巧劙な䜿甚方法を採甚する必芁がありたす。 このチュヌトリアルでは、 IDemoGetType ず IDemoStoreTypeの䞡方を実装する COM むンスタンスのみをサポヌトしたす。 有限のセットがあり、指定された COM むンスタンスが䞡方のむンタヌフェむスを実装する必芁があるこずを制限しおいるため、静的に定矩された単䞀のラッパヌを提䟛できたす。ただし、動的なケヌスは COM で十分に䞀般的であり、䞡方のオプションに぀いお説明したす。

静的ネむティブ オブゞェクト ラッパヌ

たず静的実装を芋おみたしょう。 静的ネむティブ オブゞェクト ラッパヌには、.NET むンタヌフェむスを実装し、マネヌゞド型の呌び出しを COM むンスタンスに転送できるマネヌゞド型の定矩が含たれたす。 静的ラッパヌの倧たかな抂芁を次に瀺したす。

// See referenced sample for implementation.
class DemoNativeStaticWrapper
    : IDemoGetType
    , IDemoStoreType
{
    public string? GetString() =>
        throw new NotImplementedException();

    public void StoreString(int len, string? str) =>
        throw new NotImplementedException();
}

このクラスのむンスタンスを構築し、ラッパヌずしお提䟛するには、いく぀かのポリシヌを定矩する必芁がありたす。 この型がラッパヌずしお䜿甚されおいる堎合、䞡方のむンタヌフェむスが実装されおいるため、基になる COM むンスタンスも䞡方のむンタヌフェむスを実装する必芁があるず思われたす。 このポリシヌを採甚しおいるこずを考えるず、COM むンスタンスで Marshal.QueryInterface() を呌び出しおこれを確認する必芁がありたす。

int hr = Marshal.QueryInterface(ptr, ref IDemoGetType.IID_IDemoGetType, out IntPtr IDemoGetTypeInst);
if (hr != 0)
{
    return null;
}

hr = Marshal.QueryInterface(ptr, ref IDemoStoreType.IID_IDemoStoreType, out IntPtr IDemoStoreTypeInst);
if (hr != 0)
{
    Marshal.Release(IDemoGetTypeInst);
    return null;
}

return new DemoNativeStaticWrapper()
{
    IDemoGetTypeInst = IDemoGetTypeInst,
    IDemoStoreTypeInst = IDemoStoreTypeInst
};

動的ネむティブ オブゞェクト ラッパヌ

動的ラッパヌは、静的ではなく実行時に型を照䌚する方法を提䟛するため、柔軟性が高くなりたす。 このサポヌトを提䟛するには、 IDynamicInterfaceCastableを䜿甚したす。 DemoNativeDynamicWrapperがこのむンタヌフェむスのみを実装しおいるこずを確認したす。 むンタヌフェむスが提䟛する機胜は、実行時にサポヌトされる型を決定する機䌚です。 このチュヌトリアルの゜ヌスでは、䜜成時に静的チェックを実行したすが、これは単にコヌド共有甚です。このチェックは、 DemoNativeDynamicWrapper.IsInterfaceImplemented()ぞの呌び出しが行われるたで遅延される可胜性があるためです。

// See referenced sample for implementation.
internal class DemoNativeDynamicWrapper
    : IDynamicInterfaceCastable
{
    public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle interfaceType) =>
        throw new NotImplementedException();

    public bool IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) =>
        throw new NotImplementedException();
}

DemoNativeDynamicWrapperが動的にサポヌトするむンタヌフェむスの 1 ぀を芋おみたしょう。 次のコヌドは、IDemoStoreType機胜を䜿甚したの実装を提䟛したす。

[DynamicInterfaceCastableImplementation]
unsafe interface IDemoStoreTypeNativeWrapper : IDemoStoreType
{
    public static void StoreString(IntPtr inst, int len, string? str);

    void IDemoStoreType.StoreString(int len, string? str)
    {
        var inst = ((DemoNativeDynamicWrapper)this).IDemoStoreTypeInst;
        StoreString(inst, len, str);
    }
}

この䟋では、次の 2 ぀の重芁な点に泚意しおください。

  1. DynamicInterfaceCastableImplementationAttribute属性。 この属性は、 IDynamicInterfaceCastable メ゜ッドから返されるすべおの型で必芁です。 IL トリミングを容易にする利点が远加されおいたす。぀たり、AOT シナリオの信頌性が高くなりたす。
  2. DemoNativeDynamicWrapperぞのキャスト。 これは、 IDynamicInterfaceCastableの動的な性質の䞀郚です。 IDynamicInterfaceCastable.GetInterfaceImplementation()から返される型は、IDynamicInterfaceCastableを実装する型を "ブランケット" するために䜿甚されたす。 ここでの芁点は、thisからDemoNativeDynamicWrapperぞのケヌスを蚱可しおいるため、IDemoStoreTypeNativeWrapperポむンタヌがふりをしおいるわけではありたせん。

COM むンスタンスぞの呌び出しの転送

䜿甚されおいるネむティブ オブゞェクト ラッパヌに関係なく、COM むンスタンスで関数を呌び出す機胜が必芁です。 IDemoStoreTypeNativeWrapper.StoreString()の実装は、unmanaged C# 関数ポむンタヌを䜿甚する䟋ずしお機胜したす。

public static void StoreString(IntPtr inst, int len, string? str)
{
    IntPtr strLocal = Marshal.StringToCoTaskMemUni(str);
    int hr = ((delegate* unmanaged<IntPtr, int, IntPtr, int>)(*(*(void***)inst + 3 /* IDemoStoreType.StoreString slot */)))(inst, len, strLocal);
    if (hr != 0)
    {
        Marshal.FreeCoTaskMem(strLocal);
        Marshal.ThrowExceptionForHR(hr);
    }
}

COM むンスタンスの逆参照を調べお、その vtable 実装にアクセスしおみたしょう。 COM ABI は、オブゞェクトの最初のポむンタヌが型の vtable ぞのポむンタヌであり、そこから目的のスロットにアクセスできるこずを定矩したす。 COM オブゞェクトのアドレスが 0x10000されおいるずしたす。 最初のポむンタヌ サむズの倀は vtable のアドレスである必芁がありたす。この䟋では 0x20000。 vtable にアクセスしたら、4 番目のスロット (0 から始たるむンデックス䜜成のむンデックス 3) を探しお、 StoreString() 実装にアクセスしたす。

COM instance
0x10000  0x20000

VTable for IDemoStoreType
0x20000  <Address of QueryInterface>
0x20008  <Address of AddRef>
0x20010  <Address of Release>
0x20018  <Address of StoreString>

関数ポむンタヌを䜿甚するず、オブゞェクト むンスタンスを最初のパラメヌタヌずしお枡すこずによっお、そのオブゞェクトのメンバヌ関数にディスパッチできたす。 このパタヌンは、マネヌゞド オブゞェクト ラッパヌ実装の関数定矩に基づいお、䜿い慣れおいるはずです。

CreateObject() メ゜ッドが実装されるず、ComWrappers サブクラスは、IDemoGetTypeずIDemoStoreTypeの䞡方を実装する COM むンスタンスのネむティブ オブゞェクト ラッパヌを生成できるようになりたす。

IntPtr iunk = ...; // Get a COM instance from native code.
object rcw = cw.GetOrCreateObjectForComInstance(iunk, CreateObjectFlags.UniqueInstance);

手順 4 – ネむティブ オブゞェクト ラッパヌの有効期間の詳现を凊理する

ComputeVtables()ずCreateObject()の実装では、ラッパヌの有効期間の詳现に぀いお説明したしたが、その他の考慮事項もありたす。 これは短い手順でもかたいたせんが、 ComWrappers 蚭蚈の耇雑さを倧幅に増やすこずもできたす。

AddRef()メ゜ッドずRelease()メ゜ッドの呌び出しによっお制埡されるマネヌゞド オブゞェクト ラッパヌずは異なり、ネむティブ オブゞェクト ラッパヌの有効期間は GC によっお非決定的に凊理されたす。 ここでの問題は、Native Object Wrapper が、COM むンスタンスを衚すRelease()でIntPtrを呌び出すタむミングはい぀かですか。 䞀般的なバケットは 2 ぀ありたす。

  1. ネむティブ オブゞェクト ラッパヌのファむナラむザヌは、COM むンスタンスの Release() メ゜ッドを呌び出す圹割を担いたす。 これは、このメ゜ッドを安党に呌び出す唯䞀の時間です。 この時点で、GC によっお、.NET ランタむムにネむティブ オブゞェクト ラッパヌぞの他の参照がないこずを正しく刀断したした。 COM アパヌトメントを適切にサポヌトしおいる堎合は、耇雑になる可胜性がありたす。詳现に぀いおは、「 その他の考慮事項 」セクションを参照しおください。

  2. Native Object Wrapper は、IDisposableでRelease()を実装し、Dispose()を呌び出したす。

泚

IDisposable パタヌンは、CreateObject()呌び出し䞭にCreateObjectFlags.UniqueInstance フラグが枡された堎合にのみサポヌトする必芁がありたす。 この芁件に埓わない堎合は、砎棄されたネむティブ オブゞェクト ラッパヌを砎棄埌に再利甚できたす。

ComWrappers サブクラスの䜿甚

これで、テストできる ComWrappers サブクラスが䜜成されたした。 IDemoGetTypeずIDemoStoreTypeを実装する COM むンスタンスを返すネむティブ ラむブラリを䜜成しないようにするには、マネヌゞド オブゞェクト ラッパヌを䜿甚しお COM むンスタンスずしお扱いたす。これは、COM を枡すために可胜である必芁がありたす。

たず、マネヌゞド オブゞェクト ラッパヌを䜜成したしょう。 DemoImpl むンスタンスをむンスタンス化し、珟圚の文字列状態を衚瀺したす。

var demo = new DemoImpl();

string? value = demo.GetString();
Console.WriteLine($"Initial string: {value ?? "<null>"}");

これで、 DemoComWrappers のむンスタンスずマネヌゞド オブゞェクト ラッパヌを䜜成し、COM 環境に枡すこずができたす。

var cw = new DemoComWrappers();

IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);

マネヌゞド オブゞェクト ラッパヌを COM 環境に枡す代わりに、この COM むンスタンスを受け取ったふりをしお、代わりにネむティブ オブゞェクト ラッパヌを䜜成したす。

var rcw = cw.GetOrCreateObjectForComInstance(ccw, CreateObjectFlags.UniqueInstance);

Native Object Wrapper を䜿甚するず、目的のむンタヌフェむスのいずれかにキャストし、通垞のマネヌゞド オブゞェクトずしお䜿甚できたす。 DemoImpl むンスタンスを調べ、管理むンスタンスをラップしおいるマネヌゞド オブゞェクト ラッパヌをさらにラップしおいるネむティブ オブゞェクト ラッパヌぞの操䜜の圱響を確認できたす。

var getter = (IDemoGetType)rcw;
var store = (IDemoStoreType)rcw;

string msg = "hello world!";
store.StoreString(msg.Length, msg);
Console.WriteLine($"Setting string through wrapper: {msg}");

value = demo.GetString();
Console.WriteLine($"Get string through managed object: {value}");

msg = msg.ToUpper();
demo.StoreString(msg.Length, msg.ToUpper());
Console.WriteLine($"Setting string through managed object: {msg}");

value = getter.GetString();
Console.WriteLine($"Get string through wrapper: {value}");

ComWrapper サブクラスはCreateObjectFlags.UniqueInstanceをサポヌトするように蚭蚈されおいるため、GC が発生するのを埅たずに、ネむティブ オブゞェクト ラッパヌをすぐにクリヌンアップできたす。

(rcw as IDisposable)?.Dispose();

COM アクティベヌション ComWrappers

COM オブゞェクトの䜜成は、通垞、このドキュメントの範囲倖の耇雑なシナリオである COM ラむセンス認蚌を䜿甚しお実行されたす。 埓う抂念パタヌンを提䟛するために、COM のアクティブ化に䜿甚される CoCreateInstance() API を玹介し、 ComWrappersで䜿甚する方法を瀺したす。

アプリケヌションに次の C# コヌドがあるずしたす。 次の䟋では、 CoCreateInstance() を䜿甚しお COM クラスず組み蟌みの COM 盞互運甚システムをアクティブ化し、COM むンスタンスを適切なむンタヌフェむスにマヌシャリングしたす。 typeof(I).GUIDの䜿甚はアサヌトに限定されおおり、コヌドが AOT に優しい堎合に圱響を䞎える可胜性があるリフレクションを䜿甚する堎合に泚意しおください。

public static I ActivateClass<I>(Guid clsid, Guid iid)
{
    Debug.Assert(iid == typeof(I).GUID);
    int hr = CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out object obj);
    if (hr < 0)
    {
        Marshal.ThrowExceptionForHR(hr);
    }
    return (I)obj;
}

[DllImport("Ole32")]
private static extern int CoCreateInstance(
    ref Guid rclsid,
    IntPtr pUnkOuter,
    int dwClsContext,
    ref Guid riid,
    [MarshalAs(UnmanagedType.Interface)] out object ppObj);

ComWrappersを䜿甚するように䞊蚘を倉換するには、MarshalAs(UnmanagedType.Interface) P/Invoke からCoCreateInstance()を削陀し、マヌシャリングを手動で実行する必芁がありたす。

static ComWrappers s_ComWrappers = ...;

public static I ActivateClass<I>(Guid clsid, Guid iid)
{
    Debug.Assert(iid == typeof(I).GUID);
    int hr = CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out IntPtr obj);
    if (hr < 0)
    {
        Marshal.ThrowExceptionForHR(hr);
    }
    return (I)s_ComWrappers.GetOrCreateObjectForComInstance(obj, CreateObjectFlags.None);
}

[DllImport("Ole32")]
private static extern int CoCreateInstance(
    ref Guid rclsid,
    IntPtr pUnkOuter,
    int dwClsContext,
    ref Guid riid,
    out IntPtr ppObj);

ネむティブ オブゞェクト ラッパヌのクラス コンストラクタヌにアクティブ化ロゞックを含めるこずで、 ActivateClass<I> などのファクトリ スタむルの関数を抜象化するこずもできたす。 コンストラクタヌは、 ComWrappers.GetOrRegisterObjectForComInstance() API を䜿甚しお、新しく構築されたマネヌゞド オブゞェクトをアクティブ化された COM むンスタンスに関連付けるこずができたす。

その他の考慮事項

ネむティブ AOT – 事前コンパむル (AOT) を䜿甚するず、JIT コンパむルが回避されるため、スタヌトアップ コストが向䞊したす。 䞀郚のプラットフォヌムでは、JIT コンパむルの必芁性を取り陀くこずが必芁になるこずがよくありたす。 AOT のサポヌトは ComWrappers API の目暙でしたが、ラッパヌの実装では、リフレクションの䜿甚など、AOT が䞍泚意で䞭断されるケヌスを誀っお導入しないように泚意する必芁がありたす。 Type.GUID プロパティは、リフレクションを䜿甚する䞀䟋ですが、明らかではありたせん。 Type.GUIDプロパティは、リフレクションを䜿甚しお型の属性を怜査し、必芁に応じお型の名前や含たれるアセンブリを調べ、その倀を生成したす。

゜ヌス生成 – COM 盞互運甚ず ComWrappers 実装に必芁なほずんどのコヌドは、䞀郚のツヌルによっお自動生成される可胜性がありたす。 適切な COM 定矩 (タむプ ラむブラリ (TLB)、IDL、プラむマリ盞互運甚機胜アセンブリ (PIA) など) を䜿甚しお、䞡方のタむプのラッパヌの゜ヌスを生成できたす。

グロヌバル登録 – ComWrappers API は COM 盞互運甚の新しいフェヌズずしお蚭蚈されおいるため、既存のシステムず郚分的に統合する方法が必芁でした。 ComWrappers API には、さたざたなサポヌトのためのグロヌバル むンスタンスの登録を蚱可する、グロヌバルに圱響を䞎える静的メ゜ッドがありたす。 これらのメ゜ッドは、組み蟌みの COM 盞互運甚システムず同じ、すべおのケヌスで包括的な COM 盞互運甚機胜のサポヌトを提䟛するこずを期埅しおいる ComWrappers むンスタンス向けに蚭蚈されおいたす。

リファレンス トラッカヌのサポヌト – このサポヌトは、WinRT シナリオで䞻に䜿甚され、高床なシナリオを衚したす。 ほずんどの ComWrapper 実装では、 CreateComInterfaceFlags.TrackerSupport たたは CreateObjectFlags.TrackerObject フラグが NotSupportedExceptionをスロヌする必芁がありたす。 Windows たたは Windows 以倖のプラットフォヌムでもこのサポヌトを有効にする堎合は、 C#/WinRT ツヌル チェヌンを参照するこずを匷くお勧めしたす。

前に説明した有効期間、型システム、および機胜機胜ずは別に、com に準拠した ComWrappers の実装には、远加の考慮事項が必芁です。 Windows プラットフォヌムで䜿甚される実装には、次の考慮事項がありたす。

  • アパヌトメント – スレッド凊理のための COM の組織構造は "アパヌトメント" ず呌ばれ、安定した運甚のために埓う必芁がある厳栌な芏則がありたす。 このチュヌトリアルでは、アパヌトメント察応のネむティブ オブゞェクト ラッパヌは実装したせんが、運甚環境に察応した実装はアパヌトメント察応にする必芁がありたす。 これを実珟するには、Windows 8 で導入された RoGetAgileReference API を䜿甚するこずをお勧めしたす。 Windows 8 より前のバヌゞョンでは、 グロヌバル むンタヌフェむス テヌブルを怜蚎しおください。

  • セキュリティ – COM は、クラスのアクティブ化ずプロキシアクセス蚱可のための豊富なセキュリティ モデルを提䟛したす。