Working With Devices: Getting Status on a Device

This time we will look at checking if a device is enabled, disabled, or has a problem. From last time we received a device structure that contained a SP_DEVINFO_DATA structure. Within that structure we find a DevInst integer. With that and the use of the CM_Get_DevNode_Status function we can get status information on a device.

Private Declare Auto Function CM_Get_DevNode_Status Lib "cfgmgr32.dll" (ByRef pulStatus As IntPtr, ByRef pulProblemNumber As IntPtr, ByVal dnDevInst As Integer, ByVal ulFlags As Long) As Integer

This function will return the status and any problem with the device given. The last variable must be set to 0. The Status and Problem flags can have numerous meanings, so we have two enumerations to describe all the possible values. I've only encountered a handful of status and problem codes through normal use.

'Status Flags. Devices can have one or more of these. ANDing is used to determine which apply
Public Enum DN_Flags
DN_ROOT_ENUMERATED = &H1 ' Was enumerated by ROOT
DN_DRIVER_LOADED = &H2 ' Has Register_Device_Driver
DN_ENUM_LOADED = &H4 ' Has Register_Enumerator
DN_STARTED = &H8 ' Is currently configured
DN_MANUAL = &H10 ' Manually installed
DN_NEED_TO_ENUM = &H20 ' May need reenumeration
DN_NOT_FIRST_TIME = &H40 ' Has received a config
DN_HARDWARE_ENUM = &H80 ' Enum generates hardware ID
DN_LIAR = &H100 ' Lied about can reconfig once
DN_HAS_MARK = &H200 ' Not CM_Create_DevInst lately
DN_HAS_PROBLEM = &H400 ' Need device installer
DN_FILTERED = &H800 ' Is filtered
DN_MOVED = &H1000 ' Has been moved
DN_DISABLEABLE = &H2000 ' Can be disabled
DN_REMOVABLE = &H4000 ' Can be removed
DN_PRIVATE_PROBLEM = &H8000 ' Has a private problem
DN_MF_PARENT = &H10000 ' Multi function parent
DN_MF_CHILD = &H20000 ' Multi function child
DN_WILL_BE_REMOVED = &H40000 ' DevInst is being removed
DN_NOT_FIRST_TIMEE = &H80000 ' Has received a config enumerate
DN_STOP_FREE_RES = &H100000 ' When child is stopped, free resources
DN_REBAL_CANDIDATE = &H200000 ' Don't skip during rebalance
DN_BAD_PARTIAL = &H400000 ' This devnode's log_confs do not have same resources
DN_NT_ENUMERATOR = &H800000 ' This devnode's is an NT enumerator
DN_NT_DRIVER = &H1000000 ' This devnode's is an NT driver
DN_NEEDS_LOCKING = &H2000000 ' Devnode need lock resume processing
DN_ARM_WAKEUP = &H4000000 ' Devnode can be the wakeup device
DN_APM_ENUMERATOR = &H8000000 ' APM aware enumerator
DN_APM_DRIVER = &H10000000 ' APM aware driver
DN_SILENT_INSTALL = &H20000000 ' Silent install
DN_NO_SHOW_IN_DM = &H40000000 ' No show in device manager
DN_BOOT_LOG_PROB = &H80000000 ' Had a problem during preassignment of boot log conf
End Enum

