如何确定System.IO.IOException的HResult?

时间:2009-06-13 21:23:21

标签: c# .net exception hresult

System.Exception.HResult属性受保护。我怎样才能偷看异常并获得HResult而不诉诸于反思或其他丑陋的黑客攻击?


情况如下:
我想写一个备份工具,它打开并读取系统上的文件。 我根据this guidance使用FileAccess.Read和FileShare.ReadWrite打开文件,因为在我阅读文件时我不关心文件是否打开。

在某些情况下,当我正在阅读的文件被另一个应用程序打开时,System.IO.FileStream.Read()方法抛出一个System.IO.IOException,“进程无法访问该文件,因为另一个进程已锁定文件的一部分“。这是error 33,或者我认为是HResult 0x80070021。 [编辑:我相信当另一个进程调用LockFileEx来锁定文件中的字节范围时,可以返回此内容。]

当我收到此错误时,我想暂停并重试。我认为这是采取适当的行动。如果锁定进程快速释放字节范围锁,那么我可以继续读取该文件。

出于这个原因,我如何区分IOException?我可以想到这些方法:

  • 私人反思 - 不想这样做。 Perf会发臭。
  • 调用Exception.ToString()并解析字符串。感觉hacky。在i18n版本中不起作用。

我不喜欢这些选项。是不是有更好,更清洁的方式?


我只是四处搜寻,发现System.Runtime.InteropServices.Marshal.GetHRForException。会返回像0x80070021这样的uint吗?

6 个答案:

答案 0 :(得分:55)

对于.Net Framework 4.5及更高版本,您可以使用Exception.HResult属性:

int hr = ex.HResult;

对于旧版本,您可以使用Marshal.GetHRForException取回HResult,但has significant side-effects and is not recommended

int hr = Marshal.GetHRForException(ex);

答案 1 :(得分:11)

对于它的价值, System.Exception.HResult 在.NET 4.5中不再受保护 - 只有setter受到保护。这对可能使用多个版本的框架编译的代码没有帮助。

答案 2 :(得分:3)

您还可以使用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");
    }
}

答案 3 :(得分:0)

CanRead属性在这种情况下是否有帮助?
即调用CanRead,如果返回true,则调用Read()

答案 4 :(得分:0)

您是否描述过这两种情况?我认为反射方法并不是那么慢,特别是相对于你的应用程序将要做的所有其他工作以及这种异常可能发生的频率。

如果结果是瓶颈,你可以考虑缓存一些反射操作或生成动态IL来检索属性。

答案 5 :(得分:0)

创新。
或者,您也可以通过反射来获取受保护的属性:

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;
}