エクスポート時のメンバーの変換
このトピックでは、次のメンバーがエクスポート プロセスでどのように変換されるかを説明します。
メソッド
プロパティ
イベント
メソッド
COM のクライアントは、パラメーターなどの使い慣れた COM のデータ型を渡し、HRESULT を戻り値として受け取るメソッドを呼び出そうとします。 しかし、.NET の世界では、クラスは戻り値の型に対してこのような規定はありません。実際、HRESULT は使用されません。
COM と .NET の両方のモデルを満足させるには、マネージ型のすべてのメソッドに、.NET シグネチャと暗黙の COM シグネチャを含めます。 通常、この 2 つのシグネチャは大きく異なります。 .NET クライアントは、.NET シグネチャを使用してサーバーとやりとりします。一方、COM クライアントは、COM シグネチャを使用してサーバーとやりとりします。これらのやりとりは、同時に起こることもあります。 サーバーは、.NET シグネチャを持つメソッドを実装し、ランタイム マーシャリング サービスが、マネージ メソッドに呼び出しをデリゲートする COM シグネチャを持つスタブの提供を担当します。
HRESULT の変換
マネージ戻り値を [out, retval] パラメーターに変更し、アンマネージ戻り値の型を HRESULT に変更することにより、マネージ シグネチャがアンマネージ シグネチャに変換されます。 たとえば、DoSomething メソッドが、次のシグネチャを持っているとします。
マネージ シグネチャ
short DoSomething(short i);
アンマネージ シグネチャ
HRESULT DoSomething([in] short i, [out, retval] short *rv);
COM シグネチャが HRESULT を返し、戻り値として追加の out パラメーターを持つことに注意してください。 マネージ実装からの戻り値は、常に [out, retval] パラメーターとして返され、アンマネージ シグネチャの末尾に追加されます。一方、アンマネージ シグネチャは常に HRESULT を返します。 マネージ メソッドの戻り値の型が void の場合、ランタイムは、[out, retval] パラメーターを省略します。 次に例を示します。
マネージ シグネチャ
void DoSomething(short i);
アンマネージ シグネチャ
HRESULT DoSomething([in] short i);
一定の条件下では、マネージ シグネチャをそのまま変更しない方がいい場合もあります。 マネージ シグネチャを変更しない場合は、PreserveSigAttribute を使用します。 次に例を示します。
マネージ シグネチャ
[PreserveSig] short DoSomething(short i);
アンマネージ シグネチャ
short DoSomething ([in] short i);
2 つの異なるメソッド シグネチャを持つことにより、COM と .NET クライアントの両方からシームレスにクラスを使用できます。 さらに、COM クライアントと .NET クライアントの両方が、同時に .NET クラスを使用できます。 クラスの作成者は、マネージ シグネチャだけを実装します。 Tlbexp.exe (または同等の API) を使用すると、そのクラスに対して生成されるタイプ ライブラリに、シグネチャが自動的にエクスポートされます。
オーバーロードされたメソッド
.NET はオーバーロードされたメソッドをサポートしていますが、IDispatch インターフェイスは、バインディングのために、完全メソッド シグネチャではなく、メソッド名だけを使用します。 したがって、オーバーロードされたメソッドをサポートできません。 ただし、ある型のオーバーロードされたメソッドへのアクセスを提供するために、Tlbexp.exe は、オーバーロードされたメソッドの名前を序数で修飾して、各メソッド名が一意になるようにします。
序数による修飾の例を次のマネージ シグネチャとアンマネージ シグネチャで示します。
マネージ シグネチャ
interface INew {
public:
void DoSomething();
void DoSomething(short s);
void DoSomething(short l);
void DoSomething(float f);
void DoSomething(double d);
}
アンマネージ シグネチャ
interface INew {
void DoSomething();
void DoSomething_2(short s);
void DoSomething_3(short l);
void DoSomething_4(float f);
void DoSomething_5(double d);
}
これらのメソッドの COM シグネチャは、後に一連の修飾された DoSomething_x メソッドが続く 1 つの DoSomething メソッドとして表現されています。x は、2 から始まり、メソッドのオーバーロードされた形態ごとに値が増加しています。 オーバーロードされたメソッドの中には、基本型から継承できるものもあることに注意してください。 オーバーロードされたメソッドが、増加していく型バージョンと同じ番号を保持する保証はありません。
.NET クライアントは、メソッドのオーバーロードされた形式を使用できますが、COM クライアントは、修飾されたメソッドにアクセスする必要があります。 オブジェクト ブラウザーは、正しいメソッドを選択できるようにするために、修飾されたメソッドのすべての形式とメソッド シグネチャを表示します。 実行時にバインディングされるクライアントも、修飾された名前を渡して IDispatch::GetIdsOfNames を呼び出し、オーバーロードされたメソッドの DispID を取得できます。
プロパティ
マネージ クラスおよびマネージ インターフェイスは、プロパティを持つことができます。 マネージ プロパティは、get メソッドおよび set メソッドを関連付けることができる特別のデータ型です。 これらのメソッドは、他のメソッドと同様に、個別に定義します。 Height プロパティを持つインターフェイスのコード例を次に示します。 プロパティを持つインターフェイスを実装するクラスは、プロパティの get メソッドおよび set メソッドを提供する必要があります。
interface IMammal {
IMammal Mother{get;set;}
IMammal Father{get;set;}
int Height{get;set;}
int Weight{get;set;}
}
class Human : IMammal
{
int weight;
int height;
IMammal father;
IMammal mother;
public IMammal Mother { get { return mother; } set { mother = value; } }
public IMammal Father { get { return father; } set { father = value; } }
public int Height { get { return height; } set { height = value; } }
public int Weight { get { return weight; } set { weight = value; } }
}
エクスポート時に、Tlbexp.exe はプロパティ set メソッドを [propput] に変換し、get メソッドを [propget] に変換します。 COM でのプロパティ名は、マネージ プロパティ名と同じ名前を維持します。 この規則には、次の例外があります。
プロパティ型 (値型を除く) がクラスまたはインターフェイスの場合は、プロパティ set メソッドは、[propputref] になり、パラメーターに追加の間接レベルが与えられます。
プロパティに get メソッドまたは set メソッドが関連付けられていない場合は、Tlbexp.exe はタイプ ライブラリからのプロパティを省略します。
プロパティと同様に、マネージ フィールドも、タイプ ライブラリにエクスポートされます。 ランタイム マーシャリング サービスは、すべてのパブリック フィールドに対して get メソッドと set メソッドを自動的に生成します。 変換プロセス中、Tlbexp.exe が、フィールドごとに [propput] (または [propputref]) 関数と [propget] 関数を生成するタイプ ライブラリ表現を次に示します。
タイプ ライブラリ表現
interface IMammal : IDispatch {
[propget] HRESULT Mother([out, retval] IMammal** pRetVal);
[propputref] HRESULT Mother([in] IMammal* pRetVal);
[propget] HRESULT Father([out, retval] IMammal** pRetVal);
[propputref] HRESULT Father([in] IMammal* pRetVal);
[propget] HRESULT Height([out, retval] long* pRetVal);
[propput] HRESULT Height([in] long pRetVal);
[propget] HRESULT Weight([out, retval] long* pRetVal);
[propput] HRESULT Weight([in] long pRetVal);
[propget] HRESULT Age([out, retval] long* pRetVal);
[propput] HRESULT Age([in] long pRetVal);
};
イベント
COM 相互運用機能のイベント モデルに詳しくない場合は、「マネージ イベントとアンマネージ イベント」を参照してください。 マネージ型は、デリゲート ベースのイベント モデルを使用してイベントを実装します。 たとえば、次のコード例の Class1Events インターフェイスは、Click イベントを発生させます。
Public Delegate Sub ClickDelegate()
<GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967"), _
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface Class1Event
Sub Click ()
End Interface
<ComSourceInterfaces("Class1Event, EventSrc")> _
Public Class Class1
Public Event Click As ClickDelegate
End Class
public delegate void Click();
public interface Class1Event
{
void Click();
}
[ComSourceInterfaces("Class1Event, EventSrc")]
public class Class1
{
public event ClickDelegate Click;
}
エクスポート時に、Tlbexp.exe はコクラス内でイベント インターフェイスをソースとしてマークします。 エクスポートされた ComClass1Events インターフェイスがソース インターフェイスとしてマークされているタイプ ライブラリ表現を次に示します。
タイプ ライブラリ表現
disinterface Class1Event {
properties:
methods:
[id(0x60020000)]
HRESULT Click();
};
coclass Class1
{
…
[default, source] Class1Event;
};