COM 呼び出し可能ラッパーCOM Callable Wrapper

COM クライアントが .NET オブジェクトを呼び出すと、共通言語ランタイムがマネージド オブジェクトとそのオブジェクトのための COM 呼び出し可能ラッパー (CCW: COM Callable Wrapper) を作成します。When a COM client calls a .NET object, the common language runtime creates the managed object and a COM callable wrapper (CCW) for the object. COM クライアントは .NET オブジェクトを直接参照できないため、CCW をマネージド オブジェクトのプロキシとして使用します。Unable to reference a .NET object directly, COM clients use the CCW as a proxy for the managed object.

ランタイムは、サービスを要求している COM クライアントの数に関係なく、1 つのマネージド オブジェクトに対して 1 つの CCW を作成します。The runtime creates exactly one CCW for a managed object, regardless of the number of COM clients requesting its services. 次の図に示すように、複数の COM クライアントが、INew インターフェイスを公開する CCW への参照を保持できます。As the following illustration shows, multiple COM clients can hold a reference to the CCW that exposes the INew interface. CCW は、INew インターフェイスを実装するマネージド オブジェクトへの 1 つの参照を保持し、ガベージ コレクションされます。The CCW, in turn, holds a single reference to the managed object that implements the interface and is garbage collected. COM クライアントと .NET クライアントは、同一のマネージド オブジェジェクトに対して同時に要求できます。Both COM and .NET clients can make requests on the same managed object simultaneously.

INew を公開する CCW への参照を保持している複数の COM クライアント。

CCW は、.NET Framework. 内で実行されている他のクラスからは見えません。COM callable wrappers are invisible to other classes running within the .NET Framework. CCW の主な目的は、マネージド コードとアンマネージド コードの間の呼び出しをマーシャリングすることですが、CCW は、CCW にラップされているマネージド オブジェクトのオブジェクト ID やオブジェクトの有効期間の管理も行います。Their primary purpose is to marshal calls between managed and unmanaged code; however, CCWs also manage the object identity and object lifetime of the managed objects they wrap.

オブジェクト IDObject Identity

ランタイムは、ランタイムのガベージ コレクション ヒープから .NET オブジェクトにメモリを割り当てます。これにより、ランタイムは、メモリ内で必要に応じてオブジェクトを移動させることができます。The runtime allocates memory for the .NET object from its garbage-collected heap, which enables the runtime to move the object around in memory as necessary. 一方、CCW に対して、ランタイムは、コレクション ヒープ以外からメモリを割り当てます。これにより、COM クライアントはラッパーを直接参照できます。In contrast, the runtime allocates memory for the CCW from a noncollected heap, making it possible for COM clients to reference the wrapper directly.

オブジェクトの有効期間Object Lifetime

CCW にラップされている .NET クライアントとは異なり、CCW は、従来の COM 方式で参照カウントを使用します。Unlike the .NET client it wraps, the CCW is reference-counted in traditional COM fashion. CCW の参照カウントが 0 (ゼロ) に達すると、ラッパーはマネージド オブジェクトの参照を解放します。When the reference count on the CCW reaches zero, the wrapper releases its reference on the managed object. 参照が残っていないマネージド オブジェクトは、次のガベージ コレクション サイクルで収集されます。A managed object with no remaining references is collected during the next garbage-collection cycle.

COM インターフェイスのシミュレートSimulating COM interfaces

CCW は、パブリックで COM から参照できるすべてのインターフェイス、データ型、および戻り値を、COM によるインターフェイス ベースの対話の適用と整合性のある方法で COM クライアントに公開します。CCW exposes all public, COM-visible interfaces, data types, and return values to COM clients in a manner that is consistent with COM's enforcement of interface-based interaction. COM クライアントの場合、.NET Framework オブジェクトのメソッドを呼び出すことは COM オブジェクトのメソッドを呼び出すことと同じです。For a COM client, invoking methods on a .NET Framework object is identical to invoking methods on a COM object.

このシームレスなアプローチを実現するために、CCW は IUnknownIDispatch などの従来の COM インターフェイスを製造します。To create this seamless approach, the CCW manufactures traditional COM interfaces, such as IUnknown and IDispatch. 次の図が示すように、CCW は、ラップしている .NET オブジェクトの 1 つの参照を保持します。As the following illustration shows, the CCW maintains a single reference on the .NET object that it wraps. COM クライアントと .NET オブジェクトの両方は、CCW のプロキシとスタブ構築を介して相互に対話します。Both the COM client and the .NET object interact with each other through the proxy and stub construction of the CCW.

