Working With Devices: Enabling/Disabling a device

All the code in previous blog posts leading up to this were really for one purpose. I wanted the ability to enable and disable a device.

We will use two functions to change the state of a device. SetupDiSetClassInstallParams and SetupDiCallClassInstaller

Private Declare Auto Function SetupDiSetClassInstallParams Lib "setupapi.dll" (ByVal DeviceInfoSet As IntPtr, ByVal DeviceInfoData As IntPtr, ByVal InstallParams As IntPtr, ByVal InstallParamsSize As Integer) As Boolean
Private Declare Auto Function SetupDiCallClassInstaller Lib "setupapi.dll" (ByVal InstallFunction As Integer, ByVal DeviceInfoSet As IntPtr, ByVal DeviceInfoData As IntPtr) As Boolean

Private Const DIF_PROPERTYCHANGE As Integer = &H12

Private Const DICS_ENABLE As Integer = 1
Private Const DICS_DISABLE As Integer = 2

Private Const DICS_FLAG_GLOBAL As Integer = 1
Private Const DICS_FLAG_CONFIGSPECIFIC As Integer = 2

'Struct for Enabling/Disabling a device
Private Structure SP_PROPCHANGE_PARAMS
Public ClassInstallHeader As SP_CLASSINSTALL_HEADER
Public StateChange As Integer
Public Scope As Integer
Public HwProfile As Integer
End Structure

'Two actions on a device. Just to make things simple
Public Enum Change_Action
Disable
Enable
End Enum

'Changes the state of a given device.
Public Function ChangeState(ByVal DevInfoSet As IntPtr, ByVal Device As Device, ByVal newState As Change_Action) As Boolean

'We make sure that the device isn't already set to the newState
Dim DeviceEnabled As Boolean = IsDeviceEnabled(Device.DevInfo.DevInst)
If DeviceEnabled And newState = Change_Action.Enable Then Return True
If Not DeviceEnabled And newState = Change_Action.Disable Then Return True

'We first try to set the device Globally. If it fails then we try setting it just for the current Profile
'Device Manager seems to disable devices by hardware profile. That would make since. In our case we try disable the device Globally
'first.
If ChangeDevState(DevInfoSet, Device, newState, DICS_FLAG_GLOBAL) Then
Return True
Else
Return ChangeDevState(DevInfoSet, Device, newState, DICS_FLAG_CONFIGSPECIFIC)
End If
End Function

'Changes a device state
Private Function ChangeDevState(ByVal DevInfoSet As IntPtr, ByVal Device As Device, ByVal newState As Change_Action, ByVal Scope As Integer) As Boolean

'Sets up the Change Structure
Dim Prop_Change As SP_PROPCHANGE_PARAMS = New SP_PROPCHANGE_PARAMS
Prop_Change.ClassInstallHeader.cbSize = Marshal.SizeOf(GetType(SP_CLASSINSTALL_HEADER))
Prop_Change.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE
Prop_Change.StateChange = IIf(newState = Change_Action.Enable, DICS_ENABLE, DICS_DISABLE)
Prop_Change.Scope = Scope
Prop_Change.HwProfile = 0

'We create two pointers. One for the Change Structure and a second for the Device. We set the Structures to their pointer
Dim intProp_ChangeSize As Integer = Marshal.SizeOf(Prop_Change)
Dim intProp_Change As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(Prop_Change.ClassInstallHeader))
Marshal.StructureToPtr(Prop_Change, intProp_Change, True)

Dim intDevInfoSize As Integer = Marshal.SizeOf(Device.DevInfo)
Dim intDevInfo As IntPtr = Marshal.AllocHGlobal(intDevInfoSize)
Marshal.StructureToPtr(Device.DevInfo, intDevInfo, True)


'Sets the Property Change Structure for the Device. True is success
Dim Result As Boolean = SetupDiSetClassInstallParams(DevInfoSet, intDevInfo, intProp_Change, intProp_ChangeSize)
If Result Then
'Attempts to execute the Call. True is success
Result = SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfoSet, intDevInfo)
If Result Then
'Depending on the scope used to enable/disable the device it may not work. So we'll check the device state.
If newState = Change_Action.Enable Then
Return IsDeviceEnabled(Device.DevInfo.DevInst)
Else
Return Not IsDeviceEnabled(Device.DevInfo.DevInst)
End If
Else
Throw New Win32Exception(Marshal.GetLastWin32Error)
Return False
End If
Else
Throw New Win32Exception(Marshal.GetLastWin32Error)
Return False
End If
End Function

To use this, we call the ChangeState function. We call this with the Device Info Set and Device variables along with what we want to do. We can Enable or Disable the device. First thing we do, is check whether or not the device is already set correctly. Next thing we do is attempt to enable or disable the device. The first way we try to do this is Globally. If, that fails then we try on the specific profile.

I did this after working with enabling and disabling devices and noticed that the device manager disables a device for the profile. In my specific case I needed devices disabled globally. If I just enabled or disabled globally, I'd run into problems when a device is disabled for the profile and not globally. Your use may vary, so feel free to choose one or the other, but if you ever come across a device in device manager that shows a disabled icon associated with disable in the context menu (or both enabled) then that is your problem.

The ChangeDevState function actually does the changing of the state. The code with comments will explain it as best I can.

4 comments:

Unknown said...

Thanks good example.

Unknown said...

i am new to VB.net i tried your code which give me some error like:
IsDeviceEnabled not declared
Marshal not declared

i suppose i need to import the setupapi.dll

but i have no clue how to do it.
can you give me some hint,pls?

Rob Haupt said...

I have taken a lot of this stuff and put it up into an example class. Its been a long time since I've played around with it, but I think it should give you a good place to figure things out in.

http://is.gd/t30S

Unknown said...

Thanks, that's certainly helped.