System.Exception.HResultプロパティは保護されています。リフレクションやその他の醜いハックに頼ることなく、例外の内部を覗いてHResultを取得するにはどうすればよいですか?
状況は次のとおりです。
システム上のファイルを開いて読み取るバックアップツールを作成したいと思います。 このガイダンス に従って、FileAccess.ReadとFileShare.ReadWriteを使用してファイルを開きます。これは、ファイルを読み取ったときにファイルが書き込み用に開いているかどうかを気にしないためです。
場合によっては、読み取っているファイルが別のアプリによって開かれていると、System.IO.FileStream.Read()メソッドがSystem.IO.IOExceptionをスローします。「別のプロセスがファイルの一部をロックしているため、プロセスはファイルにアクセスできません。ファイル"。これは エラー 、またはHResult0x80070021だと思います。 [[〜#〜] edit [〜#〜]:別のプロセスが LockFileEx を呼び出したときにこれを返すことができると思いますファイル内のバイト範囲をロックします。]
このエラーが発生したら、一時停止して再試行したいと思います。これがここでとるべき適切な行動だと思います。ロックプロセスがバイト範囲ロックをすばやく解放する場合は、ファイルの読み取りを続行できます。
この理由でIOExceptionを他の理由と区別するにはどうすればよいですか?私はこれらの方法を考えることができます:
私はこれらのオプションが好きではありません。より良い、よりクリーンな方法はありませんか?
探し回ったところ、 System.Runtime.InteropServices.Marshal.GetHRForException が見つかりました。それは0x80070021のようなuintを返しますか?
.Net Framework 4.5以降の場合、Exception.HResult
プロパティを使用できます。
int hr = ex.HResult;
古いバージョンの場合、 Marshal.GetHRForException
を使用してHResultを取り戻すことができますが、これ 重大な副作用があり、推奨されません :
int hr = Marshal.GetHRForException(ex);
その価値については、System.Exception.HResultは.NET4.5では保護されなくなりました-セッターのみが保護されます。これは、フレームワークの複数のバージョンでコンパイルされる可能性のあるコードには役立ちません。
ISerializable
インターフェースを使用することもできます。
static class IOExceptionExtensions
{
public static int GetHResult(this IOException ex)
{
var info = new SerializationInfo(typeof (IOException), new FormatterConverter());
ex.GetObjectData(info, new StreamingContext());
return info.GetInt32("HResult");
}
}
この場合、CanRead
プロパティは役に立ちますか?
つまりCanRead
を呼び出し、それがtrueを返す場合は、Read()
を呼び出します。
これらのケースのいずれかをプロファイリングしましたか?リフレクションメソッドは、特にアプリが実行する他のすべての作業と比較して、それほど遅くはなく、この例外が発生する可能性が高いと思います。
ボトルネックであることが判明した場合は、リフレクション操作の一部をキャッシュするか、動的ILを生成してプロパティを取得することを検討できます。
ネクロマンシング。
または、リフレクションによって保護されたプロパティをフェッチすることもできます。
private static int GetHresult(System.Exception exception)
{
int retValue = -666;
try
{
System.Reflection.PropertyInfo piHR = typeof(System.Exception).GetProperty("HResult", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
if (piHR != null)
{
object o = piHR.GetValue(exception, null);
retValue = System.Convert.ToInt32(o);
}
}
catch (Exception ex)
{
}
return retValue;
}