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.