37

System.Exception.HResult 属性受保护。如何在不诉诸反射或其他丑陋黑客的情况下查看异常并获取 HResult?


情况如下:
我想编写一个备份工具,它可以打开和读取系统上的文件。根据本指南,我使用 FileAccess.Read 和 FileShare.ReadWrite 打开文件,因为我不在乎文件在读取时是否打开以进行写入。

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

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

由于这个原因,我如何将 IOException 与其他人区分开来?我可以想到这些方法:

  • 私人反思 - 不想那样做。性能会发臭。
  • 调用 Exception.ToString() 并解析字符串。感觉很hacky。不适用于 i18n 版本。

我不喜欢这些选项。难道没有更好、更清洁的方法吗?


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

4

6 回答 6

58

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

int hr = ex.HResult;

对于旧版本,您可以使用Marshal.GetHRForException来取回 HResult,但这有很大的副作用,不推荐

int hr = Marshal.GetHRForException(ex);
于 2009-06-13T22:25:41.927 回答
11

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

于 2013-05-03T18:15:15.650 回答
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");
    }
}
于 2015-11-19T19:02:19.827 回答
0

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

于 2009-06-13T21:44:29.107 回答
0

您是否分析过这些案例中的任何一个?我想反射方法并没有那么慢,尤其是相对于您的应用程序将执行的所有其他工作以及此异常可能发生的频率。

如果事实证明这是一个瓶颈,您可以考虑缓存一些反射操作或生成动态 IL 来检索属性。

于 2009-06-13T21:49:57.830 回答
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;
}
于 2019-10-25T14:05:44.807 回答