CCW が COM インターフェイスを製造する方法を示す図。

マネージド環境でクラスによって明示的実装されるインターフェイスを公開するだけでなく、.NET Framework は、オブジェクトの代わりに、次の表にリストされている COM インターフェイスの実装を提供します。In addition to exposing the interfaces that are explicitly implemented by a class in the managed environment, the .NET Framework supplies implementations of the COM interfaces listed in the following table on behalf of the object. .NET クラスは、これらのインターフェイスの独自の実装を提供することで、既定の動作をオーバーライドできます。A .NET class can override the default behavior by providing its own implementation of these interfaces. ただし、ランタイムは IUnknownIDispatch インターフェイス実装を常に提供します。However, the runtime always provides the implementation for the IUnknown and IDispatch interfaces.

InterfaceInterface 説明Description
IDispatchIDispatch 型への遅延バインディングのメカニズムを提供します。Provides a mechanism for late binding to type.
IErrorInfoIErrorInfo エラー、そのソース、ヘルプ ファイル、ヘルプ コンテキスト、およびエラーを定義したインターフェイスの GUID (.NET クラスでは常に GUID_NULL) に関する説明文を示します。Provides a textual description of the error, its source, a Help file, Help context, and the GUID of the interface that defined the error (always GUID_NULL for .NET classes).
IProvideClassInfoIProvideClassInfo マネージド クラスによって実装される ITypeInfo インターフェイスに COM クライアントがアクセスできるようにします。Enables COM clients to gain access to the ITypeInfo interface implemented by a managed class.
ISupportErrorInfoISupportErrorInfo マネージド オブジェクトが IErrorInfo インターフェイスをサポートするかどうかを COM クライアントが判別できるようにします。Enables a COM client to determine whether the managed object supports the IErrorInfo interface. その場合は、クライアントが最新の例外オブジェクトへのポインターを取得できるようにします。If so, enables the client to obtain a pointer to the latest exception object. すべてのマネージド型は、IErrorInfo インターフェイスをサポートします。All managed types support the IErrorInfo interface.
ITypeInfoITypeInfo Tlbexp.exe によって生成された型情報と完全に等しい、クラスの型情報を提供します。Provides type information for a class that is exactly the same as the type information produced by Tlbexp.exe.
IUnknownIUnknown COM クライアントが CCW の有効期間を管理し、強制型変換を提供するための、IUnknown インターフェイスの標準的な実装を提供します。Provides the standard implementation of the IUnknown interface with which the COM client manages the lifetime of the CCW and provides type coercion.

マネージド クラスは、次の表で説明されている COM インターフェイスを提供することもできます。A managed class can also provide the COM interfaces described in the following table.

InterfaceInterface 説明Description
(_classname) クラス インターフェイスThe (_classname) class interface マネージド オブジェクトで明示的に公開されている、すべてのパブリック インターフェイス、メソッド、プロパティ、およびフィールドを公開する、ランタイムによって公開され、明示的に定義されていない、インターフェイス、Interface, exposed by the runtime and not explicitly defined, that exposes all public interfaces, methods, properties, and fields that are explicitly exposed on a managed object.
IConnectionPointIConnectionPointContainerIConnectionPoint and IConnectionPointContainer デリゲート ベースのソース イベント (イベント サブスクライバーを登録するためのインターフェイス) を供給するオブジェクトのインターフェイス。Interface for objects that source delegate-based events (an interface for registering event subscribers).
IDispatchExIDispatchEx クラスが IExpando を実装する場合、ランタイムによって提供されているインターフェイス。Interface supplied by the runtime if the class implements IExpando. IDispatchEx インターフェイスは、IDispatch インターフェイスの拡張版で、IDispatch とは異なり、列挙、追加、削除、および大文字小文字を区別したメンバーの呼び出しが可能になります。The IDispatchEx interface is an extension of the IDispatch interface that, unlike IDispatch, enables enumeration, addition, deletion, and case-sensitive calling of members.
IEnumVARIANTIEnumVARIANT クラスが IEnumerable を実装する場合、コレクション内のオブジェクトを列挙するコレクション型クラスのインターフェイス。Interface for collection-type classes, which enumerates the objects in the collection if the class implements IEnumerable.

