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);
}
}