How do I disable a system device programmatically?

23,037

Solution 1

Looks like there were two things in drf's version that were giving you trouble. Your x64 problem was caused by SP_DEVINFO_DATA having a uint where an IntPtr was needed. The second is that the SetupDiGetDevicePropertyW function is Vista+ and won't run on XP.

Here's a version with those two issues corrected. I tested as x86 and AnyCPU on XPx86 and Win7X64.

public static class DisableHardware
{
    const uint DIF_PROPERTYCHANGE = 0x12;
    const uint DICS_ENABLE = 1;
    const uint DICS_DISABLE = 2;  // disable device
    const uint DICS_FLAG_GLOBAL = 1; // not profile-specific
    const uint DIGCF_ALLCLASSES = 4;
    const uint DIGCF_PRESENT = 2;
    const uint ERROR_INVALID_DATA = 13;
    const uint ERROR_NO_MORE_ITEMS = 259;
    const uint ERROR_ELEMENT_NOT_FOUND = 1168;

    static DEVPROPKEY DEVPKEY_Device_DeviceDesc;
    static DEVPROPKEY DEVPKEY_Device_HardwareIds;

    [StructLayout(LayoutKind.Sequential)]
    struct SP_CLASSINSTALL_HEADER
    {
        public UInt32 cbSize;
        public UInt32 InstallFunction;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct SP_PROPCHANGE_PARAMS
    {
        public SP_CLASSINSTALL_HEADER ClassInstallHeader;
        public UInt32 StateChange;
        public UInt32 Scope;
        public UInt32 HwProfile;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct SP_DEVINFO_DATA
    {
        public UInt32 cbSize;
        public Guid classGuid;
        public UInt32 devInst;
        public IntPtr reserved;     // CHANGE #1 - was UInt32
    }

    [StructLayout(LayoutKind.Sequential)]
    struct DEVPROPKEY
    {
        public Guid fmtid;
        public UInt32 pid;
    }

    [DllImport("setupapi.dll", SetLastError = true)]
    static extern IntPtr SetupDiGetClassDevsW(
        [In] ref Guid ClassGuid,
        [MarshalAs(UnmanagedType.LPWStr)]
string Enumerator,
        IntPtr parent,
        UInt32 flags);

    [DllImport("setupapi.dll", SetLastError = true)]
    static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);

    [DllImport("setupapi.dll", SetLastError = true)]
    static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,
        UInt32 memberIndex,
        [Out] out SP_DEVINFO_DATA deviceInfoData);

    [DllImport("setupapi.dll", SetLastError = true)]
    static extern bool SetupDiSetClassInstallParams(
        IntPtr deviceInfoSet,
        [In] ref SP_DEVINFO_DATA deviceInfoData,
        [In] ref SP_PROPCHANGE_PARAMS classInstallParams,
        UInt32 ClassInstallParamsSize);

    [DllImport("setupapi.dll", SetLastError = true)]
    static extern bool SetupDiChangeState(
        IntPtr deviceInfoSet,
        [In] ref SP_DEVINFO_DATA deviceInfoData);

    [DllImport("setupapi.dll", SetLastError = true)]
    static extern bool SetupDiGetDevicePropertyW(
            IntPtr deviceInfoSet,
            [In] ref SP_DEVINFO_DATA DeviceInfoData,
            [In] ref DEVPROPKEY propertyKey,
            [Out] out UInt32 propertyType,
            IntPtr propertyBuffer,
            UInt32 propertyBufferSize,
            out UInt32 requiredSize,
            UInt32 flags);

    [DllImport("setupapi.dll", SetLastError = true)]
    static extern bool SetupDiGetDeviceRegistryPropertyW(
      IntPtr DeviceInfoSet,
      [In] ref SP_DEVINFO_DATA  DeviceInfoData,
      UInt32 Property,
      [Out] out UInt32  PropertyRegDataType,
      IntPtr PropertyBuffer,
      UInt32 PropertyBufferSize,
      [In,Out] ref UInt32 RequiredSize
    );

    static DisableHardware()
    {
        DisableHardware.DEVPKEY_Device_DeviceDesc = new DEVPROPKEY();
        DEVPKEY_Device_DeviceDesc.fmtid = new Guid(
                0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
                0xd1, 0x46, 0xa8, 0x50, 0xe0);
        DEVPKEY_Device_DeviceDesc.pid = 2;

        DEVPKEY_Device_HardwareIds = new DEVPROPKEY();
        DEVPKEY_Device_HardwareIds.fmtid = new Guid(
            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
            0xd1, 0x46, 0xa8, 0x50, 0xe0);
        DEVPKEY_Device_HardwareIds.pid = 3;
    }




    public static void DisableDevice(Func<string, bool> filter, bool disable = true)
    {
        IntPtr info = IntPtr.Zero;
        Guid NullGuid = Guid.Empty;
        try
        {
            info = SetupDiGetClassDevsW(
                ref NullGuid,
                null,
                IntPtr.Zero,
                DIGCF_ALLCLASSES);
            CheckError("SetupDiGetClassDevs");

            SP_DEVINFO_DATA devdata = new SP_DEVINFO_DATA();
            devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);

            // Get first device matching device criterion.
            for (uint i = 0; ; i++)
            {
                SetupDiEnumDeviceInfo(info,
                    i,
                    out devdata);
                // if no items match filter, throw
                if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
                    CheckError("No device found matching filter.", 0xcffff);
                CheckError("SetupDiEnumDeviceInfo");

                string devicepath = GetStringPropertyForDevice(info,
                                           devdata, 1); // SPDRP_HARDWAREID

                // Uncomment to print name/path
                //Console.WriteLine(GetStringPropertyForDevice(info,
                //                         devdata, DEVPKEY_Device_DeviceDesc));
                //Console.WriteLine("   {0}", devicepath);
                if (devicepath != null && filter(devicepath)) break;

            }

            SP_CLASSINSTALL_HEADER header = new SP_CLASSINSTALL_HEADER();
            header.cbSize = (UInt32)Marshal.SizeOf(header);
            header.InstallFunction = DIF_PROPERTYCHANGE;

            SP_PROPCHANGE_PARAMS propchangeparams = new SP_PROPCHANGE_PARAMS();
            propchangeparams.ClassInstallHeader = header;
            propchangeparams.StateChange = disable ? DICS_DISABLE : DICS_ENABLE;
            propchangeparams.Scope = DICS_FLAG_GLOBAL;
            propchangeparams.HwProfile = 0;

            SetupDiSetClassInstallParams(info,
                ref devdata,
                ref propchangeparams,
                (UInt32)Marshal.SizeOf(propchangeparams));
            CheckError("SetupDiSetClassInstallParams");

            SetupDiChangeState(
                info,
                ref devdata);
            CheckError("SetupDiChangeState");
        }
        finally
        {
            if (info != IntPtr.Zero)
                SetupDiDestroyDeviceInfoList(info);
        }
    }
    private static void CheckError(string message, int lasterror = -1)
    {

        int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
        if (code != 0)
            throw new ApplicationException(
                String.Format("Error disabling hardware device (Code {0}): {1}",
                    code, message));
    }

    private static string GetStringPropertyForDevice(IntPtr info, SP_DEVINFO_DATA devdata,
        uint propId)
    {
        uint proptype, outsize;
        IntPtr buffer = IntPtr.Zero;
        try
        {
            uint buflen = 512;
            buffer = Marshal.AllocHGlobal((int)buflen);
            outsize=0;
            // CHANGE #2 - Use this instead of SetupDiGetDeviceProperty 
            SetupDiGetDeviceRegistryPropertyW(
                info,
                ref devdata,
                propId,
                out proptype,
                buffer,
                buflen,
                ref outsize);
            byte[] lbuffer = new byte[outsize];
            Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
            int errcode = Marshal.GetLastWin32Error();
            if (errcode == ERROR_INVALID_DATA) return null;
            CheckError("SetupDiGetDeviceProperty", errcode);
            return Encoding.Unicode.GetString(lbuffer);
        }
        finally
        {
            if (buffer != IntPtr.Zero)
                Marshal.FreeHGlobal(buffer);
        }
    }

}

Solution 2

