using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices; using System.Security; namespace Vanara.PInvoke { /// /// Formal replacement for the Windows NTStatus definition. In ntstatus.h, it is a defined UINT value. For .NET, this class strongly /// types the value. /// The 32-bit value is organized as follows: /// /// /// Bit /// 0 - 1 /// 2 /// 3 /// 4 - 15 /// 16 - 31 /// /// /// Field /// Sev /// Customer /// Reserved /// Facility /// Code /// /// /// /// /// /// [StructLayout(LayoutKind.Sequential)] [TypeConverter(typeof(NTStatusTypeConverter))] [PInvokeData("winerr.h")] public partial struct NTStatus : IComparable, IComparable, IEquatable, IEquatable, IEquatable, IConvertible, IErrorProvider { internal readonly int _value; private const int codeMask = 0xFFFF; private const uint customerMask = 0x20000000; private const int FACILITY_NT_BIT = 0x10000000; private const uint facilityMask = 0x0FFF0000; private const int facilityShift = 16; private const uint severityMask = 0xC0000000; private const int severityShift = 30; /// Initializes a new instance of the structure. /// The raw NTStatus value. public NTStatus(int rawValue) => _value = rawValue; /// Initializes a new instance of the structure. /// The raw NTStatus value. public NTStatus(uint rawValue) => _value = unchecked((int)rawValue); /// Enumeration of facility codes [PInvokeData("winerr.h")] public enum FacilityCode : ushort { /// The default facility code. FACILITY_NULL = 0, /// The facility debugger FACILITY_DEBUGGER = 0x1, /// The facility RPC runtime FACILITY_RPC_RUNTIME = 0x2, /// The facility RPC stubs FACILITY_RPC_STUBS = 0x3, /// The facility io error code FACILITY_IO_ERROR_CODE = 0x4, /// The facility codclass error code FACILITY_CODCLASS_ERROR_CODE = 0x6, /// The facility ntwi N32 FACILITY_NTWIN32 = 0x7, /// The facility ntcert FACILITY_NTCERT = 0x8, /// The facility ntsspi FACILITY_NTSSPI = 0x9, /// The facility terminal server FACILITY_TERMINAL_SERVER = 0xA, /// The faciltiy MUI error code FACILTIY_MUI_ERROR_CODE = 0xB, /// The facility usb error code FACILITY_USB_ERROR_CODE = 0x10, /// The facility hid error code FACILITY_HID_ERROR_CODE = 0x11, /// The facility firewire error code FACILITY_FIREWIRE_ERROR_CODE = 0x12, /// The facility cluster error code FACILITY_CLUSTER_ERROR_CODE = 0x13, /// The facility acpi error code FACILITY_ACPI_ERROR_CODE = 0x14, /// The facility SXS error code FACILITY_SXS_ERROR_CODE = 0x15, /// The facility transaction FACILITY_TRANSACTION = 0x19, /// The facility commonlog FACILITY_COMMONLOG = 0x1A, /// The facility video FACILITY_VIDEO = 0x1B, /// The facility filter manager FACILITY_FILTER_MANAGER = 0x1C, /// The facility monitor FACILITY_MONITOR = 0x1D, /// The facility graphics kernel FACILITY_GRAPHICS_KERNEL = 0x1E, /// The facility driver framework FACILITY_DRIVER_FRAMEWORK = 0x20, /// The facility fve error code FACILITY_FVE_ERROR_CODE = 0x21, /// The facility FWP error code FACILITY_FWP_ERROR_CODE = 0x22, /// The facility ndis error code FACILITY_NDIS_ERROR_CODE = 0x23, /// The facility TPM FACILITY_TPM = 0x29, /// The facility RTPM FACILITY_RTPM = 0x2A, /// The facility hypervisor FACILITY_HYPERVISOR = 0x35, /// The facility ipsec FACILITY_IPSEC = 0x36, /// The facility virtualization FACILITY_VIRTUALIZATION = 0x37, /// The facility volmgr FACILITY_VOLMGR = 0x38, /// The facility BCD error code FACILITY_BCD_ERROR_CODE = 0x39, /// The facility wi N32 k ntuser FACILITY_WIN32K_NTUSER = 0x3E, /// The facility wi N32 k ntgdi FACILITY_WIN32K_NTGDI = 0x3F, /// The facility resume key filter FACILITY_RESUME_KEY_FILTER = 0x40, /// The facility RDBSS FACILITY_RDBSS = 0x41, /// The facility BTH att FACILITY_BTH_ATT = 0x42, /// The facility secureboot FACILITY_SECUREBOOT = 0x43, /// The facility audio kernel FACILITY_AUDIO_KERNEL = 0x44, /// The facility VSM FACILITY_VSM = 0x45, /// The facility volsnap FACILITY_VOLSNAP = 0x50, /// The facility sdbus FACILITY_SDBUS = 0x51, /// The facility shared VHDX FACILITY_SHARED_VHDX = 0x5C, /// The facility SMB FACILITY_SMB = 0x5D, /// The facility interix FACILITY_INTERIX = 0x99, /// The facility spaces FACILITY_SPACES = 0xE7, /// The facility security core FACILITY_SECURITY_CORE = 0xE8, /// The facility system integrity FACILITY_SYSTEM_INTEGRITY = 0xE9, /// The facility licensing FACILITY_LICENSING = 0xEA, /// The facility platform manifest FACILITY_PLATFORM_MANIFEST = 0xEB, /// The facility maximum value FACILITY_MAXIMUM_VALUE = 0xEC } /// A value indicating the severity of an value (bits 30-31). [PInvokeData("winerr.h")] public enum SeverityLevel : byte { /// /// Indicates a successful NTSTATUS value, such as STATUS_SUCCESS, or the value IO_ERR_RETRY_SUCCEEDED in error log packets. /// STATUS_SEVERITY_SUCCESS = 0x0, /// Indicates an informational NTSTATUS value, such as STATUS_SERIAL_MORE_WRITES. STATUS_SEVERITY_INFORMATIONAL = 0x1, /// Indicates a warning NTSTATUS value, such as STATUS_DEVICE_PAPER_EMPTY. STATUS_SEVERITY_WARNING = 0x2, /// /// Indicates an error NTSTATUS value, such as STATUS_INSUFFICIENT_RESOURCES for a FinalStatus value or /// IO_ERR_CONFIGURATION_ERROR for an ErrorCode value in error log packets. /// STATUS_SEVERITY_ERROR = 0x3 } /// Gets the code portion of the . /// The code value (bits 0-15). public ushort Code => GetCode(_value); /// Gets a value indicating whether this code is customer defined (true) or from Microsoft (false). /// true if customer defined; otherwise, false. public bool CustomerDefined => IsCustomerDefined(_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 => Severity == SeverityLevel.STATUS_SEVERITY_ERROR; /// 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 => !Failed; /// Performs an explicit conversion from to . /// The value. /// The resulting instance from the conversion. public static explicit operator HRESULT(NTStatus value) => value.ToHRESULT(); /// Performs an explicit conversion from to . /// The value. /// The result of the conversion. public static explicit operator int(NTStatus value) => value._value; /// Performs an explicit conversion from to . /// The value. /// The result of the conversion. public static explicit operator uint(NTStatus value) => unchecked((uint)value._value); /// Gets the code value from a 32-bit value. /// The 32-bit raw NTStatus value. /// The code value (bits 0-15). public static ushort GetCode(int ntstatus) => (ushort)(ntstatus & codeMask); /// Gets the facility value from a 32-bit value. /// The 32-bit raw NTStatus value. /// The facility value (bits 16-26). public static FacilityCode GetFacility(int ntstatus) => (FacilityCode)((ntstatus & facilityMask) >> facilityShift); /// Gets the severity value from a 32-bit value. /// The 32-bit raw NTStatus value. /// The severity value (bit 31). public static SeverityLevel GetSeverity(int ntstatus) => (SeverityLevel)((ntstatus & severityMask) >> severityShift); /// Performs an implicit conversion from to . /// The value. /// The result of the conversion. public static implicit operator NTStatus(int value) => new NTStatus(value); /// Performs an implicit conversion from to . /// The value. /// The result of the conversion. public static implicit operator NTStatus(uint value) => new NTStatus(value); /// Performs an implicit conversion from to . /// The value. /// The resulting instance from the conversion. public static implicit operator NTStatus(Win32Error value) => NTSTATUS_FROM_WIN32((uint)value); /// Gets the customer defined bit from a 32-bit value. /// The 32-bit raw NTStatus value. /// true if the customer defined bit is set; otherwise, false. public static bool IsCustomerDefined(int ntstatus) => (ntstatus & customerMask) > 0; /// Creates a new from provided values. /// The severity level. /// The bit is set for customer-defined values and clear for Microsoft-defined values. /// The facility. /// The code. /// The resulting . public static NTStatus Make(SeverityLevel severity, bool customerDefined, FacilityCode facility, ushort code) => Make(severity, customerDefined, (ushort)facility, code); /// Creates a new from provided values. /// The severity level. /// The bit is set for customer-defined values and clear for Microsoft-defined values. /// The facility. /// The code. /// The resulting . public static NTStatus Make(SeverityLevel severity, bool customerDefined, ushort facility, ushort code) => new NTStatus(unchecked((int)(((uint)severity << severityShift) | (customerDefined ? customerMask : 0U) | ((uint)facility << facilityShift) | code))); /// Converts a Win32 error to an NTSTATUS. /// The Win32 error codex. /// The equivalent NTSTATUS value. public static NTStatus NTSTATUS_FROM_WIN32(uint x) => unchecked((int)x) <= 0 ? unchecked((int)x) : unchecked((int)(((x) & 0x0000FFFF) | ((uint)FacilityCode.FACILITY_NTWIN32 << 16) | 0xC0000000U)); /// Implements the operator !=. /// The first . /// The second . /// The result of the operator. public static bool operator !=(NTStatus hrLeft, NTStatus hrRight) => !(hrLeft == hrRight); /// Implements the operator !=. /// The first . /// The second . /// The result of the operator. public static bool operator !=(NTStatus hrLeft, int hrRight) => !(hrLeft == hrRight); /// Implements the operator !=. /// The first . /// The second . /// The result of the operator. public static bool operator !=(NTStatus hrLeft, uint hrRight) => !(hrLeft == hrRight); /// Implements the operator ==. /// The first . /// The second . /// The result of the operator. public static bool operator ==(NTStatus hrLeft, NTStatus hrRight) => hrLeft.Equals(hrRight); /// Implements the operator ==. /// The first . /// The second . /// The result of the operator. public static bool operator ==(NTStatus hrLeft, int hrRight) => hrLeft.Equals(hrRight); /// Implements the operator ==. /// The first . /// The second . /// The result of the operator. public static bool operator ==(NTStatus hrLeft, uint hrRight) => hrLeft.Equals(hrRight); /// Converts the specified NTSTATUS code to its equivalent system error code. /// The NTSTATUS code to be converted. /// /// The function returns the corresponding system error code. ERROR_MR_MID_NOT_FOUND is returned when the specified NTSTATUS code /// does not have a corresponding system error code. /// [DllImport(Lib.NtDll, ExactSpelling = true)] [PInvokeData("Winternl.h", MSDNShortId = "ms680600")] public static extern uint RtlNtStatusToDosError(int status); /// /// If the supplied raw NTStatus value represents a failure, throw the associated with the optionally /// supplied message. /// /// The 32-bit raw NTStatus value. /// The optional message to assign to the . public static void ThrowIfFailed(int ntstatus, string message = null) => new NTStatus(ntstatus).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(NTStatus 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, NTStatus n => Equals(n), 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(NTStatus other) => other._value == _value; /// Gets the .NET associated with the NTStatus value and optionally adds the supplied message. /// The optional message to assign to the . /// The associated or null if this NTStatus is not a failure. [SecurityCritical] [SecuritySafeCritical] public Exception GetException(string message = null) => !Failed ? 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() => _value; /// /// If this represents a failure, throw the associated with the optionally supplied message. /// /// The optional message to assign to the . [SecurityCritical] [SecuritySafeCritical] public void ThrowIfFailed(string message = null) { var exception = GetException(message); if (exception != null) throw exception; } /// Converts this error to an . /// An equivalent . public HRESULT ToHRESULT() { Win32Error werr = RtlNtStatusToDosError(_value); return werr != Win32Error.ERROR_MR_MID_NOT_FOUND ? (HRESULT)werr : HRESULT_FROM_NT(_value); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { // Check for defined NTStatus value StaticFieldValueHash.TryGetFieldName(_value, out var err); var msg = HRESULT.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); 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); [ExcludeFromCodeCoverage] private static HRESULT HRESULT_FROM_NT(int ntStatus) => ntStatus | FACILITY_NT_BIT; 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 NTStatusTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (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 != null && value.GetType().IsPrimitive) { if (value is bool b) return b ? NTStatus.STATUS_SUCCESS : NTStatus.STATUS_UNSUCCESSFUL; if (!(value is char)) return new NTStatus((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 NTStatus)) throw new NotSupportedException(); if (destinationType.IsPrimitive && destinationType != typeof(char)) return Convert.ChangeType((NTStatus)value, destinationType); if (destinationType == typeof(string)) return ((NTStatus)value).ToString(); return base.ConvertTo(context, culture, value, destinationType); } } }