Recently I came across the following problem: how to re-install some device in MS Windows in the easiest possible way (assuming a target user has no knowledge of the system, whatsoever, maybe with exception of an Internet browser). The solution is to write a simple app, which does everything automatically. The idea was quite simple: uninstall the device and then rescan all hardware for changes, letting the PnP mechanism do the rest. Language: C# and SetupAPI (Windows Driver Kit) hidden behind fancy Windows Forms GUI. Here's the code.
Declarations:using System.Runtime.InteropServices; // ... public const int CR_SUCCESS = 0x00000000; public const int CM_LOCATE_DEVNODE_NORMAL = 0x00000000; public const int DIGCF_PRESENT = 0x00000002; public const int DIF_REMOVE = 0x00000005; // devnode info struct [StructLayout(LayoutKind.Sequential)] public class SP_DEVINFO_DATA { public int cbSize; public Guid ClassGuid; public int DevInst; public ulong Reserved; } // importing external SetupAPI methods [DllImport("cfgmgr32.dll")] public static extern UInt32 CM_Locate_DevNode(ref UInt32 DevInst, string pDeviceID, UInt32 Flags); [DllImport("cfgmgr32.dll", SetLastError = true)] public static extern UInt32 CM_Reenumerate_DevNode(UInt32 DevInst, UInt32 Flags); [DllImport("setupapi.dll")] public static extern Boolean SetupDiClassGuidsFromNameA(string ClassName, ref Guid Guids, UInt32 ClassNameSize, ref UInt32 RequiredSize); [DllImport("setupapi.dll")] public static extern IntPtr SetupDiGetClassDevsA(ref Guid ClassGuid, UInt32 Enumerator, IntPtr hwndPtr, UInt32 Flags); [DllImport("setupapi.dll")] public static extern Boolean SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, UInt32 DeviceIndex, SP_DEVINFO_DATA DeviceInfoData); [DllImport("setupapi.dll", SetLastError = true)] public static extern Boolean SetupDiCallClassInstaller(UInt32 InstallFunction, IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData); [DllImport("setupapi.dll")] public static extern Boolean SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);Uninstalling all devices in class:
private Int32 RemoveAllDevicesInClassName(string ClassName) { IntPtr NewDeviceInfoSet; UInt32 RequiredSize = 0; SP_DEVINFO_DATA DeviceInfoData = new SP_DEVINFO_DATA(); Guid[] Guids = new Guid[1]; // getting required size to store Guids for given class name Boolean result = SetupDiClassGuidsFromNameA( ClassName, ref Guids[0], RequiredSize, ref RequiredSize); if (RequiredSize == 0) { // incorrect class name return -4; } if (!result) { Guids = new Guid[RequiredSize]; // getting the actual Guids result = SetupDiClassGuidsFromNameA( ClassName, ref Guids[0], RequiredSize, ref RequiredSize); } if (!result) { // incorrect class name return -4; } // getting all present devnodes for given Guid NewDeviceInfoSet = SetupDiGetClassDevsA(ref Guids[0], 0, IntPtr.Zero, DIGCF_PRESENT); if (NewDeviceInfoSet.ToInt32() == -1) { // unavailable return -8; } // preparing device info struct DeviceInfoData.cbSize = 28; DeviceInfoData.ClassGuid = Guid.Empty; DeviceInfoData.DevInst = 0; DeviceInfoData.Reserved = 0; UInt32 i; // for each device in class (set of devnodes pointed by NewDeviceInfoSet) for (i = 0; SetupDiEnumDeviceInfo(NewDeviceInfoSet, i, DeviceInfoData); i++) { // invoke class installer method with DIF_REMOVE flag if (!SetupDiCallClassInstaller(DIF_REMOVE, NewDeviceInfoSet, DeviceInfoData)) { // failed to uninstall device SetupDiDestroyDeviceInfoList(NewDeviceInfoSet); return -16; } } // perform cleanup SetupDiDestroyDeviceInfoList(NewDeviceInfoSet); return CR_SUCCESS; }Forcing hardware reenumeration:
private Int32 ScanForHardwareChanges() { UInt32 DevInst = 0; UInt32 result = CM_Locate_DevNode(ref DevInst, null, CM_LOCATE_DEVNODE_NORMAL); if (result != CR_SUCCESS) { // failed to get devnode return -1; } result = CM_Reenumerate_DevNode(DevInst, 0); if (result != CR_SUCCESS) { // reenumeration failed return -2; } return CR_SUCCESS; }Method invocation:
private void btnFixme_Click(object sender, EventArgs e) { // remove all devices of class "Image" (image capturing devices) Int32 status = RemoveAllDevicesInClassName("Image"); if (status == CR_SUCCESS) { status = ScanForHardwareChanges(); } lblStatus.Text = status==CR_SUCCESS ? "Done!" : "Error: " + status.ToString(); }I hope the code is commented well enough, and doesn't require further explanation.
Thanks! Was looking for something like this for a long time? BTW, Is there a flag opposite to DIGCF_PRESENT, so I just get hidden devices?
ReplyDelete