.NET の相互運用における暗黙的なメソッド シグネチャの変換

プログラミング言語に依存しないようにするために、Windows COM システムと多くの Windows API は、API が成功したか失敗したかを示す HRESULT と呼ばれる 4 バイトの整数型を、エラーに関する情報とともに返します。 呼び出し元に渡す必要があるその他の値は、"out" パラメーターとして機能するポインター パラメーターを介して "返され"、通常はシグネチャの最後のパラメーターになります。 C# や Visual Basic などの言語は、従来、その言語での通常の失敗の伝播方法に合わせてエラー コードを例外に変換し、相互運用のメソッド シグネチャに HRESULT が含まれないことを想定していました。 メソッド シグネチャをネイティブ シグネチャに変換するために、ランタイムはメソッドの戻り値をもう 1 レベルの間接参照を使用して追加の "out" パラメーターに移して (つまり、マネージド シグネチャの戻り値の型へのポインターにして)、HRESULT 戻り値を想定します。 マネージド メソッドが void を返す場合、パラメーターは追加されず、戻り値は HRESULT になります。 たとえば、同じネイティブ シグネチャに変換される 2 つの C# COM メソッドを次に示します。

int Add(int a, int b);

void Add(int a, int b, out int sum);
HRESULT Add(int a, int b, /* out */ int* sum);

COM での PreserveSig

C# のすべての COM メソッドは、既定で変換されたシグネチャを使うことが想定されています。 シグネチャの変換や HRESULT 値の処理を行わずにメソッドを使用およびエクスポートするには、COM インターフェイス メソッドに PreserveSigAttribute を追加します。 属性がメソッドに適用される場合、シグネチャへの変換は行われず、エラーの HRESULT 値に対する例外はスローされません。 これは、組み込みの COM とソース生成の COM の両方に適用されます。 例として、PreserveSig 属性を含む C# メソッド シグネチャと、それに対応するネイティブ シグネチャを以下に示します。

[PreserveSig]
int Add(int a, int b, out int sum);
HRESULT Add(int a, int b, int* sum);

これは、メソッドがエラーではないさまざまな HRESULT 値を返す可能性があるが、別々に処理する必要がある場合に便利です。 たとえば、一部のメソッドでは、メソッドが失敗せずに部分的な結果のみを返す場合には値 S_FALSE を返し、すべての結果を返す場合には値 S_OK を返すことがあります。

P/Invoke での PreserveSig

DllImportAttribute 属性には、PreserveSigAttribute と同様に機能する bool PreserveSig フィールドもありますが、既定値は true です。 ランタイムがマネージド シグネチャを変換し、返された HRESULT を処理する必要があることを示すには、DllImportAttributePreserveSig フィールドを false に設定します。 例として、同じネイティブ メソッドとなる 2 つの P/Invoke のシグネチャを以下に示します。1 つは PreserveSigfalse に設定し、もう 1 つは既定の true 値のままです。

[DllImport("shlwapi.dll", EntryPoint = "SHAutoComplete", ExactSpelling = true, PreserveSig = false)]
public static extern void SHAutoComplete(IntPtr hwndEdit, SHAutoCompleteFlags dwFlags);

[DllImport("shlwapi.dll", EntryPoint = "SHAutoComplete", ExactSpelling = true)]
public static extern int SHAutoCompleteHRESULT(IntPtr hwndEdit, SHAutoCompleteFlags dwFlags);

Note

LibraryImportAttribute を使うソース生成の P/Invoke には、PreserveSig フィールドがありません。 生成されたコードでは、ネイティブとマネージドのシグネチャが常に同一であると想定されます。 詳細については、ソース生成の P/Invoke に関するページを参照してください。

HRESULT 値を手動で処理する

HRESULT を返す PreserveSig メソッドを呼び出すとき、HRESULT がエラーを示している場合は、ThrowExceptionForHR メソッドを使って対応する例外をスローできます。 同様に、PreserveSig メソッドを実装する場合、GetHRForException メソッドを使って、例外に対応する値を示す HRESULT を返すことができます。

HRESULT を構造体としてマーシャリングする

PreserveSig メソッドを使う場合、HRESULT のマネージド型は int であると想定されます。 ただし、戻り値の型としてカスタムの 4 バイト構造体を使用すると、HRESULT の操作を簡素化できるヘルパー メソッドとプロパティを定義できます。 組み込みのマーシャリングでは、これは自動的に機能します。 ソース生成のマーシャリングで HRESULT のマネージド表現として int の代わりに構造体を使用するには、引数として Error を指定して MarshalAsAttribute 属性を追加します。 この属性が存在すると、HRESULT のビットが構造体として再解釈されます。

関連項目