クラス インターフェイスの概要Introducing the class interface

マネージド コードで明示的に定義されていないクラス インターフェイスは、.NET オブジェクトで明示的に公開されるすべてのパブリック メソッド、プロパティ、フィールド、およびイベントを公開するインターフェイスです。The class interface, which is not explicitly defined in managed code, is an interface that exposes all public methods, properties, fields, and events that are explicitly exposed on the .NET object. このインターフェイスは、デュアルまたはディスパッチ専用インターフェイスです。This interface can be a dual or dispatch-only interface. クラス インターフェイスは、前にアンダー スコアの付いた、.NET クラス自体の名前を受け取ります。The class interface receives the name of the .NET class itself, preceded by an underscore. たとえば、クラス Mammal の場合、クラス インターフェイスは _Mammal です。For example, for class Mammal, the class interface is _Mammal.

派生クラスの場合、クラス インターフェイスは、基本クラスのすべてのパブリック メソッド、プロパティ、およびフィールドも公開します。For derived classes, the class interface also exposes all public methods, properties, and fields of the base class. 派生クラスは、各基本クラスのクラス インターフェイスも公開します。The derived class also exposes a class interface for each base class. たとえば、クラス Mammal がクラス MammalSuperclass を拡張し、そのクラスがさらに System.Object を拡張する場合、.NET オブジェクトは COM クライアントに _Mammal、_MammalSuperclass、および _Object という名前の 3 つのクラス インターフェイスを公開します。For example, if class Mammal extends class MammalSuperclass, which itself extends System.Object, the .NET object exposes to COM clients three class interfaces named _Mammal, _MammalSuperclass, and _Object.

たとえば、次の .NET クラスを考えます。For example, consider the following .NET class:

' Applies the ClassInterfaceAttribute to set the interface to dual.
<ClassInterface(ClassInterfaceType.AutoDual)> _
' Implicitly extends System.Object.
Public Class Mammal
    Sub Eat()
    Sub Breathe()
    Sub Sleep()
End Class
// Applies the ClassInterfaceAttribute to set the interface to dual.
[ClassInterface(ClassInterfaceType.AutoDual)]
// Implicitly extends System.Object.
public class Mammal
{
    public void Eat() {}
    public void Breathe() {}
    public void Sleep() {}
}

COM クライアントは、_Mammal という名前のクラス インターフェイスへのポインターを取得できます。これについては、タイプ ライブラリ エクスポーター (Tlbexp.exe) ツールで生成されるタイプ ライブラリで説明されています。The COM client can obtain a pointer to a class interface named _Mammal, which is described in the type library that the Type Library Exporter (Tlbexp.exe) tool generates. Mammal クラスが 1 つ以上のインターフェイスを実装した場合、それらのインターフェイスはコクラスの下に表示されます。If the Mammal class implemented one or more interfaces, the interfaces would appear under the coclass.

[odl, uuid(…), hidden, dual, nonextensible, oleautomation]
interface _Mammal : IDispatch
{
    [id(0x00000000), propget] HRESULT ToString([out, retval] BSTR*
        pRetVal);
    [id(0x60020001)] HRESULT Equals([in] VARIANT obj, [out, retval]
        VARIANT_BOOL* pRetVal);
    [id(0x60020002)] HRESULT GetHashCode([out, retval] short* pRetVal);
    [id(0x60020003)] HRESULT GetType([out, retval] _Type** pRetVal);
    [id(0x6002000d)] HRESULT Eat();
    [id(0x6002000e)] HRESULT Breathe();
    [id(0x6002000f)] HRESULT Sleep();
}
[uuid(…)]
coclass Mammal
{
    [default] interface _Mammal;
}

クラス インターフェイスの生成はオプションです。Generating the class interface is optional. 既定では、COM 相互運用が、タイプ ライブラリにエクスポートするクラスごとにディスパッチ専用インターフェイスを生成します。By default, COM interop generates a dispatch-only interface for each class you export to a type library. ClassInterfaceAttribute をクラスに適用することによって、このインターフェイスの自動作成を防止または変更することができます。You can prevent or modify the automatic creation of this interface by applying the ClassInterfaceAttribute to your class. クラス インターフェイスにより、マネージド クラスを COM に公開するタスクを軽減できますが、その使用は制限されています。Although the class interface can ease the task of exposing managed classes to COM, its uses are limited.

