Win32 API function to programmatically enable/disable device

Viewed 77533

I am writing a small C# app to disable a device (my laptop touchpad) whenever another mouse device is detected, and enable the touchpad again if a mouse is not detected. I am not even able to disable the touchpad in device manager (it is running on the default mouse class driver).

I am getting into device driver development so I thought maybe I could write a little filter driver that would just accept IOCTLs to enable and disable passing mouse event messages up the device stack, and get messages from user mode via a raw PDO. However, I asked that question and somebody has suggested that I can do this in usermode via the SetupDi.. functions. That would be really good, because this raw PDO communication method is a PITA to work with.

I have only used SetupDiGetClassDevs before, and there are so many of them, can someone with more experience with this part of the Win32 API just tell me quickly what one I should call to stop/disable a mouse device or its interface or if there is something somewhere in the dark corners of the framework that will do this (maybe in WMI?).

Update (24/9/09) I figured out how to do this with a filter driver and posted how I did it on my original question. I still want to know if it is possible to enable or disable devices directly from Win32 and if so, how - so I will leave this question open.

4 Answers

You can consider the below mentioned Win APIs CM_Request_Device_EjectW function: The CM_Request_Device_Eject function prepares a local device instance for safe removal. If the device is removable. CM_Query_And_Remove_SubTreeW function: The CM_Query_And_Remove_SubTree function checks whether a device instance and its children can be removed and if so, it removes them.

And the same can be found here: CM_Request_Device_EjectW and CM_Query_And_Remove_SubTreeW

In case you wanted to toggle the device enabled status, you can adapt @JustinGrant's amazing solution by replacing SetDeviceEnabled with the following:

public static void ToggleDeviceEnabled(Guid classGuid, string instanceId)
{
    SafeDeviceInfoSetHandle diSetHandle = null;
    try
    {
        (diSetHandle, var did) = GetDeviceInfo(classGuid, instanceId);
        // according to https://stackoverflow.com/a/13105326/308451
        // the answer might lie in CM_Get_DevNode_Status. Let's try.

        int result = NativeMethods.CM_Get_DevNode_Status(out uint devNodeStatus, out uint probNum, did.DevInst);
        // return value meanings are in Cfgmgr32.h., but CR_SUCCESS is one of them
        const int CR_SUCCESS = 0;
        if (result != CR_SUCCESS)
            throw new Win32Exception();

        // devNodeStatus now contains status bit flags: any combination of the DN_- prefixed bit flags defined in Cfg.h.
        // one of the flags is
        const uint DN_STARTED = 0x00000008; // Is currently configured

        bool currentlyEnabled = HasFlag(devNodeStatus, DN_STARTED);
        bool enable = !currentlyEnabled;

        // toggle
        EnableDevice(diSetHandle, did, enable);
    }
    finally
    {
        if (diSetHandle != null)
        {
            if (diSetHandle.IsClosed == false)
            {
                diSetHandle.Close();
            }
            diSetHandle.Dispose();
        }
    }
}

static bool HasFlag(uint flags, uint flag)
{
    return (flags & flag) == flag;
}
private static (SafeDeviceInfoSetHandle, DeviceInfoData) GetDeviceInfo(Guid classGuid, string instanceId)
{
    // Get the handle to a device information set for all devices matching classGuid that are present on the system.
    var 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);

    return (diSetHandle, diData[index]);
}
[DllImport("cfgmgr32.dll", SetLastError = true)]
public static extern int CM_Get_DevNode_Status(out uint status, out uint probNum, int devInst, int flags = 0);

Related