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

37

System.Exception.HResult属性是受保护的。如何在不使用反射或其他丑陋的方法的情况下查看异常并获取HResult?


这是情况:
我想编写一个备份工具,用于在系统上打开和读取文件。 根据这个指南,我使用FileAccess.Read和FileShare.ReadWrite打开文件,因为我不关心读取时文件是否可以写入。

但有些情况下,当我读取的某个文件被另一个应用程序打开时,System.IO.FileStream.Read()方法会抛出System.IO.IOException异常,显示“由于另一个进程锁定了文件的某部分,进程无法访问该文件”。这是错误33,或者我认为是HResult 0x80070021 [编辑: 我相信当另一个进程调用LockFileEx来锁定文件中的字节范围时,也可能返回此错误。]。

我想在遇到这种错误时暂停并重试。我认为这是需要采取的适当措施。如果锁定进程快速释放字节范围锁,则我可以继续阅读文件。

如何区分出现这种原因的IOException和其他IOException?我可以考虑以下几种方法:

  • 私有反射-不想这样做。性能会很差。
  • 调用Exception.ToString()并解析字符串。感觉很麻烦。在i18n版本中无法使用。

我不喜欢这些选项。有没有更好、更清晰的方法呢?


我刚刚搜索并找到了System.Runtime.InteropServices.Marshal.GetHRForException。它会返回像0x80070021这样的无符号整数吗?


4
私有反射 - 不想这么做。性能会很差。- 即使异常性能不佳,我也不担心性能方面的问题。但是,反射需要完全信任,难看,不受支持且容易出错 - 这就是为什么你不应该这样做。 - Mark Brackett
6个回答

59

非常感谢!这确实在C#/COM互操作方面简化了开发。 - rds
2
+1 呃,需要完全信任...但它仍然是一个解决方案。 - reSPAWNed
3
注意副作用:请注意,GetHRForException方法会设置当前线程的IErrorInfo。这可能会对一些方法产生意想不到的结果,例如ThrowExceptionForHR方法,如果它使用已设置的当前线程的IErrorInfo,则默认情况下可能会导致问题。 - HugoRune
1
我强烈建议您不要使用GetHRForException。它会导致一个非常奇怪的问题,我们花了几个月的时间才理解:http://stackoverflow.com/a/40242031/5844190 - Alsty

11

值得一提的是,System.Exception.HResult在.NET 4.5中不再受保护,只有setter是受保护的。这对于可能使用多个框架版本编译的代码并没有什么帮助。


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

0

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


不对,CanRead为true。我相信80070021是一个暂时性错误。如果我理解文档正确的话,处理它的推荐做法是“等一会儿然后重试”。 - Cheeso
如果您以只读方式打开文件,同时其他人也使用FileShare.Read打开了该文件,那么第一个调用者就无法再读取它了,这种情况是否可能发生?这就是您所说的瞬态吗? - shahkalpesh
不,我的意思是,另一个进程已经调用了FileLock或FileLockEx(http://msdn.microsoft.com/en-us/library/aa365203.aspx)来锁定文件中的某个范围。这有时被称为字节范围锁定。在某个时间点上,锁定进程将释放范围锁定。这就是我所说的“短暂”的含义。 - Cheeso
谢谢Cheeso。作为一个VB背景的人,我以为文件可以被一个读者完全锁定。从来没有想过一段字符可以被锁定以供阅读。 - shahkalpesh

0
你有对这两种情况进行过分析吗?我想反射方法并不会很慢,特别是相对于应用程序将要执行的所有其他工作以及这个异常可能发生的频率而言。
如果最终证明它是一个瓶颈,你可以考虑缓存一些反射操作或生成动态IL来检索属性。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接