How to Detect When Your USB Device Has Been Plugged In






4.91/5 (7 votes)
This short article shows how to automatically detect when a given USB device is plugged in.
Introduction
I needed to automatically connect to a USB scanner whenever it was connected to the PC. The simplest approach would have been to run a timer, which on expiry queried for the presence of the device. But this is inefficient, and unresponsive unless the timer repeat period is short which would tie up the CPU. A better solution is to automatically detect when the device is plugged in. Fortunately, that is easy to do as I shall demonstrate.
Background
You will need to understand C#, and have a basic knowledge of Windows message processing, threading, and USB devices.
Using the Code
The example code detects when a device with a specific VID
(Vendor ID) and PID
(Process ID) connects, and then restarts it.
The first step is to intercept the WM_DEVICECHANGE
Windows message which is sent whenever the device configuration changes, e.g., a USB device is plugged in. This is easily done by hooking a Windows procedure to an existing window as follows:
var helper = new System.Windows.Interop.WindowInteropHelper(_hiddenWindow);
IntPtr hWnd = helper.Handle;
System.Windows.Interop.HwndSource src = System.Windows.Interop.HwndSource.FromHwnd(hWnd);
src.AddHook(WndProc);
The _hiddenWindow
variable is an instance of the Systems.Windows.Window
class.
The WndProc
method processes the window messages:
private const int WM_DEVICECHANGE = 537;
private System.Threading.Tasks.Task _restartDeviceTask = null;
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_DEVICECHANGE)
{
if (_configData.AutomaticStartup &&
(Status == ReaderStatus.Stopped) && (_restartDeviceTask == null))
{
// This must be done asynchronously otherwise it screws the message pump
_restartDeviceTask = new System.Threading.Tasks.Task((Action)delegate
{
RestartUSBDevice();
_restartDeviceTask = null;
});
_restartDeviceTask.Start();
}
}
handled = false;
return 0;
}
On receipt of a WM_DEVICECHANGE
message, the method checks whether or not we need to restart our device. If so, it creates a new task to invoke the RestartUSBDevice
method. The background task ensures that the method does not block execution of the windows message pump, which would hang our application.
This line simply works out if we need to start our device, and makes sure a task is not already running:
if (_configData.AutomaticStartup &&
(Status == ReaderStatus.Stopped) && (_restartDeviceTask == null))
The task handle is set to null
by the task after the device has been restarted.
The RestartUSBDevice
method searches for loaded USB devices with our Vendor ID, and one of our Process IDs and if it finds one of interest, it starts it:
private void RestartUSBDevice()
{
if (Status != ReaderStatus.Stopped)
{
return;
}
const string VID_OURCOMPANY = "VID_0DB5";
using (var searcher = new ManagementObjectSearcher_
(@"SELECT * FROM Win32_PnPEntity WHERE DeviceID LIKE '%" + VID_OURCOMPANY + "%'"))
{
const string PID_MERCURY_READER = "PID_3001";
const string PID_JUPITER_READER = "PID_2003";
try
{
ManagementObjectCollection deviceCollection = searcher.Get();
foreach (var device in deviceCollection)
{
string deviceID = (string)device.GetPropertyValue("DeviceID");
if (
deviceID.Contains(PID_MERCURY_READER) ||
deviceID.Contains(PID_JUPITER_READER)
)
{
StartUSBDevice();
break;
}
}
}
catch (Exception)
{
}
}
}
Note that the code uses string
constants for each VID
and PID
rather than using "PID_3001
" and "PID_2003
" directly. This is to make the code more readable, as it tells us the meaning of each string
.
Note also that the RestartUSBDevice
method uses API functions which will not work unless the Windows message pump is working, hence the reason why the windows procedure invokes the method from a background task.
Points of Interest
As already pointed out, you must be careful what you do in the Windows procedure, as you might inadvertently block the Windows message processing.
History
- 4th May, 2017: First version