'Problem associated with device. A device has 0 or 1 problem associated with it.
Public Enum CM_PROB
CM_PROB_NOT_CONFIGURED = &H1 ' no config for device
CM_PROB_DEVLOADER_FAILED = &H2 ' service load failed
CM_PROB_OUT_OF_MEMORY = &H3 ' out of memory
CM_PROB_ENTRY_IS_WRONG_TYPE = &H4 '
CM_PROB_LACKED_ARBITRATOR = &H5 '
CM_PROB_BOOT_CONFIG_CONFLICT = &H6 ' boot config conflict
CM_PROB_FAILED_FILTER = &H7 '
CM_PROB_DEVLOADER_NOT_FOUND = &H8 ' Devloader not found
CM_PROB_INVALID_DATA = &H9 ' Invalid ID
CM_PROB_FAILED_START = &HA '
CM_PROB_LIAR = &HB '
CM_PROB_NORMAL_CONFLICT = &HC ' config conflict
CM_PROB_NOT_VERIFIED = &HD '
CM_PROB_NEED_RESTART = &HE ' requires restart
CM_PROB_REENUMERATION = &HF '
CM_PROB_PARTIAL_LOG_CONF = &H10 '
CM_PROB_UNKNOWN_RESOURCE = &H11 ' unknown res type
CM_PROB_REINSTALL = &H12 '
CM_PROB_REGISTRY = &H13 '
CM_PROB_VXDLDR = &H14 ' WINDOWS 95 ONLY
CM_PROB_WILL_BE_REMOVED = &H15 ' devinst will remove
CM_PROB_DISABLED = &H16 ' devinst is disabled
CM_PROB_DEVLOADER_NOT_READY = &H17 ' Devloader not ready
CM_PROB_DEVICE_NOT_THERE = &H18 ' device doesn't exist
CM_PROB_MOVED = &H19 '
CM_PROB_TOO_EARLY = &H1A '
CM_PROB_NO_VALID_LOG_CONF = &H1B ' no valid log config
CM_PROB_FAILED_INSTALL = &H1C ' install failed
CM_PROB_HARDWARE_DISABLED = &H1D ' device disabled
CM_PROB_CANT_SHARE_IRQ = &H1E ' can't share IRQ
CM_PROB_FAILED_ADD = &H1F ' driver failed add
CM_PROB_DISABLED_SERVICE = &H20 ' service's Start = 4
CM_PROB_TRANSLATION_FAILED = &H21 ' resource translation failed
CM_PROB_NO_SOFTCONFIG = &H22 ' no soft config
CM_PROB_BIOS_TABLE = &H23 ' device missing in BIOS table
CM_PROB_IRQ_TRANSLATION_FAILED = &H24 ' IRQ translator failed
CM_PROB_FAILED_DRIVER_ENTRY = &H25 ' DriverEntry() failed.
CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD = &H26 ' Driver should have unloaded.
CM_PROB_DRIVER_FAILED_LOAD = &H27 ' Driver load unsuccessful.
CM_PROB_DRIVER_SERVICE_KEY_INVALID = &H28 ' Error accessing driver's service key
CM_PROB_LEGACY_SERVICE_NO_DEVICES = &H29 ' Loaded legacy service created no devices
CM_PROB_DUPLICATE_DEVICE = &H2A ' Two devices were discovered with the same name
CM_PROB_FAILED_POST_START = &H2B ' The drivers set the device state to failed
CM_PROB_HALTED = &H2C ' This device was failed post start via usermode
CM_PROB_PHANTOM = &H2D ' The devinst currently exists only in the registry
CM_PROB_SYSTEM_SHUTDOWN = &H2E ' The system is shutting down
CM_PROB_HELD_FOR_EJECT = &H2F ' The device is offline awaiting removal
CM_PROB_DRIVER_BLOCKED = &H30 ' One or more drivers is blocked from loading
CM_PROB_REGISTRY_TOO_LARGE = &H31 ' System hive has grown too large
CM_PROB_SETPROPERTIES_FAILED = &H32 ' Failed to apply one or more registry properties
NUM_CM_PROB = &H33 '
End Enum

'Returns true if the device is enabled
Public Function IsDeviceEnabled(ByVal DevInst As Integer) As Boolean
Dim Result As Integer
Dim Problem, Status As IntPtr
Result = CM_Get_DevNode_Status(Status, Problem, DevInst, 0)
If Result = 13 Then Return Nothing
If Result <> 0 Then Throw New ApplicationException("Return Code From CM_Get_DevNode_Status was not 0")
Return Not Problem.ToInt32 = CM_PROB.CM_PROB_DISABLED
End Function

'Returns true if the Status Flag to hide the device is set.
Public Function IsDeviceHidden(ByVal DevInst As Integer) As Boolean
Dim Result As Integer
Dim Problem, Status As IntPtr
Result = CM_Get_DevNode_Status(Status, Problem, DevInst, 0)
If Result <> 0 Then Throw New ApplicationException("Return Code From CM_Get_DevNode_Status was not 0")
Return (Status.ToInt32 And DN_Flags.DN_NO_SHOW_IN_DM) = DN_Flags.DN_NO_SHOW_IN_DM
End Function

'Gets all the Device Status Flags for a device
Public Function GetDeviceStatus(ByVal DevInst As Integer) As String()
Dim Result As Integer
Dim Problem, Status As IntPtr
Result = CM_Get_DevNode_Status(Status, Problem, DevInst, 0)
If Result = 13 Then Return Nothing
If Result <> 0 Then Throw New ApplicationException("Return Code From CM_Get_DevNode_Status was not 0")
Return GetBitFlags(Status, GetType(DN_Flags))
End Function

'Gets a Device Problem
Public Function GetDeviceProblem(ByVal DevInst As Integer) As String
Dim Result As Integer
Dim Problem, Status As IntPtr
Result = CM_Get_DevNode_Status(Status, Problem, DevInst, 0)
If Result = 13 Then Return Nothing
If Result <> 0 Then Throw New ApplicationException("Return Code From CM_Get_DevNode_Status was not 0")
If Problem.ToInt32 = 0 Then
Return "No Problem"
Else
Return CType(Problem.ToInt32, CM_PROB).ToString
End If
End Function

'Returns an array of strings representing the Status Flags set for a device.
Private Function GetBitFlags(ByVal Bits As IntPtr, ByVal EnumType As Type) As String()
Dim arrData As New ArrayList
For Each TypeName As String In [Enum].GetNames(EnumType)
Dim Number As Int32 = [Enum].Parse(EnumType, TypeName)
If (Bits.ToInt32 And Number) = Number Then
arrData.Add(TypeName)
End If
Next
Dim StrData(arrData.Count) As String
arrData.CopyTo(StrData)
Return StrData
End Function

Here is how you could call this. after you retrieve a device in variable objDevice.

If IsDeviceEnabled(objDevice.DevInfo.DevInst) Then
'Device Enabled
...
Else
'Device Disabled
...
End If

I think that the code should be fairly straightforward to follow.

No comments: