2009-03-23 5 views
18

最近、Win32 APIとの相互運用を含む多くのコードを書いており、Windows API関数の呼び出しによって引き起こされるネイティブ(アンマネージド)エラーを処理する最善の方法を知り始めました。Win32Exceptionをスローする

現在、ネイティブ関数への呼び出しは次のようになり:

は例外を発生させ回線が同等なように書き換えることができる

// NativeFunction returns true when successful and false when an error 
// occurred. When an error occurs, the MSDN docs usually tell you that the 
// error code can be discovered by calling GetLastError (as long as the 
// SetLastError flag has been set in the DllImport attribute). 
// Marshal.GetLastWin32Error is the equivalent managed function, it seems. 
if (!WinApi.NativeFunction(param1, param2, param3)) 
    throw new Win32Exception(); 
は、私は信じている:今
throw new Win32Exception(Marshal.GetLastWin32Error()); 

が、これがすべてですよく設定されているWin32エラーコードと例外オブジェクトのプロパティとして人間が判読可能な(一般的な)説明を適切に含む例外がスローされます。しかし、私は、これらの例外のすべてではないにしても、少なくともいくつかを修正/ラップすることが推奨されています。つまり、コンテキスト指向のエラーメッセージが多少出ます。つまり、ネイティブコードがどんな状況であっても意味があります。使用されています。 Win32Exceptionのコンストラクタでカスタムエラーメッセージを指定

  1. :私はこのためにいくつかの選択肢を検討しています。

    throw new Win32Exception(Marshal.GetLastWin32Error(), "My custom error message."); 
    
  2. 元のエラーコードとメッセージの両方が保持されるように(Win32Exceptionは現在親例外のInnerExceptionである)別の例外オブジェクトにWin32Exceptionラッピング。

    throw new Exception("My custom error message.", Win32Exception(Marshal.GetLastWin32Error())); 
    
  3. ラッパー例外として別のWin32Exceptionを用いる以外は2と同じ。

  4. Exceptionから派生したカスタムクラスをラッパー例外として使用する点を除いて、2と同じです。

  5. 2と同じですが、例外として、BCL(基本クラスライブラリ)例外を親として使用する点が異なります。この場合、InnerExceptionWin32Exceptionに設定するのが適切かどうかは不明です(おそらく、低レベルのラッパーではありますが、Win32の相互運用機能がバックグラウンドで行われていることを明らかにしていない高水準/抽象インターフェースではありません)。

本質的に私が知りたいのは、.NETのWin32エラーに対処するために推奨されるプラクティスは何ですか?私はそれをオープンソースコードでさまざまな方法で行っていますが、設計上のガイドラインがあるかどうかは不思議でした。そうでない場合、私はここであなたの個人的な好みに興味を持っています。 (おそらく、あなたは上記の方法のどれも使用しないでしょうか?)

答えて

4

これは実際にはWin32の例外に特有ではありません。問題は2つの異なるエラーケースが2つの異なる例外派生タイプによって識別されるべきときと、それらが内部に格納された異なる値を持つ同じタイプをいつ投げるべきかということです。

残念ながら、これはあなたのコードが呼び出されるすべての状況を事前に知らなくても答えることは不可能です。これはタイプによって例外をフィルタリングできるだけの問題です。大まかに言えば、2つのエラーケースを別々に扱うことが有用であるという強い感情がある場合は、異なるタイプをスローします。

それ以外の場合は、Exception.Messageによって返された文字列がユーザーに記録または表示される必要があることがよくあります。

追加情報がある場合は、自分自身のより高いレベルでWin32Exceptionをラップします。たとえば、ファイルに対して何かをしようとしているときに、実行しているユーザーにはその権限がありません。 Win32Exceptionをキャッチし、自分自身の例外クラスにラップします。そのクラスのメッセージには、ファイル名と操作が試みられ、内部例外のメッセージが続きます。

+2

+1その他の情報 - 「ファイルが見つかりません」という例外が表示されない場合は、ファイルが見つからないという例外が表示されます。 –

