How to check if IOException is Not-Enough-Disk-Space-Exception type?
How can I check if IOException
is a "Not enough disk space" exception type?
At the moment I check to see if the message matches something like "Not enough disk space", but I know that this won't work if the OS language is not English.
Solution 1:
You need to check the HResult
and test against ERROR_DISK_FULL (0x70) and ERROR_HANDLE_DISK_FULL (0x27), which can be converted to HResults
by OR
'ing with 0x80070000
.
For .Net Framework 4.5 and above, you can use the Exception.HResult
property:
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;
}
For older versions, you can use Marshal.GetHRForException
to get back the HResult, but this has significant side-effects and is not recommended:
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;
}
From the MSDN documentation:
Note that the GetHRForException method sets the IErrorInfo of the current thread. This can cause unexpected results for methods like the ThrowExceptionForHR methods that default to using the IErrorInfo of the current thread if it is set.
See also How do I determine the HResult for a System.IO.IOException?
Solution 2:
In .NET 4.5, the HResult
property getter is now Public, so you do not have to use Marshal.GetHRForException
(along with its side affects) anymore.
http://msdn.microsoft.com/en-us/library/system.exception.hresult(v=vs.110).aspx states "Starting with the .NET Framework 4.5, the HResult property's setter is protected, whereas its getter is public. In previous versions of the .NET Framework, both getter and setter are protected"
So you can use Justin's answer, but replace Marshal.GetHRForException(ex)
with ex.HResult
.
Solution 3:
Well, it's a bit hacky, but here we go.
First thing to do is to get the HResult
from the exception. As it's a protected member, we need a bit of reflection to get the value. Here's an extension method to will do the trick:
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);
}
}
Now, in your catch scope, you can get the HResult
:
catch (Exception ex)
{
int hResult = ex.HResultPublic();
}
From here, you'll have to interpret the HResult. You'll need this link.
We need to get the ErrorCode
which is stored in the 16 first bits of the value, so here's some bit operation:
int errorCode = (int)(hResult & 0x0000FFFF);
Now, refer to the list of system error codes and here we are:
ERROR_DISK_FULL
112 (0x70)
So test it using:
switch (errorCode)
{
case 112:
// Disk full
}
Maybe there are some "higher level" functions to get all this stuff, but at least it works.
Solution 4:
Most simple inline solution (min .NET 4.5 and C# 6):
try
{
//...
}
catch (IOException ex) when ((ex.HResult & 0xFFFF) == 0x27 || (ex.HResult & 0xFFFF) == 0x70)
{
//...
}