-
Notifications
You must be signed in to change notification settings - Fork 117
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Is your feature request related to a problem? Please describe.
.NET framework contains built-in mapping of HRESULT to Exception in Marshal.ThrowExceptionForHR.
Similar functionality is missing in CsWin32 for WIN32_ERROR, RPC_STATUS, and NTSTATUS, prompting every implementer to write their own mapping, making the behavior highly inconsistent.
Describe the solution you'd like
It would be nice to get built-in error to exception mapping. My C# projects typically contain copy-pasted code like this:
internal static class WindowsErrorMapper
{
extension(WIN32_ERROR error)
{
[SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Code for the OutOfMemoryException is returned by the system.")]
public void ThrowOnFailure()
{
switch (error)
{
case WIN32_ERROR.ERROR_SUCCESS:
case WIN32_ERROR.ERROR_MORE_DATA:
// No error occurred, so exit gracefully.
return;
}
var genericException = new Win32Exception(unchecked((int)error));
Exception exceptionToThrow;
// We will try to translate the generic Win32 exception to a more specific built-in exception.
switch (error)
{
case WIN32_ERROR.ERROR_APP_INIT_FAILURE:
exceptionToThrow = new ApplicationException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_DS_INVALID_DN_SYNTAX:
case WIN32_ERROR.ERROR_INVALID_PARAMETER:
case WIN32_ERROR.ERROR_INVALID_NAME:
case WIN32_ERROR.ERROR_BAD_ARGUMENTS:
case WIN32_ERROR.ERROR_INVALID_FLAG_NUMBER:
case WIN32_ERROR.ERROR_INVALID_ADDRESS:
exceptionToThrow = new ArgumentException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_ARITHMETIC_OVERFLOW:
exceptionToThrow = new ArithmeticException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_BAD_EXE_FORMAT:
exceptionToThrow = new BadImageFormatException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_BAD_FORMAT:
case WIN32_ERROR.ERROR_SXS_MANIFEST_PARSE_ERROR:
case WIN32_ERROR.ERROR_INVALID_DATA:
case WIN32_ERROR.ERROR_DATATYPE_MISMATCH:
exceptionToThrow = new FormatException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_MOD_NOT_FOUND:
exceptionToThrow = new DllNotFoundException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_HANDLE_EOF:
exceptionToThrow = new EndOfStreamException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_INVALID_ORDINAL:
exceptionToThrow = new EntryPointNotFoundException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_OPERATION_ABORTED:
case WIN32_ERROR.ERROR_CANCELLED:
exceptionToThrow = new OperationCanceledException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_FILE_NOT_FOUND:
exceptionToThrow = new FileNotFoundException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_PATH_NOT_FOUND:
case WIN32_ERROR.ERROR_INVALID_DRIVE:
case WIN32_ERROR.ERROR_DIRECTORY:
exceptionToThrow = new DirectoryNotFoundException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_FILENAME_EXCED_RANGE:
exceptionToThrow = new PathTooLongException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_ACCESS_DENIED:
case WIN32_ERROR.ERROR_DS_DRA_ACCESS_DENIED:
case WIN32_ERROR.ERROR_WRONG_PASSWORD:
case WIN32_ERROR.ERROR_PASSWORD_EXPIRED:
case WIN32_ERROR.ERROR_NOACCESS:
case WIN32_ERROR.ERROR_ELEVATION_REQUIRED:
case WIN32_ERROR.ERROR_PRIVILEGE_NOT_HELD:
case WIN32_ERROR.ERROR_ACCESS_DISABLED_BY_POLICY:
exceptionToThrow = new UnauthorizedAccessException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_ALREADY_EXISTS:
case WIN32_ERROR.ERROR_FILE_EXISTS:
case WIN32_ERROR.ERROR_SHARING_VIOLATION:
case WIN32_ERROR.ERROR_LOCK_VIOLATION:
case WIN32_ERROR.ERROR_FILE_CORRUPT:
exceptionToThrow = new IOException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_NOT_ENOUGH_MEMORY:
case WIN32_ERROR.ERROR_OUTOFMEMORY:
case WIN32_ERROR.ERROR_DS_DRA_OUT_OF_MEM:
exceptionToThrow = new OutOfMemoryException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_FILE_INVALID:
exceptionToThrow = new FileLoadException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_DLL_INIT_FAILED:
exceptionToThrow = new TypeInitializationException(null, genericException);
break;
case WIN32_ERROR.ERROR_INVALID_INDEX:
exceptionToThrow = new ArgumentOutOfRangeException(null, genericException.Message);
break;
case WIN32_ERROR.ERROR_INVALID_DATATYPE:
exceptionToThrow = new InvalidCastException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_INVALID_FILTER_PROC:
exceptionToThrow = new InvalidFilterCriteriaException(genericException.Message);
break;
case WIN32_ERROR.ERROR_INVALID_VARIANT:
exceptionToThrow = new InvalidOleVariantTypeException(genericException.Message);
break;
case WIN32_ERROR.ERROR_INVALID_OPERATION:
exceptionToThrow = new InvalidOperationException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_INVALID_FUNCTION:
exceptionToThrow = new InvalidProgramException(genericException.Message);
break;
case WIN32_ERROR.ERROR_PROC_NOT_FOUND:
exceptionToThrow = new MissingMethodException(genericException.Message);
break;
case WIN32_ERROR.ERROR_RESOURCE_NAME_NOT_FOUND:
case WIN32_ERROR.ERROR_RESOURCE_DATA_NOT_FOUND:
exceptionToThrow = new MissingManifestResourceException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_BUFFER_OVERFLOW:
case WIN32_ERROR.ERROR_MARSHALL_OVERFLOW:
exceptionToThrow = new OverflowException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_OLD_WIN_VERSION:
exceptionToThrow = new PlatformNotSupportedException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_STACK_OVERFLOW:
exceptionToThrow = new StackOverflowException(genericException.Message);
break;
case WIN32_ERROR.ERROR_NOT_LOCKED:
exceptionToThrow = new SynchronizationLockException(genericException.Message);
break;
case WIN32_ERROR.ERROR_THREAD_1_INACTIVE:
exceptionToThrow = new ThreadStateException(genericException.Message);
break;
case WIN32_ERROR.ERROR_INVALID_DLL:
case WIN32_ERROR.ERROR_INVALID_MODULETYPE:
exceptionToThrow = new TypeLoadException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_NOT_SUPPORTED:
exceptionToThrow = new NotSupportedException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_CALL_NOT_IMPLEMENTED:
exceptionToThrow = new NotImplementedException(genericException.Message, genericException);
break;
case WIN32_ERROR.ERROR_INVALID_HANDLE:
case WIN32_ERROR.ERROR_INVALID_TARGET_HANDLE:
exceptionToThrow = new ObjectDisposedException(null, genericException.Message);
break;
case WIN32_ERROR.ERROR_NO_LOGON_SERVERS:
exceptionToThrow = new SocketException((int)error);
break;
case WIN32_ERROR.ERROR_NO_SUCH_DOMAIN:
case WIN32_ERROR.ERROR_DS_OBJ_NOT_FOUND:
case WIN32_ERROR.ERROR_DS_NAME_ERROR_NO_MAPPING:
case WIN32_ERROR.ERROR_SOME_NOT_MAPPED:
// This error code means either a non-existing DN or Access Denied.
case WIN32_ERROR.ERROR_DS_DRA_BAD_DN:
exceptionToThrow = new DirectoryObjectNotFoundException(null, genericException);
break;
case WIN32_ERROR.ERROR_GEN_FAILURE:
default:
// We were not able to translate the Win32Exception to a more specific type.
exceptionToThrow = genericException;
break;
}
throw exceptionToThrow;
}
public HRESULT ToHResult()
{
// HRESULT_FROM_WIN32(unsigned long x) { return (HRESULT)(x) <= 0 ? (HRESULT)(x) : (HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000);}
return PInvoke.HRESULT_FROM_WIN32(error);
}
public static WIN32_ERROR GetLastError()
{
int lastErrorCode = Marshal.GetLastWin32Error();
return (WIN32_ERROR)lastErrorCode;
}
public static void ThrowLastError()
{
WIN32_ERROR lastError = WIN32_ERROR.GetLastError();
lastError.ThrowOnFailure();
}
}
extension(RPC_STATUS status)
{
[SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Code for the OutOfMemoryException is returned by the system.")]
public void ThrowOnFailure()
{
if (status == RPC_STATUS.RPC_S_OK)
{
// No error occurred, so exit gracefully.
return;
}
var genericException = new Win32Exception((int)status);
Exception exceptionToThrow;
switch (status)
{
case RPC_STATUS.RPC_S_OBJECT_NOT_FOUND:
exceptionToThrow = new DirectoryObjectNotFoundException(objectIdentifier:null, genericException);
break;
case RPC_STATUS.RPC_S_OUT_OF_MEMORY:
exceptionToThrow = new OutOfMemoryException(genericException.Message, genericException);
break;
case RPC_STATUS.RPC_S_ZERO_DIVIDE:
case RPC_STATUS.RPC_S_FP_DIV_ZERO:
exceptionToThrow = new DivideByZeroException(genericException.Message, genericException);
break;
case RPC_STATUS.RPC_S_FP_OVERFLOW:
case RPC_STATUS.RPC_S_FP_UNDERFLOW:
exceptionToThrow = new NotFiniteNumberException(genericException.Message);
break;
case RPC_STATUS.RPC_S_ACCESS_DENIED:
exceptionToThrow = new UnauthorizedAccessException(genericException.Message, genericException);
break;
case RPC_STATUS.RPC_S_CALL_CANCELLED:
exceptionToThrow = new OperationCanceledException(genericException.Message, genericException);
break;
case RPC_STATUS.RPC_S_INVALID_BINDING:
case RPC_STATUS.RPC_S_INVALID_STRING_BINDING:
case RPC_STATUS.RPC_S_INVALID_NET_ADDR:
exceptionToThrow = new ArgumentException(genericException.Message, genericException);
break;
case RPC_STATUS.RPC_S_INVALID_TIMEOUT:
exceptionToThrow = new ArgumentOutOfRangeException(null, genericException.Message);
break;
case RPC_STATUS.RPC_S_WRONG_KIND_OF_BINDING:
case RPC_STATUS.RPC_S_INVALID_ASYNC_HANDLE:
case RPC_STATUS.RPC_S_CALL_IN_PROGRESS:
case RPC_STATUS.RPC_S_ADDRESS_ERROR:
exceptionToThrow = new InvalidOperationException(genericException.Message, genericException);
break;
case RPC_STATUS.RPC_S_UNSUPPORTED_TRANS_SYN:
case RPC_STATUS.RPC_S_UNSUPPORTED_TYPE:
case RPC_STATUS.RPC_S_UNKNOWN_AUTHN_TYPE:
case RPC_STATUS.RPC_S_UNKNOWN_AUTHN_SERVICE:
case RPC_STATUS.RPC_S_UNKNOWN_AUTHZ_SERVICE:
exceptionToThrow = new NotSupportedException(genericException.Message, genericException);
break;
case RPC_STATUS.RPC_S_SERVER_UNAVAILABLE:
case RPC_STATUS.RPC_S_CALL_FAILED:
case RPC_STATUS.RPC_S_CALL_FAILED_DNE:
case RPC_STATUS.RPC_S_SERVER_TOO_BUSY:
exceptionToThrow = new SocketException((int)status);
break;
default:
// We were not able to translate the exception to a more specific type.
exceptionToThrow = genericException;
break;
}
throw exceptionToThrow;
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request