using System; using System.ComponentModel; using System.Globalization; using System.Runtime.InteropServices; using System.Security; namespace Vanara.PInvoke; /// Represents a Win32 Error Code. This can be used in place of a return value. [StructLayout(LayoutKind.Sequential, Pack = 4)] [TypeConverter(typeof(Win32ErrorTypeConverter))] [PInvokeData("winerr.h")] public partial struct Win32Error : IEquatable, IEquatable, IConvertible, IComparable, IComparable, IErrorProvider { internal readonly uint value; /// Initializes a new instance of the struct with an error value. /// The error value. public Win32Error(uint i) => value = i; /// Gets a value indicating whether this is a failure. /// if failed; otherwise, . public bool Failed => !Succeeded; /// Gets a value indicating whether this is a success. /// if succeeded; otherwise, . public bool Succeeded => value == ERROR_SUCCESS; /// Performs an explicit conversion from to . /// The error. /// The result of the conversion. public static explicit operator HRESULT(Win32Error error) => unchecked((int)error.value) <= 0 ? unchecked((int)error.value) : HRESULT.Make(true, HRESULT.FacilityCode.FACILITY_WIN32, error.value & 0xffff); /// Performs an explicit conversion from to . /// The value. /// The result of the conversion. public static explicit operator uint(Win32Error value) => value.value; /// Tries to extract a Win32Error from an exception. /// The exception. /// The error. If undecipherable, ERROR_UNIDENTIFIED_ERROR is returned. public static Win32Error FromException(Exception exception) { if (exception is Win32Exception we) return unchecked((uint)we.NativeErrorCode); if (exception.InnerException is Win32Exception iwe) return unchecked((uint)iwe.NativeErrorCode); var hr = new HRESULT(exception.HResult); return hr.Facility == HRESULT.FacilityCode.FACILITY_WIN32 ? (Win32Error)(uint)hr.Code : (Win32Error)ERROR_UNIDENTIFIED_ERROR; } /// Gets the last error. /// The last error. [SecurityCritical] [System.Diagnostics.DebuggerStepThrough] public static Win32Error GetLastError() => new(ExtGetLastError()); /// Performs an explicit conversion from to . /// The value. /// The result of the conversion. public static implicit operator Win32Error(uint value) => new(value); /// Implements the operator !=. /// The error left. /// The error right. /// The result of the operator. public static bool operator !=(Win32Error errLeft, Win32Error errRight) => errLeft.value != errRight.value; /// Implements the operator !=. /// The error left. /// The error right. /// The result of the operator. public static bool operator !=(Win32Error errLeft, uint errRight) => errLeft.value != errRight; /// Implements the operator ==. /// The error left. /// The error right. /// The result of the operator. public static bool operator ==(Win32Error errLeft, Win32Error errRight) => errLeft.value == errRight.value; /// Implements the operator ==. /// The error left. /// The error right. /// The result of the operator. public static bool operator ==(Win32Error errLeft, uint errRight) => errLeft.value == errRight; /// Throws if failed. /// The error. /// The message. [System.Diagnostics.DebuggerStepThrough] public static void ThrowIfFailed(Win32Error err, string message = null) => err.ThrowIfFailed(message); /// Throws the last error. /// The message to associate with the exception. [System.Diagnostics.DebuggerStepThrough] public static void ThrowLastError(string message = null) => GetLastError().ThrowIfFailed(message); /// Throws the last error if the predicate delegate returns . /// The type of the value to evaluate. /// The value to check. /// The delegate which returns on failure. /// The message. /// The passed in on success. public static T ThrowLastErrorIf(T value, Func valueIsFailure, string message = null) { if (valueIsFailure(value)) GetLastError().ThrowIfFailed(message); return value; } /// Throws the last error if the function returns . /// The value to check. /// The message. public static bool ThrowLastErrorIfFalse(bool value, string message = null) => ThrowLastErrorIf(value, v => !v, message); /// Throws the last error if the value is an invalid handle. /// The SafeHandle to check. /// The message. public static T ThrowLastErrorIfInvalid(T value, string message = null) where T : SafeHandle => ThrowLastErrorIf(value, v => v.IsInvalid, message); /// Throws the last error if the value is a NULL pointer (IntPtr.Zero). /// The pointer to check. /// The message. public static IntPtr ThrowLastErrorIfNull(IntPtr value, string message = null) => ThrowLastErrorIf(value, v => v == IntPtr.Zero, message); /// Throws if the last error failed, unless the error is the specified value. /// The failure code to ignore. /// The message to associate with the exception. [System.Diagnostics.DebuggerStepThrough] public static void ThrowLastErrorUnless(Win32Error exception, string message = null) => GetLastError().ThrowUnless(exception, 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(Win32Error 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 Int32 value for comparison.", nameof(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(uint other) => other == value; /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// if the specified is equal to this instance; otherwise, . public override bool Equals(object obj) => 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(Win32Error other) => other.value == value; /// /// Gets the .NET associated with the value and optionally adds the supplied message. /// /// The optional message to assign to the . /// The associated or if this is not a failure. [SecurityCritical, SecuritySafeCritical] public Exception GetException(string message = null) => Succeeded ? null : ToHRESULT().GetException(message); /// 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() => unchecked((int)value); /// Throws if failed. /// The message. /// [System.Diagnostics.DebuggerStepThrough] public void ThrowIfFailed(string message = null) { if (value != ERROR_SUCCESS) throw GetException(message); } /// Throws if failed, unless the error is the specified value. /// The failure code to ignore. /// The message. [System.Diagnostics.DebuggerStepThrough] public void ThrowUnless(Win32Error exception, string message = null) { if (value != ERROR_SUCCESS && value != (uint)exception) throw GetException(message); } /// Converts this error to an . /// The equivalent of this error. public HRESULT ToHRESULT() => (HRESULT)this; /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { _=StaticFieldValueHash.TryGetFieldName(value, out var err); var msg = HRESULT.FormatMessage(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); short IConvertible.ToInt16(IFormatProvider provider) => ((IConvertible)value).ToInt16(provider); int IConvertible.ToInt32(IFormatProvider provider) => ((IConvertible)value).ToInt32(provider); 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)value).ToUInt16(provider); uint IConvertible.ToUInt32(IFormatProvider provider) => ((IConvertible)value).ToUInt32(provider); ulong IConvertible.ToUInt64(IFormatProvider provider) => ((IConvertible)value).ToUInt64(provider); [DllImport(Lib.Kernel32, SetLastError = false, EntryPoint = "GetLastError")] private static extern uint ExtGetLastError(); private static uint? ValueFromObj(object obj) { if (obj == null) return null; TypeConverter c = TypeDescriptor.GetConverter(obj); return c.CanConvertTo(typeof(uint)) ? (uint?)c.ConvertTo(obj, typeof(uint)) : null; } } internal class Win32ErrorTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType.IsPrimitive && sourceType != typeof(bool) && sourceType != typeof(char) || base.CanConvertFrom(context, sourceType); public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => destinationType == typeof(string) || destinationType.IsPrimitive && destinationType != typeof(char) || base.CanConvertTo(context, destinationType); public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) => value != null && value.GetType().IsPrimitive && value is not char && value is not bool ? new Win32Error((uint)Convert.ChangeType(value, TypeCode.UInt32)) : base.ConvertFrom(context, culture, value); public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value is not Win32Error err) throw new NotSupportedException(); if (destinationType.IsPrimitive && destinationType != typeof(char)) return Convert.ChangeType(err, destinationType); return destinationType == typeof(string) ? err.ToString() : base.ConvertTo(context, culture, value, destinationType); } }