diff --git a/PInvoke/Shared/WinError/Win32Error.cs b/PInvoke/Shared/WinError/Win32Error.cs index 3967c53f..1ea738fc 100644 --- a/PInvoke/Shared/WinError/Win32Error.cs +++ b/PInvoke/Shared/WinError/Win32Error.cs @@ -4,297 +4,283 @@ using System.Globalization; using System.Runtime.InteropServices; using System.Security; -namespace Vanara.PInvoke +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 { - /// 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) { - internal readonly uint value; - - /// Initializes a new instance of the struct with an error value. - /// The i. - public Win32Error(uint i) => value = i; - - /// Gets a value indicating whether this is a failure. - /// true if failed; otherwise, false. - public bool Failed => !Succeeded; - - /// Gets a value indicating whether this is a success. - /// true if succeeded; otherwise, false. - 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); - if (hr.Facility == HRESULT.FacilityCode.FACILITY_WIN32) - return unchecked((uint)hr.Code); - return ERROR_UNIDENTIFIED_ERROR; - } - - /// Gets the last error. - /// - [SecurityCritical] - [System.Diagnostics.DebuggerStepThrough] - public static Win32Error GetLastError() => new Win32Error(ExtGetLastError()); - - /// Performs an explicit conversion from to . - /// The value. - /// The result of the conversion. - public static implicit operator Win32Error(uint value) => new Win32Error(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 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. - /// true if the specified is equal to this instance; otherwise, false. - 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 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) => 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; - var c = TypeDescriptor.GetConverter(obj); - return c.CanConvertTo(typeof(uint)) ? (uint?)c.ConvertTo(obj, typeof(uint)) : null; - } + 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; } - internal class Win32ErrorTypeConverter : TypeConverter + /// 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) { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - if (sourceType.IsPrimitive && sourceType != typeof(bool) && sourceType != typeof(char)) - return true; - return base.CanConvertFrom(context, sourceType); - } + if (valueIsFailure(value)) + GetLastError().ThrowIfFailed(message); + return value; + } - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (destinationType == typeof(string) || destinationType.IsPrimitive && destinationType != typeof(char)) - return true; - return base.CanConvertTo(context, destinationType); - } + /// 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); - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) - { - if (value != null && value.GetType().IsPrimitive && !(value is char) && !(value is bool)) - return new Win32Error((uint)Convert.ChangeType(value, TypeCode.UInt32)); - return base.ConvertFrom(context, culture, value); - } + /// 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); - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, - Type destinationType) - { - if (!(value is Win32Error err)) throw new NotSupportedException(); - if (destinationType.IsPrimitive && destinationType != typeof(char)) - return Convert.ChangeType(err, destinationType); - if (destinationType == typeof(string)) - return err.ToString(); - return base.ConvertTo(context, culture, value, destinationType); - } + /// 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); } } \ No newline at end of file