.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
を処理する必要があることを示すには、DllImportAttribute
の PreserveSig
フィールドを false
に設定します。 例として、同じネイティブ メソッドとなる 2 つの P/Invoke のシグネチャを以下に示します。1 つは PreserveSig
を false
に設定し、もう 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
のビットが構造体として再解釈されます。
関連項目
.NET