+0

@Earwicker:あなたは公正なポイントを作っています...また、Harperが指摘しているように、頻繁に(必ずしもそうではありませんが)むしろあいまいで役に立たない傾向があるため、Win32Exceptionとの関連でこの質問をしています。 – Noldorin

+0

最後の段落を明確にするために、内側のエラーメッセージ*を内側の例外としてラップするのが最善であることを意味しますか? – Noldorin

3

私の見解では、これを処理する適切な方法は、ターゲットオーディエンスとクラスの使用方法によって異なります。

クラス/メソッドの呼び出し元が、ある形式または別の形式でWin32に呼び出していることを認識している場合は、指定したオプション1)を使用します。これは私にとって最も「はっきりしている」ようです。 (ただし、この場合は、Win32 APIが直接使用されることを明示する方法でクラス名を付けます)。つまり、BCLにはWin32Exceptionを実際にサブクラス化する例外があり、単にラップするのではなく、より明確になるという例外があります。たとえば、SocketExceptionはWin32Exceptionから派生します。私は個人的にこのアプローチを使用したことはありませんが、これを処理する潜在的にクリーンな方法のように見えます。

クラスの呼び出し元が、Win32 APIを直接呼び出しているとは思わない場合は、例外を処理し、定義したより具体的なカスタム例外を使用します。たとえば、クラスを使用していて、Win32 APIを使用しているという表示がない場合(なぜなら、特定の非自明な理由で内部的に使用しているため)、私はWin32Exceptionを処理する必要があります。あなたはいつもこれを文書化することができますが、それをトラップして特定のビジネスコンテキストでより意味を持つ例外を与えることは、私にとってはより合理的です。この場合、私は最初のWin32Exceptionを内部例外(つまり、ケース4)としてラップするかもしれませんが、内部例外の原因によっては、そうでないかもしれません。

また、ネイティブ呼び出しからWin32Exceptionがスローされることがありますが、BCLには他にも関連性の高い例外があります。これは、ラップされていないネイティブAPIを呼び出している場合ですが、BCLにラップされた同様の関数があります。その場合、私はおそらく例外をトラップし、それが私が期待しているものであることを確認しますが、標準のBCL例外をその場所にスローします。これの良い例は、Win32Exceptionをスローする代わりにSecurityExceptionを使用することです。

一般的に、私はあなたがリストしたオプション2と3を避けます。

オプション2は一般的な例外タイプをスローします - 私はそれを完全に避けることをお勧めします。特定の例外をより一般化されたものにするのは無理です。

オプション3は、冗長なようだ - 何の利点は、個人的に私は、好ましくは#2または#4 ...#4を行うだろう

+0

提案していただきありがとうございます。自分の状況では、コードを含むクラスが本当に他のプロセス/スレッド/共有メモリを操作するもののラッパーであるため、Win32Exceptionを直接スローするのが最善の方法かもしれません。 – Noldorin

+0

(続き)BCL例外の投棄(およびWin32のもののラッピング)も適切です。オプション4は、Win32相互運用機能が使用されているという事実(すなわち、比較的高水準のインタフェース)を隠そうとしているときに、最高のように見えます。 – Noldorin

2

オプション1の上に本当にありません。あなたの例外の中でWin32Exceptionをラップします。これはコンテキスト依存です。このように:

void ReadFile() 
{ 
    if (!WinApi.NativeFunction(param1, param2, param3)) 
     throw MyReadFileException("Couldn't read file", new Win32Exception()); 
} 

このように、誰かが例外をキャッチすると、問題が発生した場所がわかります。私はあなたのテキストエラーメッセージを解釈するためにキャッチが必要なので#1はしません。そして、#3は本当に追加情報を与えません。

+0

合意。オプション4はおそらく、Win32の例外を隠したいときに使うでしょう - おそらくBTPの例外を使って、適切なところでカスタムのものを使わないでください。 – Noldorin

関連する問題