It can be done using P/Invoke methods on the Windows Setup API. The API functions have been in Windows since W2K. The API calls may require administrative rights. Below is some rough code for using these APIs to disable (as well as enable) devices in C#, which allow invoking:

  DisableDevice(
        n => n.ToUpperInvariant().Contains("VID_3F20&PID_8A1D"),
        true); // true disables the device, false enables it

The first parameter is a filter that is passed the hardware ID. This disables or enables the first device matching the provided filter. There is no effect if the device is already in the target state.

The code follows.

public static class DisableHardware {
const uint DIF_PROPERTYCHANGE = 0x12;
const uint DICS_ENABLE = 1;
const uint DICS_DISABLE = 2;  // disable device
const uint DICS_FLAG_GLOBAL = 1; // not profile-specific
const uint DIGCF_ALLCLASSES = 4;
const uint DIGCF_PRESENT = 2;
const uint ERROR_NO_MORE_ITEMS = 259;
const uint ERROR_ELEMENT_NOT_FOUND = 1168;

static DEVPROPKEY DEVPKEY_Device_DeviceDesc;
static DEVPROPKEY DEVPKEY_Device_HardwareIds;

[StructLayout(LayoutKind.Sequential)]
struct SP_CLASSINSTALL_HEADER
{
    public UInt32 cbSize;
    public UInt32 InstallFunction;
}

[StructLayout(LayoutKind.Sequential)]
struct SP_PROPCHANGE_PARAMS
{
    public SP_CLASSINSTALL_HEADER ClassInstallHeader;
    public UInt32 StateChange;
    public UInt32 Scope;
    public UInt32 HwProfile;
}

[StructLayout(LayoutKind.Sequential)]
struct SP_DEVINFO_DATA
{
    public UInt32 cbSize;
    public Guid classGuid;
    public UInt32 devInst;
    public UInt32 reserved;
}

[StructLayout(LayoutKind.Sequential)]
struct DEVPROPKEY
{
    public Guid fmtid;
    public UInt32 pid;
}

[DllImport("setupapi.dll", SetLastError = true)]
static extern IntPtr SetupDiGetClassDevsW(
    [In] ref Guid ClassGuid,
    [MarshalAs(UnmanagedType.LPWStr)]
    string Enumerator,
    IntPtr parent,
    UInt32 flags);

[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);

[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,
    UInt32 memberIndex,
    [Out] out SP_DEVINFO_DATA deviceInfoData);

[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiSetClassInstallParams(
    IntPtr deviceInfoSet,
    [In] ref SP_DEVINFO_DATA deviceInfoData,
    [In] ref SP_PROPCHANGE_PARAMS classInstallParams,
    UInt32 ClassInstallParamsSize);

[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiChangeState(
    IntPtr deviceInfoSet,
    [In] ref SP_DEVINFO_DATA deviceInfoData);

[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiGetDevicePropertyW(
        IntPtr deviceInfoSet,
        [In] ref SP_DEVINFO_DATA DeviceInfoData,
        [In] ref DEVPROPKEY propertyKey,
        [Out] out UInt32 propertyType,
        IntPtr propertyBuffer,
        UInt32 propertyBufferSize,
        out UInt32 requiredSize,
        UInt32 flags);

static DisableHardware()
{
    DisableHardware.DEVPKEY_Device_DeviceDesc = new DEVPROPKEY();
    DEVPKEY_Device_DeviceDesc.fmtid = new Guid(
            0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
            0xd1, 0x46, 0xa8, 0x50, 0xe0);
    DEVPKEY_Device_DeviceDesc.pid = 2;

    DEVPKEY_Device_HardwareIds = new DEVPROPKEY();
    DEVPKEY_Device_HardwareIds.fmtid = new Guid(
        0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
        0xd1, 0x46, 0xa8, 0x50, 0xe0);
    DEVPKEY_Device_HardwareIds.pid = 3;
}




public static void DisableDevice(Func<string, bool> filter, bool disable = true)
{
    IntPtr info = IntPtr.Zero;
    Guid NullGuid = Guid.Empty;
    try
    {
        info = SetupDiGetClassDevsW(
            ref NullGuid,
            null,
            IntPtr.Zero,
            DIGCF_ALLCLASSES);
        CheckError("SetupDiGetClassDevs");

        SP_DEVINFO_DATA devdata = new SP_DEVINFO_DATA();
        devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);

        // Get first device matching device criterion.
        for (uint i = 0; ; i++)
        {
            SetupDiEnumDeviceInfo(info,
                i,
                out devdata);
            // if no items match filter, throw
            if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
                CheckError("No device found matching filter.", 0xcffff);
            CheckError("SetupDiEnumDeviceInfo");

            string devicepath = GetStringPropertyForDevice(info,
                                       devdata, DEVPKEY_Device_HardwareIds);
            // Uncomment to print name/path
            //Console.WriteLine(GetStringPropertyForDevice(info,
            //                         devdata, DEVPKEY_Device_DeviceDesc));
            //Console.WriteLine("   {0}", devicepath);
            if (devicepath != null && filter(devicepath)) break;

        }

        SP_CLASSINSTALL_HEADER header = new SP_CLASSINSTALL_HEADER();
        header.cbSize = (UInt32)Marshal.SizeOf(header);
        header.InstallFunction = DIF_PROPERTYCHANGE;

        SP_PROPCHANGE_PARAMS propchangeparams = new SP_PROPCHANGE_PARAMS();
        propchangeparams.ClassInstallHeader = header;
        propchangeparams.StateChange = disable ? DICS_DISABLE : DICS_ENABLE;
        propchangeparams.Scope = DICS_FLAG_GLOBAL;
        propchangeparams.HwProfile = 0;

        SetupDiSetClassInstallParams(info,
            ref devdata,
            ref propchangeparams,
            (UInt32)Marshal.SizeOf(propchangeparams));
        CheckError("SetupDiSetClassInstallParams");

        SetupDiChangeState(
            info,
            ref devdata);
        CheckError("SetupDiChangeState");
    }
    finally
    {
        if (info != IntPtr.Zero)
            SetupDiDestroyDeviceInfoList(info);
    }
}
private static void CheckError(string message, int lasterror = -1)
{

    int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
    if (code != 0)
        throw new ApplicationException(
            String.Format("Error disabling hardware device (Code {0}): {1}",
                code, message));
}

private static string GetStringPropertyForDevice(IntPtr info, SP_DEVINFO_DATA devdata,
    DEVPROPKEY key)
{
    uint proptype, outsize;
    IntPtr buffer = IntPtr.Zero;
    try
    {
        uint buflen = 512;
        buffer = Marshal.AllocHGlobal((int)buflen);
        SetupDiGetDevicePropertyW(
            info,
            ref devdata,
            ref key,
            out proptype,
            buffer,
            buflen,
            out outsize,
            0);
        byte[] lbuffer = new byte[outsize];
        Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
        int errcode = Marshal.GetLastWin32Error();
        if (errcode == ERROR_ELEMENT_NOT_FOUND) return null;
        CheckError("SetupDiGetDeviceProperty", errcode);
        return Encoding.Unicode.GetString(lbuffer);
    }
    finally
    {
        if (buffer != IntPtr.Zero)
            Marshal.FreeHGlobal(buffer);
    }
}
}

Solution 3

I've built upon the XP-compatible answer and improved it a bit by adding a SafeDeviceInformationSetHandle class and performing some general code cleanup/refactoring, and am adding it here for posterity:

using System;
using System.Text;
using System.Security;
using System.ComponentModel;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;

public static class SetupApi
{
    private const string SETUPAPI = "setupapi.dll";
    private const int ERROR_INVALID_DATA = 13;
    private const int ERROR_INSUFFICIENT_BUFFER = 122;

    private class SafeDeviceInformationSetHandle : SafeHandleMinusOneIsInvalid
    {
        private SafeDeviceInformationSetHandle() : base(true)
        { }

        private SafeDeviceInformationSetHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle)
        {
            SetHandle(preexistingHandle);
        }

        [SecurityCritical]
        protected override bool ReleaseHandle()
        {
            return SetupDiDestroyDeviceInfoList(handle);
        }
    }

    #region Enumerations

    [Flags]
    private enum DIGCF : uint
    {
        DEFAULT         = 0x00000001,
        PRESENT         = 0x00000002,
        ALLCLASSES      = 0x00000004,
        PROFILE         = 0x00000008,
        DEVICEINTERFACE = 0x00000010
    }

    private enum SPDRP : uint
    {
        /// <summary>
        /// DeviceDesc (R/W)
        /// </summary>
        DEVICEDESC = 0x00000000,

        /// <summary>
        /// HardwareID (R/W)
        /// </summary>
        HARDWAREID = 0x00000001,

        /// <summary>
        /// CompatibleIDs (R/W)
        /// </summary>
        COMPATIBLEIDS = 0x00000002,

        /// <summary>
        /// unused
        /// </summary>
        UNUSED0 = 0x00000003,

        /// <summary>
        /// Service (R/W)
        /// </summary>
        SERVICE = 0x00000004,

        /// <summary>
        /// unused
        /// </summary>
        UNUSED1 = 0x00000005,

        /// <summary>
        /// unused
        /// </summary>
        UNUSED2 = 0x00000006,

        /// <summary>
        /// Class (R--tied to ClassGUID)
        /// </summary>
        CLASS = 0x00000007,

        /// <summary>
        /// ClassGUID (R/W)
        /// </summary>
        CLASSGUID = 0x00000008,

        /// <summary>
        /// Driver (R/W)
        /// </summary>
        DRIVER = 0x00000009,

        /// <summary>
        /// ConfigFlags (R/W)
        /// </summary>
        CONFIGFLAGS = 0x0000000A,

        /// <summary>
        /// Mfg (R/W)
        /// </summary>
        MFG = 0x0000000B,

        /// <summary>
        /// FriendlyName (R/W)
        /// </summary>
        FRIENDLYNAME = 0x0000000C,

        /// <summary>
        /// LocationInformation (R/W)
        /// </summary>
        LOCATION_INFORMATION = 0x0000000D,

        /// <summary>
        /// PhysicalDeviceObjectName (R)
        /// </summary>
        PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E,

        /// <summary>
        /// Capabilities (R)
        /// </summary>
        CAPABILITIES = 0x0000000F,

        /// <summary>
        /// UiNumber (R)
        /// </summary>
        UI_NUMBER = 0x00000010,

        /// <summary>
        /// UpperFilters (R/W)
        /// </summary>
        UPPERFILTERS = 0x00000011,

        /// <summary>
        /// LowerFilters (R/W)
        /// </summary>
        LOWERFILTERS = 0x00000012,

        /// <summary>
        /// BusTypeGUID (R)
        /// </summary>
        BUSTYPEGUID = 0x00000013,

        /// <summary>
        /// LegacyBusType (R)
        /// </summary>
        LEGACYBUSTYPE = 0x00000014,

        /// <summary>
        /// BusNumber (R)
        /// </summary>
        BUSNUMBER = 0x00000015,

        /// <summary>
        /// Enumerator Name (R)
        /// </summary>
        ENUMERATOR_NAME = 0x00000016,

        /// <summary>
        /// Security (R/W, binary form)
        /// </summary>
        SECURITY = 0x00000017,

        /// <summary>
        /// Security (W, SDS form)
        /// </summary>
        SECURITY_SDS = 0x00000018,

        /// <summary>
        /// Device Type (R/W)
        /// </summary>
        DEVTYPE = 0x00000019,

        /// <summary>
        /// Device is exclusive-access (R/W)
        /// </summary>
        EXCLUSIVE = 0x0000001A,

        /// <summary>
        /// Device Characteristics (R/W)
        /// </summary>
        CHARACTERISTICS = 0x0000001B,

        /// <summary>
        /// Device Address (R)
        /// </summary>
        ADDRESS = 0x0000001C,

        /// <summary>
        /// UiNumberDescFormat (R/W)
        /// </summary>
        UI_NUMBER_DESC_FORMAT = 0X0000001D,

        /// <summary>
        /// Device Power Data (R)
        /// </summary>
        DEVICE_POWER_DATA = 0x0000001E,

        /// <summary>
        /// Removal Policy (R)
        /// </summary>
        REMOVAL_POLICY = 0x0000001F,

        /// <summary>
        /// Hardware Removal Policy (R)
        /// </summary>
        REMOVAL_POLICY_HW_DEFAULT = 0x00000020,

        /// <summary>
        /// Removal Policy Override (RW)
        /// </summary>
        REMOVAL_POLICY_OVERRIDE = 0x00000021,

        /// <summary>
        /// Device Install State (R)
        /// </summary>
        INSTALL_STATE = 0x00000022,

        /// <summary>
        /// Device Location Paths (R)
        /// </summary>
        LOCATION_PATHS = 0x00000023,
    }

    private enum DIF : uint
    {
        SELECTDEVICE                   = 0x00000001,
        INSTALLDEVICE                  = 0x00000002,
        ASSIGNRESOURCES                = 0x00000003,
        PROPERTIES                     = 0x00000004,
        REMOVE                         = 0x00000005,
        FIRSTTIMESETUP                 = 0x00000006,
        FOUNDDEVICE                    = 0x00000007,
        SELECTCLASSDRIVERS             = 0x00000008,
        VALIDATECLASSDRIVERS           = 0x00000009,
        INSTALLCLASSDRIVERS            = 0x0000000A,
        CALCDISKSPACE                  = 0x0000000B,
        DESTROYPRIVATEDATA             = 0x0000000C,
        VALIDATEDRIVER                 = 0x0000000D,
        DETECT                         = 0x0000000F,
        INSTALLWIZARD                  = 0x00000010,
        DESTROYWIZARDDATA              = 0x00000011,
        PROPERTYCHANGE                 = 0x00000012,
        ENABLECLASS                    = 0x00000013,
        DETECTVERIFY                   = 0x00000014,
        INSTALLDEVICEFILES             = 0x00000015,
        UNREMOVE                       = 0x00000016,
        SELECTBESTCOMPATDRV            = 0x00000017,
        ALLOW_INSTALL                  = 0x00000018,
        REGISTERDEVICE                 = 0x00000019,
        NEWDEVICEWIZARD_PRESELECT      = 0x0000001A,
        NEWDEVICEWIZARD_SELECT         = 0x0000001B,
        NEWDEVICEWIZARD_PREANALYZE     = 0x0000001C,
        NEWDEVICEWIZARD_POSTANALYZE    = 0x0000001D,
        NEWDEVICEWIZARD_FINISHINSTALL  = 0x0000001E,
        UNUSED1                        = 0x0000001F,
        INSTALLINTERFACES              = 0x00000020,
        DETECTCANCEL                   = 0x00000021,
        REGISTER_COINSTALLERS          = 0x00000022,
        ADDPROPERTYPAGE_ADVANCED       = 0x00000023,
        ADDPROPERTYPAGE_BASIC          = 0x00000024,
        RESERVED1                      = 0x00000025,
        TROUBLESHOOTER                 = 0x00000026,
        POWERMESSAGEWAKE               = 0x00000027,
        ADDREMOTEPROPERTYPAGE_ADVANCED = 0x00000028,
        UPDATEDRIVER_UI                = 0x00000029,
        FINISHINSTALL_ACTION           = 0x0000002A,
        RESERVED2                      = 0x00000030,
    }

    private enum DICS : uint
    {
        ENABLE     = 0x00000001,
        DISABLE    = 0x00000002,
        PROPCHANGE = 0x00000003,
        START      = 0x00000004,
        STOP       = 0x00000005,
    }

    [Flags]
    private enum DICS_FLAG : uint
    {
        GLOBAL          = 0x00000001,
        CONFIGSPECIFIC  = 0x00000002,
        CONFIGGENERAL   = 0x00000004,
    }

    #endregion

    #region Structures

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVINFO_DATA
    {
        public UInt32 cbSize;
        public Guid ClassGuid;
        public UInt32 DevInst;
        public IntPtr Reserved;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_CLASSINSTALL_HEADER
    {
        public UInt32 cbSize;
        public DIF InstallFunction;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_PROPCHANGE_PARAMS
    {
        public SP_CLASSINSTALL_HEADER ClassInstallHeader;
        public DICS StateChange;
        public DICS_FLAG Scope;
        public UInt32 HwProfile;
    }

    #endregion

    #region P/Invoke Functions

    [DllImport(SETUPAPI, SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern SafeDeviceInformationSetHandle SetupDiGetClassDevs(
        [In] ref Guid ClassGuid,
        [In] string Enumerator,
        IntPtr hwndParent,
        DIGCF Flags
    );

    [DllImport(SETUPAPI, SetLastError = true)]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

    [DllImport(SETUPAPI, SetLastError = true)]
    private static extern bool SetupDiEnumDeviceInfo(
        SafeDeviceInformationSetHandle DeviceInfoSet,
        UInt32 MemberIndex,
        ref SP_DEVINFO_DATA DeviceInfoData
    );

    [DllImport(SETUPAPI, SetLastError = true)]
    private static extern bool SetupDiSetClassInstallParams(
        SafeDeviceInformationSetHandle DeviceInfoSet,
        [In] ref SP_DEVINFO_DATA deviceInfoData,
        [In] ref SP_PROPCHANGE_PARAMS classInstallParams,
        UInt32 ClassInstallParamsSize
    );

    [DllImport(SETUPAPI, SetLastError = true)]
    private static extern bool SetupDiChangeState(
        SafeDeviceInformationSetHandle DeviceInfoSet,
        [In, Out] ref SP_DEVINFO_DATA DeviceInfoData
    );

    [DllImport(SETUPAPI, SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern bool SetupDiGetDeviceRegistryProperty(
        SafeDeviceInformationSetHandle DeviceInfoSet,
        [In] ref SP_DEVINFO_DATA DeviceInfoData,
        SPDRP Property,
        out RegistryValueKind PropertyRegDataType,
        [Out] byte[] PropertyBuffer,
        UInt32 PropertyBufferSize,
        out UInt32 RequiredSize
    );

    #endregion

    private static void CheckWin32CallSuccess(bool success)
    {
        if (!success)
        {
            throw new Win32Exception();
        }
    }

    private static string GetStringPropertyForDevice(SafeDeviceInformationSetHandle infoSet, ref SP_DEVINFO_DATA devInfo, SPDRP property)
    {
        RegistryValueKind regType;
        UInt32 requiredSize;

        if (!SetupDiGetDeviceRegistryProperty(infoSet, ref devInfo, property, out regType, null, 0, out requiredSize))
        {
            switch (Marshal.GetLastWin32Error())
            {
                case ERROR_INSUFFICIENT_BUFFER:
                    break;
                case ERROR_INVALID_DATA:
                    return string.Empty;
                default:
                    throw new Win32Exception();
            }
        }

        byte[] propertyBuffer = new byte[requiredSize];
        CheckWin32CallSuccess(SetupDiGetDeviceRegistryProperty(infoSet, ref devInfo, property, out regType, propertyBuffer, (uint)propertyBuffer.Length, out requiredSize));

        return Encoding.Unicode.GetString(propertyBuffer);
    }

    public static void EnableDevice(Func<string, bool> hardwareIdFilter, bool enable)
    {
        Guid nullGuid = Guid.Empty;
        using (SafeDeviceInformationSetHandle infoSet = SetupDiGetClassDevs(ref nullGuid, null, IntPtr.Zero, DIGCF.ALLCLASSES))
        {
            CheckWin32CallSuccess(!infoSet.IsInvalid);

            SP_DEVINFO_DATA devInfo = new SP_DEVINFO_DATA();
            devInfo.cbSize = (UInt32) Marshal.SizeOf(devInfo);

            for (uint index = 0; ; ++index)
            {
                CheckWin32CallSuccess(SetupDiEnumDeviceInfo(infoSet, index, ref devInfo));

                string hardwareId = GetStringPropertyForDevice(infoSet, ref devInfo, SPDRP.HARDWAREID);

                if ((!string.IsNullOrEmpty(hardwareId)) && (hardwareIdFilter(hardwareId)))
                {
                    break;
                }
            }

            SP_CLASSINSTALL_HEADER classinstallHeader = new SP_CLASSINSTALL_HEADER();
            classinstallHeader.cbSize = (UInt32) Marshal.SizeOf(classinstallHeader);
            classinstallHeader.InstallFunction = DIF.PROPERTYCHANGE;

            SP_PROPCHANGE_PARAMS propchangeParams = new SP_PROPCHANGE_PARAMS
                                                        {
                                                            ClassInstallHeader = classinstallHeader,
                                                            StateChange = enable ? DICS.ENABLE : DICS.DISABLE,
                                                            Scope = DICS_FLAG.GLOBAL,
                                                            HwProfile = 0,
                                                        };

            CheckWin32CallSuccess(SetupDiSetClassInstallParams(infoSet, ref devInfo, ref propchangeParams, (UInt32)Marshal.SizeOf(propchangeParams)));

            CheckWin32CallSuccess(SetupDiChangeState(infoSet, ref devInfo));
        }
    }
}

Solution 4

I managed to solve this Problem using DevManView.exe (freeware):

  1. Download DevManView.exe and put the .exe file somewhere: http://www.nirsoft.net/utils/device_manager_view.html

  2. Go to your Device Manager and search for the Name of the device you want to enable/disable.

  3. In C#, create and start a new process for disabling the device (using the name of the device you found in the device manager).

    Process devManViewProc = new Process();
    devManViewProc.StartInfo.FileName = @"<path to DevManView.exe>\DevManView.exe";
    devManViewProc.StartInfo.Arguments = "/disable \"<name of the device>\"";
    devManViewProc.Start();
    devManViewProc.WaitForExit();
    
  4. And enable it again.

    devManViewProc.StartInfo.Arguments = "/enable \"<name of the device>\"";
    devManViewProc.Start();
    devManViewProc.WaitForExit();
    
Share:
23,037
wbd
Author by

wbd

Updated on March 01, 2021

Comments

  • wbd
    wbd over 2 years

    I am looking for way to disable a system device (either USB or internal) in C#.NET given either the PID & VID or the device name.

    After searching I found Hardware Helper Library for C# on CodeProject.

    But I need something that will work on XP, Vista & Windows 7 (both x86 and x64 operating systems)...the project I linked only works with XP and Vista x86...even when running the application with administrator privileges.

    Does anyone know of a solution that would work on all operating systems?

  • wbd
    wbd over 12 years
    Whilst this does work, I can't compile for Any CPU and must compile for x86. It does require Admin Privileges. Thanks, if there are no answers that get around the Any CPU thing I will give you the bounty.
  • Chibueze Opata
    Chibueze Opata over 10 years
    Hi drf, please I'm trying to use this code in .NET 2.0, can you help me translate the 'Func' function, I'm assuming it means devicepath.Contains(vid)? Is this correct? Thanks
  • drf
    drf over 10 years
    @Chibueze The System.Func<string, bool> parameter in DisableDevice is a predicate where the first device enumerated with a name matching the predicate is disabled. If you know the device name when the method is called, the filter parameter type can be refactored with a string (e.g., the function signature becomes void DisableDevice(Func<string, bool> filter, bool disable=true)), and the segment filter(devicepath) replaced with devicepath.ToUpperInvariant() == filter.ToUpperInvariant(). In .NET 2, you could also use a delegate (string) in place of a lambda function for the predicate.
  • Malaise
    Malaise almost 9 years
    Does SetupDiGetDeviceProperty just not work from .NET? I always get error 87, though in C++ doing the same thing, with the same parameters works fine. I thought maybe a DLLImport declaration problem, as one odd difference I saw was the order parameters were pushed onto the stack was different in .NET than C++. But I had the exact same declaration for the import as this code here (and tried several tweaks).
  • hynsey
    hynsey over 8 years
    This works great for me with one exception. When I disable a device in Device Manager, the device is still visible in the list to enable. When I disable a device with this code, the device disappears from Device Manager. So I need to store the Hardware Id to re-enable it. So is performing something different to what Windows does when you use Device Manager?
  • Kiran Hegde
    Kiran Hegde about 4 years
    This answer is almost right except in the function GetStringPropertyForDevice. If the code is used as is, you might experience the program terminating due to a crash. The problem is that, the function uses a fixed buffer length of 512, which a lot of times is not enough to hold the data. What needs to be done is to first get the size of buffer as follows: if(!SetupDiGetDeviceRegistryPropertyW(info, ref devdata, propId, out proptype, IntPtr.Zero, 0, ref outsize)). Once the size is got, allocate the buffer and call SetupDiGetDeviceRegistryPropertyW again
  • Monsignor
    Monsignor about 3 years
    Copy-pasted drf's answer.