【问题标题】:How do I determine the HResult for a System.IO.IOException?如何确定 System.IO.IOException 的 HResult?
【发布时间】:2010-11-02 18:07:27
【问题描述】:

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


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

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

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

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

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

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


我刚刚四处寻找,发现System.Runtime.InteropServices.Marshal.GetHRForException。这会返回一个像 0x80070021 这样的 uint 吗?

【问题讨论】:

  • > 私人反思 - 不想那样做。性能会发臭。 - 无论如何,异常性能很臭,所以我不会担心性能方面。但是,反射确实需要 FullTrust,它很丑陋,并且不受支持并且容易损坏 - 这就是为什么你不应该这样做。

标签: c# .net exception hresult


【解决方案1】:

对于 .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);

【讨论】:

  • 非常感谢!在 C#/COM 互操作性的情况下,这确实简化了开发。
  • +1 Eww,需要完全信任......但它仍然是一个解决方案。
  • 注意副作用:“请注意,GetHRForException 方法设置当前线程的 IErrorInfo。这可能会导致意外结果,例如ThrowExceptionForHR 方法默认使用当前线程的 IErrorInfo(如果已设置)。"
  • 我强烈建议您不要使用 GetHRForException。它导致了一个非常奇怪的问题,我们花了几个月的时间才理解:stackoverflow.com/a/40242031/5844190
【解决方案2】:

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

【讨论】:

    【解决方案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");
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 不,CanRead 是真的。我相信 80070021 是一个暂时性错误。如果我正确阅读了文档,建议的做法是“等待一段时间,然后重试”。
      • 是否有可能您打开文件进行阅读并且其他人也打开了它(使用 FileShare.Read),第一个调用者无法再阅读它?这就是你所说的瞬态吗?
      • 不,我的意思是,另一个进程在文件上调用了 FileLock 或 FileLockEx (msdn.microsoft.com/en-us/library/aa365203.aspx) 以锁定文件内的范围。这有时称为字节范围锁。在某些时候,锁定过程将释放范围锁定。这就是我所说的“瞬态”。
      • 谢谢芝士。来自 VB 背景,我认为该文件可以被 1 个阅读器完全锁定。从没想过可以锁定一系列字符以供阅读。
      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        亡灵术。
        或者您可以通过反射获取受保护的属性:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-15
          • 1970-01-01
          • 2010-11-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多