From 8bb6b7289e983b0e95609e630c60722e6a165175 Mon Sep 17 00:00:00 2001 From: Inga Lovinde <52715130+inga-lovinde@users.noreply.github.com> Date: Sun, 1 Dec 2019 01:09:35 +0300 Subject: [PATCH] Workaround for WMI unavailability during PC shutdown --- .../Contracts/DeviceInfo.cs | 2 + .../Contracts/DevicesStatus.cs | 11 + .../Devices/DeviceHelper.cs | 5 + RadeonResetBugFixService/MainHandler.cs | 54 +++-- .../RadeonResetBugFixService.csproj | 5 +- .../Tasks/AbstractDevicesTask.cs | 91 ++++++++ .../Tasks/AbstractDriverTask.cs | 52 ----- .../Tasks/DisableAmdVideoTask.cs | 7 +- .../Tasks/DisableVirtualVideoTask.cs | 6 +- .../Tasks/EnableAmdVideoTask.cs | 6 +- .../Tasks/EnableVirtualVideoTask.cs | 6 +- .../Tasks/LastResortDevicesRestoreTask.cs | 105 +++++++++ RadeonResetBugFixService/Tasks/SleepTask.cs | 23 ++ .../ThirdParty/DisableDevice.cs | 201 ++++++++++++++++++ 14 files changed, 500 insertions(+), 74 deletions(-) create mode 100644 RadeonResetBugFixService/Contracts/DevicesStatus.cs create mode 100644 RadeonResetBugFixService/Tasks/AbstractDevicesTask.cs delete mode 100644 RadeonResetBugFixService/Tasks/AbstractDriverTask.cs create mode 100644 RadeonResetBugFixService/Tasks/LastResortDevicesRestoreTask.cs create mode 100644 RadeonResetBugFixService/Tasks/SleepTask.cs diff --git a/RadeonResetBugFixService/Contracts/DeviceInfo.cs b/RadeonResetBugFixService/Contracts/DeviceInfo.cs index 7fb7ed1..bbfe783 100644 --- a/RadeonResetBugFixService/Contracts/DeviceInfo.cs +++ b/RadeonResetBugFixService/Contracts/DeviceInfo.cs @@ -8,6 +8,8 @@ public string ClassName { get; set; } + public string Description => $"{this.Name} ({this.DeviceId}, {this.ClassGuid})"; + public string DeviceId { get; set; } public long? ErrorCode { get; set; } diff --git a/RadeonResetBugFixService/Contracts/DevicesStatus.cs b/RadeonResetBugFixService/Contracts/DevicesStatus.cs new file mode 100644 index 0000000..ffd8e09 --- /dev/null +++ b/RadeonResetBugFixService/Contracts/DevicesStatus.cs @@ -0,0 +1,11 @@ +namespace RadeonResetBugFixService.Contracts +{ + using System.Collections.Generic; + + class DevicesStatus + { + public List EnabledDevices { get; } = new List(); + + public List DisabledDevices { get; } = new List(); + } +} diff --git a/RadeonResetBugFixService/Devices/DeviceHelper.cs b/RadeonResetBugFixService/Devices/DeviceHelper.cs index 9ae2bac..73877b3 100644 --- a/RadeonResetBugFixService/Devices/DeviceHelper.cs +++ b/RadeonResetBugFixService/Devices/DeviceHelper.cs @@ -79,5 +79,10 @@ { ThirdParty.DisableDevice.DeviceHelper.SetDeviceEnabled(deviceInfo.ClassGuid, deviceInfo.DeviceId, true); } + + public static bool? IsDeviceCurrentlyDisabled(DeviceInfo deviceInfo) + { + return ThirdParty.DisableDevice.DeviceHelper.IsDeviceDisabled(deviceInfo.ClassGuid, deviceInfo.DeviceId); + } } } diff --git a/RadeonResetBugFixService/MainHandler.cs b/RadeonResetBugFixService/MainHandler.cs index 3d810d5..3c1cae7 100644 --- a/RadeonResetBugFixService/MainHandler.cs +++ b/RadeonResetBugFixService/MainHandler.cs @@ -11,6 +11,10 @@ { private string LogFilename { get; } + private DevicesStatus StartupDevicesStatus { get; } = new DevicesStatus(); + + private DevicesStatus ShutdownDevicesStatus { get; } = new DevicesStatus(); + private object Mutex = new object(); public MainHandler() @@ -28,15 +32,23 @@ { using (ILogger logger = new TaskLoggerWrapper(fileLogger, "Startup")) { - lock (this.Mutex) + try + { + lock (this.Mutex) + { + TasksProcessor.ProcessTasks( + logger, + new ITask[] + { + new SleepTask(TimeSpan.FromSeconds(20)), + new DisableVirtualVideoTask(this.StartupDevicesStatus), + new EnableAmdVideoTask(this.StartupDevicesStatus), + }); + } + } + catch (Exception e) { - TasksProcessor.ProcessTasks( - logger, - new ITask[] - { - new DisableVirtualVideoTask(), - new EnableAmdVideoTask(), - }); + logger.LogError(e.ToString()); } } } @@ -48,16 +60,24 @@ { using (ILogger logger = new TaskLoggerWrapper(fileLogger, "Shutdown")) { - lock (this.Mutex) + try + { + lock (this.Mutex) + { + TasksProcessor.ProcessTasks( + logger, + new ITask[] + { + new StopAudioServiceTask(), + new DisableAmdVideoTask(this.ShutdownDevicesStatus), + new EnableVirtualVideoTask(this.ShutdownDevicesStatus), + new LastResortDevicesRestoreTask(this.StartupDevicesStatus), + }); + } + } + catch (Exception e) { - TasksProcessor.ProcessTasks( - logger, - new ITask[] - { - new StopAudioServiceTask(), - new DisableAmdVideoTask(), - new EnableVirtualVideoTask(), - }); + logger.LogError(e.ToString()); } } } diff --git a/RadeonResetBugFixService/RadeonResetBugFixService.csproj b/RadeonResetBugFixService/RadeonResetBugFixService.csproj index 6568400..467bfd0 100644 --- a/RadeonResetBugFixService/RadeonResetBugFixService.csproj +++ b/RadeonResetBugFixService/RadeonResetBugFixService.csproj @@ -52,6 +52,7 @@ + @@ -71,9 +72,11 @@ + + - + diff --git a/RadeonResetBugFixService/Tasks/AbstractDevicesTask.cs b/RadeonResetBugFixService/Tasks/AbstractDevicesTask.cs new file mode 100644 index 0000000..c33d4af --- /dev/null +++ b/RadeonResetBugFixService/Tasks/AbstractDevicesTask.cs @@ -0,0 +1,91 @@ +namespace RadeonResetBugFixService.Tasks +{ + using System; + using System.Linq; + using Contracts; + using Devices; + + abstract class AbstractDevicesTask : ITask + { + public abstract string TaskName { get; } + + private DevicesStatus DevicesStatus { get; } + + protected AbstractDevicesTask(DevicesStatus devicesStatus) + { + if (devicesStatus == null) + { + throw new ArgumentNullException(nameof(devicesStatus)); + } + + this.DevicesStatus = devicesStatus; + } + + protected virtual bool ShouldDisable(DeviceInfo deviceInfo) => false; + + protected virtual bool ShouldEnable(DeviceInfo deviceInfo) => false; + + void ITask.Run(ILogger logger) + { + foreach (var device in DeviceHelper.GetDevices().ToArray()) + { + if (this.ShouldDisable(device)) + { + this.DevicesStatus.DisabledDevices.Add(device); + + if (device.IsDisabled) + { + logger.Log($"{device.Description} is already disabled"); + } + else + { + logger.Log($"Disabling {device.Description}"); + DeviceHelper.DisableDevice(device); + logger.Log($"Disabled {device.Description}"); + } + } + else if (this.ShouldEnable(device)) + { + this.DevicesStatus.EnabledDevices.Add(device); + + if (!device.IsDisabled) + { + logger.Log($"{device.Description} is already enabled"); + } + else + { + logger.Log($"Enabling {device.Description}"); + DeviceHelper.EnableDevice(device); + logger.Log($"Enabled {device.Description}"); + } + } + } + + foreach (var device in DeviceHelper.GetDevices().ToArray()) + { + if (this.ShouldDisable(device)) + { + if (!device.IsDisabled) + { + logger.LogError($"{device.Description} is enabled but should be disabled"); + } + else + { + logger.Log($"Successfully checked {device.Description} status"); + } + } + else if (this.ShouldEnable(device)) + { + if (device.IsDisabled) + { + logger.Log($"{device.Description} is disabled but should be enabled"); + } + else + { + logger.Log($"Successfully checked {device.Description} status"); + } + } + } + } + } +} diff --git a/RadeonResetBugFixService/Tasks/AbstractDriverTask.cs b/RadeonResetBugFixService/Tasks/AbstractDriverTask.cs deleted file mode 100644 index 36d7a6a..0000000 --- a/RadeonResetBugFixService/Tasks/AbstractDriverTask.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace RadeonResetBugFixService.Tasks -{ - using System.Linq; - using Contracts; - using Devices; - - abstract class AbstractDriverTask : ITask - { - protected virtual bool ShouldDisable(DeviceInfo deviceInfo) => false; - - protected virtual bool ShouldEnable(DeviceInfo deviceInfo) => false; - - public abstract string TaskName { get; } - - void ITask.Run(ILogger logger) - { - var devices = DeviceHelper.GetDevices().ToArray(); - foreach (var device in devices) - { - //logger.Log($"Present({device.IsPresent}) ErrorCode({device.ErrorCode}) Disabled({device.IsDisabled}) ClassName({device.ClassName}) Service({device.Service}) Manufacturer({device.Manufacturer}) Name({device.Name}) Id({device.DeviceId}) ClassGuid({device.ClassGuid})"); - var deviceDescription = $"{device.Name} ({device.DeviceId}, {device.ClassGuid})"; - - if (this.ShouldDisable(device)) - { - if (device.IsDisabled) - { - logger.Log($"{deviceDescription} already disabled"); - } - else - { - logger.Log($"Disabling {deviceDescription}"); - DeviceHelper.DisableDevice(device); - logger.Log($"Disabled {deviceDescription}"); - } - } - else if (this.ShouldEnable(device)) - { - if (!device.IsDisabled) - { - logger.Log($"{deviceDescription} already enabled"); - } - else - { - logger.Log($"Enabling {deviceDescription}"); - DeviceHelper.EnableDevice(device); - logger.Log($"Enabled {deviceDescription}"); - } - } - } - } - } -} diff --git a/RadeonResetBugFixService/Tasks/DisableAmdVideoTask.cs b/RadeonResetBugFixService/Tasks/DisableAmdVideoTask.cs index fe63086..021136f 100644 --- a/RadeonResetBugFixService/Tasks/DisableAmdVideoTask.cs +++ b/RadeonResetBugFixService/Tasks/DisableAmdVideoTask.cs @@ -5,9 +5,14 @@ namespace RadeonResetBugFixService.Tasks using Contracts; using Devices; - class DisableAmdVideoTask : AbstractDriverTask + class DisableAmdVideoTask : AbstractDevicesTask { public override string TaskName => "Disabling AMD video"; + + public DisableAmdVideoTask(DevicesStatus devicesStatus) : base(devicesStatus) + { + } + protected override bool ShouldDisable(DeviceInfo deviceInfo) => KnownDevices.IsAmdVideo(deviceInfo); } } diff --git a/RadeonResetBugFixService/Tasks/DisableVirtualVideoTask.cs b/RadeonResetBugFixService/Tasks/DisableVirtualVideoTask.cs index fac89e2..4351e28 100644 --- a/RadeonResetBugFixService/Tasks/DisableVirtualVideoTask.cs +++ b/RadeonResetBugFixService/Tasks/DisableVirtualVideoTask.cs @@ -3,10 +3,14 @@ using Contracts; using Devices; - class DisableVirtualVideoTask : AbstractDriverTask + class DisableVirtualVideoTask : AbstractDevicesTask { public override string TaskName => "Disabling virtual video"; + public DisableVirtualVideoTask(DevicesStatus devicesStatus) : base(devicesStatus) + { + } + protected override bool ShouldDisable(DeviceInfo deviceInfo) => KnownDevices.IsVirtualVideo(deviceInfo); } } diff --git a/RadeonResetBugFixService/Tasks/EnableAmdVideoTask.cs b/RadeonResetBugFixService/Tasks/EnableAmdVideoTask.cs index fc82a45..723f75c 100644 --- a/RadeonResetBugFixService/Tasks/EnableAmdVideoTask.cs +++ b/RadeonResetBugFixService/Tasks/EnableAmdVideoTask.cs @@ -3,10 +3,14 @@ using Contracts; using Devices; - class EnableAmdVideoTask : AbstractDriverTask + class EnableAmdVideoTask : AbstractDevicesTask { public override string TaskName => "Enabling AMD video"; + public EnableAmdVideoTask(DevicesStatus devicesStatus) : base(devicesStatus) + { + } + protected override bool ShouldEnable(DeviceInfo deviceInfo) => KnownDevices.IsAmdVideo(deviceInfo); } diff --git a/RadeonResetBugFixService/Tasks/EnableVirtualVideoTask.cs b/RadeonResetBugFixService/Tasks/EnableVirtualVideoTask.cs index 83cd82e..ddbfdf0 100644 --- a/RadeonResetBugFixService/Tasks/EnableVirtualVideoTask.cs +++ b/RadeonResetBugFixService/Tasks/EnableVirtualVideoTask.cs @@ -3,10 +3,14 @@ using Contracts; using Devices; - class EnableVirtualVideoTask : AbstractDriverTask + class EnableVirtualVideoTask : AbstractDevicesTask { public override string TaskName => "Enabling virtual video"; + public EnableVirtualVideoTask(DevicesStatus devicesStatus) : base(devicesStatus) + { + } + protected override bool ShouldEnable(DeviceInfo deviceInfo) => KnownDevices.IsVirtualVideo(deviceInfo); } } diff --git a/RadeonResetBugFixService/Tasks/LastResortDevicesRestoreTask.cs b/RadeonResetBugFixService/Tasks/LastResortDevicesRestoreTask.cs new file mode 100644 index 0000000..41be2e6 --- /dev/null +++ b/RadeonResetBugFixService/Tasks/LastResortDevicesRestoreTask.cs @@ -0,0 +1,105 @@ +namespace RadeonResetBugFixService.Tasks +{ + using System; + using Contracts; + using Devices; + + class LastResortDevicesRestoreTask : ITask + { + string ITask.TaskName => "Attempting to restore devices state"; + + private DevicesStatus StartupDevicesStatus { get; } + + public LastResortDevicesRestoreTask(DevicesStatus startupDevicesStatus) + { + if (startupDevicesStatus == null) + { + throw new ArgumentNullException(nameof(startupDevicesStatus)); + } + + this.StartupDevicesStatus = startupDevicesStatus; + } + + void ITask.Run(ILogger logger) + { + foreach (var device in this.StartupDevicesStatus.EnabledDevices) + { + logger.Log($"Processing {device.Description}; should be disabled"); + try + { + var disabledStatus = DeviceHelper.IsDeviceCurrentlyDisabled(device); + if (!disabledStatus.HasValue) + { + logger.Log("Device not present"); + } + else if (!disabledStatus.Value) + { + logger.Log("Device enabled; attempting to disable..."); + DeviceHelper.DisableDevice(device); + logger.Log("Disabled device; checking status..."); + var newStatus = DeviceHelper.IsDeviceCurrentlyDisabled(device); + if (!newStatus.HasValue) + { + logger.LogError("Device not present"); + } + else if (!newStatus.Value) + { + logger.LogError("Device is enabled but should be disabled"); + } + else + { + logger.Log("Successfully checked device status"); + } + } + else + { + logger.Log("Device is disabled"); + } + } + catch (Exception e) + { + logger.LogError(e.ToString()); + } + } + + foreach (var device in this.StartupDevicesStatus.DisabledDevices) + { + logger.Log($"Processing {device.Description}; should be enabled"); + try + { + var disabledStatus = DeviceHelper.IsDeviceCurrentlyDisabled(device); + if (!disabledStatus.HasValue) + { + logger.Log("Device not present"); + } + else if (disabledStatus.Value) + { + logger.Log("Device disabled; attempting to enable..."); + DeviceHelper.EnableDevice(device); + logger.Log("Enabled device; checking status..."); + var newStatus = DeviceHelper.IsDeviceCurrentlyDisabled(device); + if (!newStatus.HasValue) + { + logger.LogError("Device not present"); + } else if (newStatus.Value) + { + logger.LogError("Device is disabled but should be enabled"); + } + else + { + logger.Log("Successfully checked device status"); + } + } + else + { + logger.Log("Device is enabled"); + } + } + catch (Exception e) + { + logger.LogError(e.ToString()); + } + } + } + } +} diff --git a/RadeonResetBugFixService/Tasks/SleepTask.cs b/RadeonResetBugFixService/Tasks/SleepTask.cs new file mode 100644 index 0000000..5f8de2e --- /dev/null +++ b/RadeonResetBugFixService/Tasks/SleepTask.cs @@ -0,0 +1,23 @@ +namespace RadeonResetBugFixService.Tasks +{ + using System; + using System.Threading; + using Contracts; + + class SleepTask : ITask + { + private TimeSpan SleepTime { get; } + + public string TaskName => "Sleep"; + + public SleepTask(TimeSpan sleepTime) + { + this.SleepTime = sleepTime; + } + + public void Run(ILogger logger) + { + Thread.Sleep(this.SleepTime); + } + } +} diff --git a/RadeonResetBugFixService/ThirdParty/DisableDevice.cs b/RadeonResetBugFixService/ThirdParty/DisableDevice.cs index d5eaf5f..4c4dc1b 100644 --- a/RadeonResetBugFixService/ThirdParty/DisableDevice.cs +++ b/RadeonResetBugFixService/ThirdParty/DisableDevice.cs @@ -1,6 +1,7 @@ namespace RadeonResetBugFixService.ThirdParty.DisableDevice { // Code taken from https://stackoverflow.com/a/1610140 + // Code for obtaining device status is mine using System; using System.Text; @@ -158,6 +159,150 @@ OnlyValidateViaAuthenticode = unchecked((int)0xe0000245) } + // CR_ return values from Cfgmgr32.h + internal enum CmReturnCode : int + { + CR_SUCCESS = 0x00000000, + CR_DEFAULT = 0x00000001, + CR_OUT_OF_MEMORY = 0x00000002, + CR_INVALID_POINTER = 0x00000003, + CR_INVALID_FLAG = 0x00000004, + CR_INVALID_DEVNODE = 0x00000005, + CR_INVALID_RES_DES = 0x00000006, + CR_INVALID_LOG_CONF = 0x00000007, + CR_INVALID_ARBITRATOR = 0x00000008, + CR_INVALID_NODELIST = 0x00000009, + CR_DEVNODE_HAS_REQS = 0x0000000A, + CR_INVALID_RESOURCEID = 0x0000000B, + CR_DLVXD_NOT_FOUND = 0x0000000C, + CR_NO_SUCH_DEVNODE = 0x0000000D, + CR_NO_MORE_LOG_CONF = 0x0000000E, + CR_NO_MORE_RES_DES = 0x0000000F, + CR_ALREADY_SUCH_DEVNODE = 0x00000010, + CR_INVALID_RANGE_LIST = 0x00000011, + CR_INVALID_RANGE = 0x00000012, + CR_FAILURE = 0x00000013, + CR_NO_SUCH_LOGICAL_DEV = 0x00000014, + CR_CREATE_BLOCKED = 0x00000015, + CR_NOT_SYSTEM_VM = 0x00000016, + CR_REMOVE_VETOED = 0x00000017, + CR_APM_VETOED = 0x00000018, + CR_INVALID_LOAD_TYPE = 0x00000019, + CR_BUFFER_SMALL = 0x0000001A, + CR_NO_ARBITRATOR = 0x0000001B, + CR_NO_REGISTRY_HANDLE = 0x0000001C, + CR_REGISTRY_ERROR = 0x0000001D, + CR_INVALID_DEVICE_ID = 0x0000001E, + CR_INVALID_DATA = 0x0000001F, + CR_INVALID_API = 0x00000020, + CR_DEVLOADER_NOT_READY = 0x00000021, + CR_NEED_RESTART = 0x00000022, + CR_NO_MORE_HW_PROFILES = 0x00000023, + CR_DEVICE_NOT_THERE = 0x00000024, + CR_NO_SUCH_VALUE = 0x00000025, + CR_WRONG_TYPE = 0x00000026, + CR_INVALID_PRIORITY = 0x00000027, + CR_NOT_DISABLEABLE = 0x00000028, + CR_FREE_RESOURCES = 0x00000029, + CR_QUERY_VETOED = 0x0000002A, + CR_CANT_SHARE_IRQ = 0x0000002B, + CR_NO_DEPENDENT = 0x0000002C, + CR_SAME_RESOURCES = 0x0000002D, + CR_NO_SUCH_REGISTRY_KEY = 0x0000002E, + CR_INVALID_MACHINENAME = 0x0000002F, + CR_REMOTE_COMM_FAILURE = 0x00000030, + CR_MACHINE_UNAVAILABLE = 0x00000031, + CR_NO_CM_SERVICES = 0x00000032, + CR_ACCESS_DENIED = 0x00000033, + CR_CALL_NOT_IMPLEMENTED = 0x00000034, + CR_INVALID_PROPERTY = 0x00000035, + CR_DEVICE_INTERFACE_ACTIVE = 0x00000036, + CR_NO_SUCH_DEVICE_INTERFACE = 0x00000037, + CR_INVALID_REFERENCE_STRING = 0x00000038, + CR_INVALID_CONFLICT_LIST = 0x00000039, + CR_INVALID_INDEX = 0x0000003A, + CR_INVALID_STRUCTURE_SIZE = 0x0000003B, + } + + // DN_ flags from Cfg.h + [Flags] + internal enum CmStatus : ulong + { + DN_ROOT_ENUMERATED = 0x00000001, /* Was enumerated by ROOT */ + DN_DRIVER_LOADED = 0x00000002, /* Has Register_Device_Driver */ + DN_ENUM_LOADED = 0x00000004, /* Has Register_Enumerator */ + DN_STARTED = 0x00000008, /* Is currently configured */ + DN_MANUAL = 0x00000010, /* Manually installed */ + DN_NEED_TO_ENUM = 0x00000020, /* May need reenumeration */ + DN_NOT_FIRST_TIME = 0x00000040, /* Has received a config */ + DN_HARDWARE_ENUM = 0x00000080, /* Enum generates hardware ID */ + DN_LIAR = 0x00000100, /* Lied about can reconfig once */ + DN_HAS_MARK = 0x00000200, /* Not CM_Create_DevNode lately */ + DN_HAS_PROBLEM = 0x00000400, /* Need device installer */ + DN_FILTERED = 0x00000800, /* Is filtered */ + DN_MOVED = 0x00001000, /* Has been moved */ + DN_DISABLEABLE = 0x00002000, /* Can be rebalanced */ + DN_REMOVABLE = 0x00004000, /* Can be removed */ + DN_PRIVATE_PROBLEM = 0x00008000, /* Has a private problem */ + DN_MF_PARENT = 0x00010000, /* Multi function parent */ + DN_MF_CHILD = 0x00020000, /* Multi function child */ + DN_WILL_BE_REMOVED = 0x00040000, /* Devnode is being removed */ + } + + // CM_PROB_ problem values defined in Cfg.h + internal enum CmProblem : ulong + { + CM_PROB_NOT_CONFIGURED = 0x00000001, + CM_PROB_DEVLOADER_FAILED = 0x00000002, + CM_PROB_OUT_OF_MEMORY = 0x00000003, + CM_PROB_ENTRY_IS_WRONG_TYPE = 0x00000004, + CM_PROB_LACKED_ARBITRATOR = 0x00000005, + CM_PROB_BOOT_CONFIG_CONFLICT = 0x00000006, + CM_PROB_FAILED_FILTER = 0x00000007, + CM_PROB_DEVLOADER_NOT_FOUND = 0x00000008, + CM_PROB_INVALID_DATA = 0x00000009, + CM_PROB_FAILED_START = 0x0000000A, + CM_PROB_LIAR = 0x0000000B, + CM_PROB_NORMAL_CONFLICT = 0x0000000C, + CM_PROB_NOT_VERIFIED = 0x0000000D, + CM_PROB_NEED_RESTART = 0x0000000E, + CM_PROB_REENUMERATION = 0x0000000F, + CM_PROB_PARTIAL_LOG_CONF = 0x00000010, + CM_PROB_UNKNOWN_RESOURCE = 0x00000011, + CM_PROB_REINSTALL = 0x00000012, + CM_PROB_REGISTRY = 0x00000013, + CM_PROB_VXDLDR = 0x00000014, + CM_PROB_WILL_BE_REMOVED = 0x00000015, + CM_PROB_DISABLED = 0x00000016, + CM_PROB_DEVLOADER_NOT_READY = 0x00000017, + CM_PROB_DEVICE_NOT_THERE = 0x00000018, + CM_PROB_MOVED = 0x00000019, + CM_PROB_TOO_EARLY = 0x0000001A, + CM_PROB_NO_VALID_LOG_CONF = 0x0000001B, + CM_PROB_FAILED_INSTALL = 0x0000001C, + CM_PROB_HARDWARE_DISABLED = 0x0000001D, + CM_PROB_CANT_SHARE_IRQ = 0x0000001E, + CM_PROB_FAILED_ADD = 0x0000001F, + CM_PROB_DISABLED_SERVICE = 0x00000020, + CM_PROB_TRANSLATION_FAILED = 0x00000021, + CM_PROB_NO_SOFTCONFIG = 0x00000022, + CM_PROB_BIOS_TABLE = 0x00000023, + CM_PROB_IRQ_TRANSLATION_FAILED = 0x00000024, + CM_PROB_FAILED_DRIVER_ENTRY = 0x00000025, + CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD = 0x00000026, + CM_PROB_DRIVER_FAILED_LOAD = 0x00000027, + CM_PROB_DRIVER_SERVICE_KEY_INVALID = 0x00000028, + CM_PROB_LEGACY_SERVICE_NO_DEVICES = 0x00000029, + CM_PROB_DUPLICATE_DEVICE = 0x0000002A, + CM_PROB_FAILED_POST_START = 0x0000002B, + CM_PROB_HALTED = 0x0000002C, + CM_PROB_PHANTOM = 0x0000002D, + CM_PROB_SYSTEM_SHUTDOWN = 0x0000002E, + CM_PROB_HELD_FOR_EJECT = 0x0000002F, + CM_PROB_DRIVER_BLOCKED = 0x00000030, + CM_PROB_REGISTRY_TOO_LARGE = 0x00000031, + } + [StructLayout(LayoutKind.Sequential)] internal struct DeviceInfoData { @@ -183,6 +328,8 @@ private const string setupapi = "setupapi.dll"; + private const string cfgmgr32 = "cfgmgr32.dll"; + private NativeMethods() { } @@ -231,6 +378,12 @@ ref int requiredSize); ref DeviceInfoData deviceInfoData, [In()] ref PropertyChangeParameters classInstallParams, int classInstallParamsSize); + [DllImport(cfgmgr32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.I4)] + public static extern CmReturnCode CM_Get_DevNode_Status(ref CmStatus pulStatus, +ref CmProblem pulProblemNumber, +int dnDevInst, ulong ulFlags); + } internal class SafeDeviceInfoSetHandle : SafeHandleZeroOrMinusOneIsInvalid @@ -290,6 +443,54 @@ ref PropertyChangeParameters classInstallParams, int classInstallParamsSize); } } + /// + /// Returns true if device is disabled + /// + /// The class guid of the device. Available in the device manager. + /// The device instance id of the device. Available in the device manager. + public static bool? IsDeviceDisabled(Guid classGuid, string instanceId) + { + SafeDeviceInfoSetHandle diSetHandle = null; + try + { + // Get the handle to a device information set for all devices matching classGuid that are present on the + // system. + diSetHandle = NativeMethods.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, SetupDiGetClassDevsFlags.Present); + // Get the device information data for each matching device. + DeviceInfoData[] diData = GetDeviceInfoData(diSetHandle); + // Find the index of our instance. i.e. the touchpad mouse - I have 3 mice attached... + int index = GetIndexOfInstance(diSetHandle, diData, instanceId); + + if (index == -1) + { + return null; + } + + // Get status... + var status = default(CmStatus); + var problem = default(CmProblem); + var result = NativeMethods.CM_Get_DevNode_Status(ref status, ref problem, diData[index].DevInst, 0); + + if (result != CmReturnCode.CR_SUCCESS) + { + throw new Win32Exception($"Cfgmgr32 error: {result}"); + } + + return problem == CmProblem.CM_PROB_DISABLED; + } + finally + { + if (diSetHandle != null) + { + if (diSetHandle.IsClosed == false) + { + diSetHandle.Close(); + } + diSetHandle.Dispose(); + } + } + } + private static DeviceInfoData[] GetDeviceInfoData(SafeDeviceInfoSetHandle handle) { List data = new List();