注意事項

独自のものを明示的に定義する代わりにクラス インターフェイスを使用すると、マネージド クラスの将来のバージョン管理が複雑になることがあります。Using the class interface, instead of explicitly defining your own, can complicate the future versioning of your managed class. クラス インターフェイスを使用する前に、次のガイドラインを参照してください。Please read the following guidelines before using the class interface.

クラス インターフェイスを生成するのではなく、COM クライアントが使用する明示的なインターフェイスを定義します。Define an explicit interface for COM clients to use rather than generating the class interface.

COM 相互運用はクラス インターフェイスを自動的に生成するため、クラスにバージョン後の変更が生じると、共通言語ランタイムによって公開されているクラス インターフェイスのレイアウトが変更されることがあります。Because COM interop generates a class interface automatically, post-version changes to your class can alter the layout of the class interface exposed by the common language runtime. COM クライアントは通常、インターフェイスのレイアウトの変更を処理するように準備されていないため、クラスのメンバー レイアウトを変更した場合に、クライアントが破損します。Since COM clients are typically unprepared to handle changes in the layout of an interface, they break if you change the member layout of the class.

このガイドラインは、COM クライアントに公開されるインターフェイスは変更不可にしておく必要があるという概念を促進しています。This guideline reinforces the notion that interfaces exposed to COM clients must remain unchangeable. 間違えてインターフェイスのレイアウトを並べ替えることで COM クライアントが破損するリスクを抑えるため、インターフェイスを明示的に定義して、クラスに対するすべての変更をインターフェイス レイアウトから分離します。To reduce the risk of breaking COM clients by inadvertently reordering the interface layout, isolate all changes to the class from the interface layout by explicitly defining interfaces.

ClassInterfaceAttribute を使用してクラス インターフェイスの自動生成を中止し、次のコード フラグメントに示すように、クラスの明示的なインターフェイスを実装します。Use the ClassInterfaceAttribute to disengage the automatic generation of the class interface and implement an explicit interface for the class, as the following code fragment shows:

<ClassInterface(ClassInterfaceType.None)>Public Class LoanApp
    Implements IExplicit
    Sub M() Implements IExplicit.M
…
End Class
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit
{
    int IExplicit.M() { return 0; }
}

ClassInterfaceType.None 値により、クラス メタデータをタイプ ライブラリにエクスポートするときに、クラス インターフェイスが生成されなくなります。The ClassInterfaceType.None value prevents the class interface from being generated when the class metadata is exported to a type library. 前の例では、COM クライアントは IExplicit インターフェイスを通してのみ LoanApp クラスにアクセスできます。In the preceding example, COM clients can access the LoanApp class only through the IExplicit interface.

ディスパッチ識別子 (DISPID) をキャッシュしないようにします。Avoid caching dispatch identifiers (DispIds)

クラス インターフェイスの使用は、インターフェイス メンバーの DISPID をキャッシュしていないスクリプト化されたクライアント、Microsoft Visual Basic 6.0 クライアント、または遅延バインディング クライアントのために許容されるオプションです。Using the class interface is an acceptable option for scripted clients, Microsoft Visual Basic 6.0 clients, or any late-bound client that does not cache the DispIds of interface members. DISPID は遅延バインディングを有効にするインターフェイス メンバーを特定します。DispIds identify interface members to enable late binding.

クラス インターフェイスでは、インターフェイス内のメンバーの位置に基づいて DISPID が生成されます。For the class interface, generation of DispIds is based on the position of the member in the interface. メンバーの順序を変更してクラスをタイプ ライブラリにエクスポートすると、クラス インターフェイスで生成される DISPID が変更されます。If you change the order of the member and export the class to a type library, you will alter the DispIds generated in the class interface.

