我该如何检查IOException
是否为“磁盘空间不足”异常类型?
目前,我会检查消息是否与“磁盘空间不足”之类的内容匹配,但我知道如果操作系统语言不是英语,这种方法将无法正常工作。
我该如何检查IOException
是否为“磁盘空间不足”异常类型?
目前,我会检查消息是否与“磁盘空间不足”之类的内容匹配,但我知道如果操作系统语言不是英语,这种方法将无法正常工作。
您需要检查HResult
并针对ERROR_DISK_FULL (0x70)和ERROR_HANDLE_DISK_FULL (0x27)进行测试,这些值可以通过使用OR
运算符与0x80070000
相结合转换为HResults
。
对于 .Net Framework 4.5 及以上版本,您可以使用Exception.HResult
属性:
static bool IsDiskFull(Exception ex)
{
const int HR_ERROR_HANDLE_DISK_FULL = unchecked((int)0x80070027);
const int HR_ERROR_DISK_FULL = unchecked((int)0x80070070);
return ex.HResult == HR_ERROR_HANDLE_DISK_FULL
|| ex.HResult == HR_ERROR_DISK_FULL;
}
对于旧版本,您可以使用Marshal.GetHRForException
来获取HResult,但这具有重大的副作用,不建议使用:
static bool IsDiskFull(Exception ex)
{
const int ERROR_HANDLE_DISK_FULL = 0x27;
const int ERROR_DISK_FULL = 0x70;
int win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
return win32ErrorCode == ERROR_HANDLE_DISK_FULL || win32ErrorCode == ERROR_DISK_FULL;
}
来自 MSDN 文档:
请注意,GetHRForException 方法会设置当前线程的 IErrorInfo。这可能会对像 ThrowExceptionForHR 这样默认使用当前线程的 IErrorInfo 的方法产生意外结果。
HRESULT
-具体文档页面,位于 https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/705fb797-2175-4a90-b5a3-3918024b10b8。其中包含所有 Win32 代码,转换为 HRESULT
(即在高字中使用 7
facility),以及其他 facility 的结果代码。在同一文档部分中,甚至有一页解释了 HRESULT
值的位布局。 - Peter DunihoHResult
属性getter现在是公共的,因此您不再需要使用Marshal.GetHRForException
(以及它的副作用)。http://msdn.microsoft.com/en-us/library/system.exception.hresult(v=vs.110).aspx说明:"从.NET Framework 4.5开始,HResult属性的setter是受保护的,而其getter是公共的。在之前版本的.NET Framework中,getter和setter都是受保护的"
因此,您可以使用Justin的答案,但将Marshal.GetHRForException(ex)
替换为ex.HResult
。
最简单的行内解决方案(需要.NET 4.5和C# 6):
try
{
//...
}
catch (IOException ex) when ((ex.HResult & 0xFFFF) == 0x27 || (ex.HResult & 0xFFFF) == 0x70)
{
//...
}
好的,这个方法有点巧妙,但我们还是试一下。
首先要做的是从异常中获取HResult
。由于它是受保护的成员,所以我们需要使用反射来获取该值。这里有一个扩展方法可以完成这个任务:
public static class ExceptionExtensions
{
public static int HResultPublic(this Exception exception)
{
var hResult = exception.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(z => z.Name.Equals("HResult")).First();
return (int)hResult.GetValue(exception, null);
}
}
现在,在你的 catch 块中,可以获取 HResult
:
catch (Exception ex)
{
int hResult = ex.HResultPublic();
}
从这里开始,你需要解释HResult。你需要查看这个链接。
我们需要获取存储在值的前16位中的ErrorCode
,因此需要进行一些位操作:
int errorCode = (int)(hResult & 0x0000FFFF);
现在,请参考系统错误代码列表,我们找到了:
ERROR_DISK_FULL
112 (0x70)
因此,请使用以下方式进行测试:
switch (errorCode)
{
case 112:
// Disk full
}
也许有一些“更高级”的函数可以获取所有这些东西,但至少它能够工作。
HResult
已经被提升为public
。剩下的唯一问题可能是在静态成员中缓存GetProperties
调用-这将成为一个很好的包装器,以拥有“向后兼容”的包装器(前提是您同时使用Public
和NonPublic
绑定标志-您确实这样做了:-))。尽管,在异常处理面前,缓存成员的性能收益可能无关紧要。 - Christian.KException.HResult
强制转换为基于 ushort 的 HResultErrorCodes
枚举类型,这样可以单独应用 0xFFFF 掩码并消除调用代码中的魔法数字或常量。 - jnm2System.IOException有许多派生的异常类型,但是这些派生类型中没有一个听起来像您的异常。您可以查看异常的HResult或Data属性,也许这将详细说明更具体的错误代码。根据MSDN,这两个属性都是该异常类型的一部分。只需确保您正在尝试捕获特定的异常类型,而不仅仅是基本的Exception类型。