using System; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Security; namespace Vanara.PInvoke { /// /// Formal replacement for the Windows HRESULT definition. In windows.h, it is a defined UINT value. For .NET, this class strongly types /// the value. /// The 32-bit value is organized as follows: /// /// /// Bit /// 31 /// 30 /// 29 /// 28 /// 27 /// 26 - 16 /// 15 - 0 /// /// /// Field /// Severity /// Severity /// Customer /// NT status /// MsgID /// Facility /// Code /// /// /// /// /// /// [StructLayout(LayoutKind.Sequential)] [TypeConverter(typeof(HRESULTTypeConverter))] [PInvokeData("winerr.h")] public partial struct HRESULT : IComparable, IComparable, IEquatable, IEquatable, IEquatable, IConvertible, IErrorProvider { internal readonly int _value; private const int codeMask = 0xFFFF; private const uint facilityMask = 0x7FF0000; private const int facilityShift = 16; private const uint severityMask = 0x80000000; private const int severityShift = 31; /// Initializes a new instance of the structure. /// The raw HRESULT value. public HRESULT(int rawValue) => _value = rawValue; /// Initializes a new instance of the structure. /// The raw HRESULT value. public HRESULT(uint rawValue) => _value = unchecked((int)rawValue); /// Enumeration of facility codes [PInvokeData("winerr.h")] public enum FacilityCode { /// The default facility code. FACILITY_NULL = 0, /// The source of the error code is an RPC subsystem. FACILITY_RPC = 1, /// The source of the error code is a COM Dispatch. FACILITY_DISPATCH = 2, /// The source of the error code is OLE Storage. FACILITY_STORAGE = 3, /// The source of the error code is COM/OLE Interface management. FACILITY_ITF = 4, /// This region is reserved to map undecorated error codes into HRESULTs. FACILITY_WIN32 = 7, /// The source of the error code is the Windows subsystem. FACILITY_WINDOWS = 8, /// The source of the error code is the Security API layer. FACILITY_SECURITY = 9, /// The source of the error code is the Security API layer. FACILITY_SSPI = 9, /// The source of the error code is the control mechanism. FACILITY_CONTROL = 10, /// The source of the error code is a certificate client or server? FACILITY_CERT = 11, /// The source of the error code is Wininet related. FACILITY_INTERNET = 12, /// The source of the error code is the Windows Media Server. FACILITY_MEDIASERVER = 13, /// The source of the error code is the Microsoft Message Queue. FACILITY_MSMQ = 14, /// The source of the error code is the Setup API. FACILITY_SETUPAPI = 15, /// The source of the error code is the Smart-card subsystem. FACILITY_SCARD = 16, /// The source of the error code is COM+. FACILITY_COMPLUS = 17, /// The source of the error code is the Microsoft agent. FACILITY_AAF = 18, /// The source of the error code is .NET CLR. FACILITY_URT = 19, /// The source of the error code is the audit collection service. FACILITY_ACS = 20, /// The source of the error code is Direct Play. FACILITY_DPLAY = 21, /// The source of the error code is the ubiquitous memoryintrospection service. FACILITY_UMI = 22, /// The source of the error code is Side-by-side servicing. FACILITY_SXS = 23, /// The error code is specific to Windows CE. FACILITY_WINDOWS_CE = 24, /// The source of the error code is HTTP support. FACILITY_HTTP = 25, /// The source of the error code is common Logging support. FACILITY_USERMODE_COMMONLOG = 26, /// The source of the error code is the user mode filter manager. FACILITY_USERMODE_FILTER_MANAGER = 31, /// The source of the error code is background copy control FACILITY_BACKGROUNDCOPY = 32, /// The source of the error code is configuration services. FACILITY_CONFIGURATION = 33, /// The source of the error code is state management services. FACILITY_STATE_MANAGEMENT = 34, /// The source of the error code is the Microsoft Identity Server. FACILITY_METADIRECTORY = 35, /// The source of the error code is a Windows update. FACILITY_WINDOWSUPDATE = 36, /// The source of the error code is Active Directory. FACILITY_DIRECTORYSERVICE = 37, /// The source of the error code is the graphics drivers. FACILITY_GRAPHICS = 38, /// The source of the error code is the user Shell. FACILITY_SHELL = 39, /// The source of the error code is the Trusted Platform Module services. FACILITY_TPM_SERVICES = 40, /// The source of the error code is the Trusted Platform Module applications. FACILITY_TPM_SOFTWARE = 41, /// The source of the error code is Performance Logs and Alerts FACILITY_PLA = 48, /// The source of the error code is Full volume encryption. FACILITY_FVE = 49, /// The source of the error code is the Firewall Platform. FACILITY_FWP = 50, /// The source of the error code is the Windows Resource Manager. FACILITY_WINRM = 51, /// The source of the error code is the Network Driver Interface. FACILITY_NDIS = 52, /// The source of the error code is the Usermode Hypervisor components. FACILITY_USERMODE_HYPERVISOR = 53, /// The source of the error code is the Configuration Management Infrastructure. FACILITY_CMI = 54, /// The source of the error code is the user mode virtualization subsystem. FACILITY_USERMODE_VIRTUALIZATION = 55, /// The source of the error code is the user mode volume manager FACILITY_USERMODE_VOLMGR = 56, /// The source of the error code is the Boot Configuration Database. FACILITY_BCD = 57, /// The source of the error code is user mode virtual hard disk support. FACILITY_USERMODE_VHD = 58, /// The source of the error code is System Diagnostics. FACILITY_SDIAG = 60, /// The source of the error code is the Web Services. FACILITY_WEBSERVICES = 61, /// The source of the error code is a Windows Defender component. FACILITY_WINDOWS_DEFENDER = 80, /// The source of the error code is the open connectivity service. FACILITY_OPC = 81, /// FACILITY_XPS = 82, /// FACILITY_MBN = 84, /// FACILITY_POWERSHELL = 84, /// FACILITY_RAS = 83, /// FACILITY_P2P_INT = 98, /// FACILITY_P2P = 99, /// FACILITY_DAF = 100, /// FACILITY_BLUETOOTH_ATT = 101, /// FACILITY_AUDIO = 102, /// FACILITY_STATEREPOSITORY = 103, /// FACILITY_VISUALCPP = 109, /// FACILITY_SCRIPT = 112, /// FACILITY_PARSE = 113, /// FACILITY_BLB = 120, /// FACILITY_BLB_CLI = 121, /// FACILITY_WSBAPP = 122, /// FACILITY_BLBUI = 128, /// FACILITY_USN = 129, /// FACILITY_USERMODE_VOLSNAP = 130, /// FACILITY_TIERING = 131, /// FACILITY_WSB_ONLINE = 133, /// FACILITY_ONLINE_ID = 134, /// FACILITY_DEVICE_UPDATE_AGENT = 135, /// FACILITY_DRVSERVICING = 136, /// FACILITY_DLS = 153, /// FACILITY_DELIVERY_OPTIMIZATION = 208, /// FACILITY_USERMODE_SPACES = 231, /// FACILITY_USER_MODE_SECURITY_CORE = 232, /// FACILITY_USERMODE_LICENSING = 234, /// FACILITY_SOS = 160, /// FACILITY_DEBUGGERS = 176, /// FACILITY_SPP = 256, /// FACILITY_RESTORE = 256, /// FACILITY_DMSERVER = 256, /// FACILITY_DEPLOYMENT_SERVICES_SERVER = 257, /// FACILITY_DEPLOYMENT_SERVICES_IMAGING = 258, /// FACILITY_DEPLOYMENT_SERVICES_MANAGEMENT = 259, /// FACILITY_DEPLOYMENT_SERVICES_UTIL = 260, /// FACILITY_DEPLOYMENT_SERVICES_BINLSVC = 261, /// FACILITY_DEPLOYMENT_SERVICES_PXE = 263, /// FACILITY_DEPLOYMENT_SERVICES_TFTP = 264, /// FACILITY_DEPLOYMENT_SERVICES_TRANSPORT_MANAGEMENT = 272, /// FACILITY_DEPLOYMENT_SERVICES_DRIVER_PROVISIONING = 278, /// FACILITY_DEPLOYMENT_SERVICES_MULTICAST_SERVER = 289, /// FACILITY_DEPLOYMENT_SERVICES_MULTICAST_CLIENT = 290, /// FACILITY_DEPLOYMENT_SERVICES_CONTENT_PROVIDER = 293, /// FACILITY_LINGUISTIC_SERVICES = 305, /// FACILITY_AUDIOSTREAMING = 1094, /// FACILITY_ACCELERATOR = 1536, /// FACILITY_WMAAECMA = 1996, /// FACILITY_DIRECTMUSIC = 2168, /// FACILITY_DIRECT3D10 = 2169, /// FACILITY_DXGI = 2170, /// FACILITY_DXGI_DDI = 2171, /// FACILITY_DIRECT3D11 = 2172, /// FACILITY_DIRECT3D11_DEBUG = 2173, /// FACILITY_DIRECT3D12 = 2174, /// FACILITY_DIRECT3D12_DEBUG = 2175, /// FACILITY_LEAP = 2184, /// FACILITY_AUDCLNT = 2185, /// FACILITY_WINCODEC_DWRITE_DWM = 2200, /// FACILITY_WINML = 2192, /// FACILITY_DIRECT2D = 2201, /// FACILITY_DEFRAG = 2304, /// FACILITY_USERMODE_SDBUS = 2305, /// FACILITY_JSCRIPT = 2306, /// FACILITY_PIDGENX = 2561, /// FACILITY_EAS = 85, /// FACILITY_WEB = 885, /// FACILITY_WEB_SOCKET = 886, /// FACILITY_MOBILE = 1793, /// FACILITY_SQLITE = 1967, /// FACILITY_UTC = 1989, /// FACILITY_WEP = 2049, /// FACILITY_SYNCENGINE = 2050, /// FACILITY_XBOX = 2339, /// FACILITY_GAME = 2340, /// FACILITY_PIX = 2748 } /// A value indicating whether an is a success (Severity bit 31 equals 0). [PInvokeData("winerr.h")] public enum SeverityLevel { /// Success Success = 0, /// Failure Fail = 1 } /// Gets the code portion of the . /// The code value (bits 0-15). public int Code => GetCode(_value); /// Gets the facility portion of the . /// The facility value (bits 16-26). public FacilityCode Facility => GetFacility(_value); /// Gets a value indicating whether this is a failure (Severity bit 31 equals 1). /// true if failed; otherwise, false. public bool Failed => _value < 0; /// Gets the severity level of the . /// The severity level. public SeverityLevel Severity => GetSeverity(_value); /// Gets a value indicating whether this is a success (Severity bit 31 equals 0). /// true if succeeded; otherwise, false. public bool Succeeded => _value >= 0; /// Performs an explicit conversion from to . /// if set to returns S_OK; otherwise S_FALSE. /// The result of the conversion. public static explicit operator HRESULT(bool value) => value ? S_OK : S_FALSE; /// Performs an explicit conversion from to . /// The value. /// The result of the conversion. public static explicit operator int(HRESULT value) => value._value; /// Performs an explicit conversion from to . /// The value. /// The result of the conversion. public static explicit operator uint(HRESULT value) => unchecked((uint)value._value); /// Tries to extract a HRESULT from an exception. /// The exception. /// The error. If undecipherable, E_FAIL is returned. public static HRESULT FromException(Exception exception) { if (exception is Win32Exception we) return new Win32Error(unchecked((uint)we.NativeErrorCode)).ToHRESULT(); if (exception.InnerException is Win32Exception iwe) return new Win32Error(unchecked((uint)iwe.NativeErrorCode)).ToHRESULT(); #if !(NET20 || NET35 || NET40) if (exception.HResult != 0) return new HRESULT(exception.HResult); else if (exception.InnerException != null && exception.InnerException.HResult != 0) return new HRESULT(exception.InnerException.HResult); #endif return E_FAIL; } /// Gets the code value from a 32-bit value. /// The 32-bit raw HRESULT value. /// The code value (bits 0-15). public static int GetCode(int hresult) => hresult & codeMask; /// Gets the facility value from a 32-bit value. /// The 32-bit raw HRESULT value. /// The facility value (bits 16-26). public static FacilityCode GetFacility(int hresult) => (FacilityCode)((hresult & facilityMask) >> facilityShift); /// Gets the severity value from a 32-bit value. /// The 32-bit raw HRESULT value. /// The severity value (bit 31). public static SeverityLevel GetSeverity(int hresult) => (SeverityLevel)((hresult & severityMask) >> severityShift); /// Performs an implicit conversion from to . /// The value. /// The result of the conversion. public static implicit operator HRESULT(int value) => new HRESULT(value); /// Performs an implicit conversion from to . /// The value. /// The resulting instance from the conversion. public static implicit operator HRESULT(uint value) => new HRESULT(value); /// Maps an NT Status value to an HRESULT value. /// The NT Status value. /// The HRESULT value. public static HRESULT HRESULT_FROM_NT(NTStatus err) => err.ToHRESULT(); /// Maps a system error code to an HRESULT value. /// The system error code. /// The HRESULT value. public static HRESULT HRESULT_FROM_WIN32(Win32Error err) => err.ToHRESULT(); /// Creates a new from provided values. /// if set to false, sets the severity bit to 1. /// The facility. /// The code. /// The resulting . public static HRESULT Make(bool severe, FacilityCode facility, uint code) => Make(severe, (uint)facility, code); /// Creates a new from provided values. /// if set to false, sets the severity bit to 1. /// The facility. /// The code. /// The resulting . public static HRESULT Make(bool severe, uint facility, uint code) => new HRESULT(unchecked((int)((severe ? severityMask : 0) | (facility << facilityShift) | code))); /// Implements the operator !=. /// The first . /// The second . /// The result of the operator. public static bool operator !=(HRESULT hrLeft, HRESULT hrRight) => !(hrLeft == hrRight); /// Implements the operator !=. /// The first . /// The second . /// The result of the operator. public static bool operator !=(HRESULT hrLeft, int hrRight) => !(hrLeft == hrRight); /// Implements the operator !=. /// The first . /// The second . /// The result of the operator. public static bool operator !=(HRESULT hrLeft, uint hrRight) => !(hrLeft == hrRight); /// Implements the operator ==. /// The first . /// The second . /// The result of the operator. public static bool operator ==(HRESULT hrLeft, HRESULT hrRight) => hrLeft.Equals(hrRight); /// Implements the operator ==. /// The first . /// The second . /// The result of the operator. public static bool operator ==(HRESULT hrLeft, int hrRight) => hrLeft.Equals(hrRight); /// Implements the operator ==. /// The first . /// The second . /// The result of the operator. public static bool operator ==(HRESULT hrLeft, uint hrRight) => hrLeft.Equals(hrRight); /// /// If the supplied raw HRESULT value represents a failure, throw the associated with the optionally /// supplied message. /// /// The 32-bit raw HRESULT value. /// The optional message to assign to the . [System.Diagnostics.DebuggerStepThrough] public static void ThrowIfFailed(int hresult, string message = null) => new HRESULT(hresult).ThrowIfFailed(message); /// Compares the current object with another object of the same type. /// An object to compare with this object. /// /// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value /// Meaning Less than zero This object is less than the parameter.Zero This object is equal to . Greater than zero This object is greater than . /// public int CompareTo(HRESULT other) => _value.CompareTo(other._value); /// /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current /// instance precedes, follows, or occurs in the same position in the sort order as the other object. /// /// An object to compare with this instance. /// /// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less /// than zero This instance precedes in the sort order. Zero This instance occurs in the same position in the /// sort order as . Greater than zero This instance follows in the sort order. /// public int CompareTo(object obj) { var v = ValueFromObj(obj); return v.HasValue ? _value.CompareTo(v.Value) : throw new ArgumentException(@"Object cannot be converted to a UInt32 value for comparison.", nameof(obj)); } /// Indicates whether the current object is equal to an . /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public bool Equals(int other) => other == _value; /// Indicates whether the current object is equal to an . /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public bool Equals(uint other) => unchecked((int)other) == _value; /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object obj) => obj switch { null => false, HRESULT h => Equals(h), int i => Equals(i), uint u => Equals(u), _ => Equals(_value, ValueFromObj(obj)), }; /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public bool Equals(HRESULT other) => other._value == _value; /// Gets the .NET associated with the HRESULT value and optionally adds the supplied message. /// The optional message to assign to the . /// The associated or null if this HRESULT is not a failure. [SecurityCritical, SecuritySafeCritical] public Exception GetException(string message = null) { if (!Failed) return null; var exceptionForHR = Marshal.GetExceptionForHR(_value, new IntPtr(-1)); if (exceptionForHR.GetType() == typeof(COMException)) { if (Facility == FacilityCode.FACILITY_WIN32) return string.IsNullOrEmpty(message) ? new Win32Exception(Code) : new Win32Exception(Code, message); return new COMException(message ?? exceptionForHR.Message, _value); } if (!string.IsNullOrEmpty(message)) { Type[] types = { typeof(string) }; var constructor = exceptionForHR.GetType().GetConstructor(types); if (null != constructor) { object[] parameters = { message }; exceptionForHR = constructor.Invoke(parameters) as Exception; } } return exceptionForHR; } /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => _value; /// /// If this represents a failure, throw the associated with the optionally supplied message. /// /// The optional message to assign to the . [SecurityCritical, SecuritySafeCritical] [System.Diagnostics.DebuggerStepThrough] public void ThrowIfFailed(string message = null) { var exception = GetException(message); if (exception != null) throw exception; } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { string err = null; // Check for defined HRESULT value if (!StaticFieldValueHash.TryGetFieldName(_value, out err) && Facility == FacilityCode.FACILITY_WIN32) { foreach (var info2 in typeof(Win32Error).GetFields(BindingFlags.Public | BindingFlags.Static).Where(fi => fi.FieldType == typeof(uint))) { if ((HRESULT)(Win32Error)(uint)info2.GetValue(null) == this) { err = $"HRESULT_FROM_WIN32({info2.Name})"; break; } } } var msg = FormatMessage(unchecked((uint)_value)); return (err ?? string.Format(CultureInfo.InvariantCulture, "0x{0:X8}", _value)) + (msg == null ? "" : ": " + msg); } TypeCode IConvertible.GetTypeCode() => _value.GetTypeCode(); bool IConvertible.ToBoolean(IFormatProvider provider) => Succeeded; byte IConvertible.ToByte(IFormatProvider provider) => ((IConvertible)_value).ToByte(provider); char IConvertible.ToChar(IFormatProvider provider) => throw new NotSupportedException(); DateTime IConvertible.ToDateTime(IFormatProvider provider) => throw new NotSupportedException(); decimal IConvertible.ToDecimal(IFormatProvider provider) => ((IConvertible)_value).ToDecimal(provider); double IConvertible.ToDouble(IFormatProvider provider) => ((IConvertible)_value).ToDouble(provider); /// Converts this error to an . /// An equivalent . HRESULT IErrorProvider.ToHRESULT() => this; short IConvertible.ToInt16(IFormatProvider provider) => ((IConvertible)_value).ToInt16(provider); int IConvertible.ToInt32(IFormatProvider provider) => _value; long IConvertible.ToInt64(IFormatProvider provider) => ((IConvertible)_value).ToInt64(provider); sbyte IConvertible.ToSByte(IFormatProvider provider) => ((IConvertible)_value).ToSByte(provider); float IConvertible.ToSingle(IFormatProvider provider) => ((IConvertible)_value).ToSingle(provider); string IConvertible.ToString(IFormatProvider provider) => ToString(); object IConvertible.ToType(Type conversionType, IFormatProvider provider) => ((IConvertible)_value).ToType(conversionType, provider); ushort IConvertible.ToUInt16(IFormatProvider provider) => ((IConvertible)unchecked((uint)_value)).ToUInt16(provider); uint IConvertible.ToUInt32(IFormatProvider provider) => unchecked((uint)_value); ulong IConvertible.ToUInt64(IFormatProvider provider) => ((IConvertible)unchecked((uint)_value)).ToUInt64(provider); /// Formats the message. /// The error. /// The string. internal static string FormatMessage(uint id) { var flags = 0x1200U; // FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM var buf = new System.Text.StringBuilder(1024); do { if (0 != FormatMessage(flags, default, id, 0, buf, (uint)buf.Capacity, default)) return buf.ToString(); var lastError = Win32Error.GetLastError(); if (lastError == Win32Error.ERROR_MR_MID_NOT_FOUND || lastError == Win32Error.ERROR_MUI_FILE_NOT_FOUND) break; if (lastError != Win32Error.ERROR_INSUFFICIENT_BUFFER) lastError.ThrowIfFailed(); buf.Capacity *= 2; } while (true && buf.Capacity < 1024 * 16); // Don't go crazy return string.Empty; } [DllImport(Lib.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] private static extern int FormatMessage(uint dwFlags, HINSTANCE lpSource, uint dwMessageId, uint dwLanguageId, System.Text.StringBuilder lpBuffer, uint nSize, IntPtr Arguments); private static int? ValueFromObj(object obj) { switch (obj) { case null: return null; case int i: return i; case uint u: return unchecked((int)u); default: var c = TypeDescriptor.GetConverter(obj); return c.CanConvertTo(typeof(int)) ? (int?)c.ConvertTo(obj, typeof(int)) : null; } } } internal class HRESULTTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(Win32Error) || sourceType.IsPrimitive && sourceType != typeof(char)) return true; return base.CanConvertFrom(context, sourceType); } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string) || destinationType.IsPrimitive && destinationType != typeof(char)) return true; return base.CanConvertTo(context, destinationType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is Win32Error e) return e.ToHRESULT(); if (value != null && value.GetType().IsPrimitive) { if (value is bool b) return b ? HRESULT.S_OK : HRESULT.S_FALSE; if (!(value is char)) return new HRESULT((int)Convert.ChangeType(value, TypeCode.Int32)); } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (!(value is HRESULT hr)) throw new NotSupportedException(); if (destinationType.IsPrimitive && destinationType != typeof(char)) return Convert.ChangeType(hr, destinationType); if (destinationType == typeof(string)) return hr.ToString(); return base.ConvertTo(context, culture, value, destinationType); } } }