クラス インターフェイスを使用するとき、遅延バインドされた COM クライアントの中断を回避するには、ClassInterfaceAttributeClassInterfaceType.AutoDispatch 値を指定して適用します。To avoid breaking late-bound COM clients when using the class interface, apply the ClassInterfaceAttribute with the ClassInterfaceType.AutoDispatch value. この値は、ディスパッチ専用のクラスのインターフェイスを実装しますが、タイプ ライブラリからインターフェイスの説明を省略します。This value implements a dispatch-only class interface, but omits the interface description from the type library. インターフェイスの説明がないと、クライアントはコンパイル時に DISPID をキャッシュできません。Without an interface description, clients are unable to cache DispIds at compile time. これはクラス インターフェイスの既定のインターフェイス型ですが、属性値を明示的に適用することもできます。Although this is the default interface type for the class interface, you can apply the attribute value explicitly.

<ClassInterface(ClassInterfaceType.AutoDispatch)> Public Class LoanApp
    Implements IAnother
    Sub M() Implements IAnother.M
…
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class LoanApp
{
    public int M() { return 0; }
}

実行時にインターフェイス メンバーの DISPID を取得するために、COM クライアントは IDispatch.GetIdsOfNames を呼び出すことができます。To get the DispId of an interface member at run time, COM clients can call IDispatch.GetIdsOfNames. インターフェイスでメソッドを呼び出すには、返された DISPID を IDispatch.Invoke への引数として渡します。To invoke a method on the interface, pass the returned DispId as an argument to IDispatch.Invoke.

クラス インターフェイス用のデュアル インターフェイスのオプションの使用を制限します。Restrict using the dual interface option for the class interface.

デュアル インターフェイスは、COM クライアントによるインターフェイス メンバーへの事前バインディングと遅延バインディングを有効にします。Dual interfaces enable early and late binding to interface members by COM clients. デザイン時およびテスト中は、クラス インターフェイスをデュアルに設定すると役に立つ場合があります。At design time and during testing, you might find it useful to set the class interface to dual. 変更されることのないマネージド クラス (およびその基本クラス) の場合、このオプションも使用できます。For a managed class (and its base classes) that will never be modified, this option is also acceptable. それ以外の場合はすべて、クラス インターフェイスをデュアルに設定しないでください。In all other cases, avoid setting the class interface to dual.

自動的に生成されたデュアル インターフェイスが適切な場合もまれにありますが、より多くの場合、それはバージョンに関連する複雑さを生じさせます。An automatically generated dual interface might be appropriate in rare cases; however, more often it creates version-related complexity. たとえば、派生クラスのクラス インターフェイスを使用する COM クライアントは、基本クラスが変更されると簡単に中断します。For example, COM clients using the class interface of a derived class can easily break with changes to the base class. サード パーティが基本クラスを提供するとき、クラス インターフェイスのレイアウトを自分で制御することはできません。When a third party provides the base class, the layout of the class interface is out of your control. さらに、ディスパッチ専用インターフェイスとは異なり、デュアル インターフェイス (ClassInterfaceType.AutoDual) は、エクスポートされたタイプ ライブラリ内にクラス インターフェイスの説明を提供します。Further, unlike a dispatch-only interface, a dual interface (ClassInterfaceType.AutoDual) provides a description of the class interface in the exported type library. そのような説明により、遅延バインディングのクライアントではコンパイル時に DISPID をキャッシュすることが促進されます。Such a description encourages late-bound clients to cache DispIds at compile time.

すべての COM イベント通知が遅延バインドされていることを確認します。Ensure that all COM event notifications are late-bound.

既定では、COM の型情報はマネージド アセンブリに直接埋め込まれているため、プライマリ相互運用アセンブリ (PIA) の必要はありません。By default, COM type information is embedded directly into managed assemblies, which eliminates the need for primary interop assemblies (PIAs). ただし、埋め込まれた型情報の制限の 1 つとして、早期にバインドされた vtable 呼び出しによる COM イベント通知の配信はサポートされず、遅延バインドされた IDispatch::Invoke 呼び出しのみがサポートされるということがあります。However, one of the limitations of embedded type information is that it does not supported delivery of COM event notifications by early-bound vtable calls, but only supports late-bound IDispatch::Invoke calls.

アプリケーションから COM イベント インターフェイス メソッドに対して早期にバインドされた呼び出しが必要な場合は、Visual Studio の [相互運用型の埋め込み] プロパティを true に設定するか、プロジェクト ファイルに次の要素を含めることができます。If your application requires early-bound calls to COM event interface methods, you can set the Embed Interop Types property in Visual Studio to true, or include the following element in your project file:

<EmbedInteropTypes>True</EmbedInteropTypes>

関連項目See also