How to Receive Plug & Play Device Notifications Without a Windows Form

How to receive Plug & Play device notifications without a windows form

You'll need a window, there's no way around that. Here's a sample implementation. Implement an event handler for the DeviceChangeNotifier.DeviceNotify event to get notifications. Call the DeviceChangeNotifier.Start() method at the start of your program. Call DeviceChangeNotifier.Stop() at the end of your program. Beware that the DeviceNotify event is raised on a background thread, be sure to lock as needed to keep your code thread-safe.

using System;
using System.Windows.Forms;
using System.Threading;

class DeviceChangeNotifier : Form {
public delegate void DeviceNotifyDelegate(Message msg);
public static event DeviceNotifyDelegate DeviceNotify;
private static DeviceChangeNotifier mInstance;

public static void Start() {
Thread t = new Thread(runForm);
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
}
public static void Stop() {
if (mInstance == null) throw new InvalidOperationException("Notifier not started");
DeviceNotify = null;
mInstance.Invoke(new MethodInvoker(mInstance.endForm));
}
private static void runForm() {
Application.Run(new DeviceChangeNotifier());
}

private void endForm() {
this.Close();
}
protected override void SetVisibleCore(bool value) {
// Prevent window getting visible
if (mInstance == null) CreateHandle();
mInstance = this;
value = false;
base.SetVisibleCore(value);
}
protected override void WndProc(ref Message m) {
// Trap WM_DEVICECHANGE
if (m.Msg == 0x219) {
DeviceNotifyDelegate handler = DeviceNotify;
if (handler != null) handler(m);
}
base.WndProc(ref m);
}
}

c# receive plug and play events

I've accomplished this in the past when writing a file browser by using a dummy HwndSource and adding an event handler via the HwndSource.AddHook() method.

// In object constructor
var hwndSource = new HwndSource(0, 0, 0, 0, 0, "", IntPtr.Zero); // Set up dummy HwndSource
hwndSource.AddHook(sourceHook);

IntPtr sourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg = WM_DEVICECHANGE)
if (wParam.ToInt32 == DBT_DEVICEARRIVAL) // Do what you need to do
if (wParam.ToInt32 == DBT_DEVICEREMOVALCOMPLETE) // Handle device removal
}
// Uses these defined constants:
private const int WM_DEVICECHANGE = 0x219;
private const int DBT_DEVICEARRIVAL = 0x8000;
private const int DBT_DEVICEREMOVALCOMPLETE = 0x8004;

The MSDN for WM_DEVICECHANGE also has info for other const definitions that may be useful:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx

Check for device change (add/remove) events

If you have a window in your application, you can use something like this:

using System;
using System.Runtime.InteropServices;

internal static class UsbNotification
{
public const int DbtDevicearrival = 0x8000; // system detected a new device
public const int DbtDeviceremovecomplete = 0x8004; // device is gone
public const int WmDevicechange = 0x0219; // device change event
private const int DbtDevtypDeviceinterface = 5;
private static readonly Guid GuidDevinterfaceUSBDevice = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED"); // USB devices
private static IntPtr notificationHandle;

/// <summary>
/// Registers a window to receive notifications when USB devices are plugged or unplugged.
/// </summary>
/// <param name="windowHandle">Handle to the window receiving notifications.</param>
public static void RegisterUsbDeviceNotification(IntPtr windowHandle)
{
DevBroadcastDeviceinterface dbi = new DevBroadcastDeviceinterface
{
DeviceType = DbtDevtypDeviceinterface,
Reserved = 0,
ClassGuid = GuidDevinterfaceUSBDevice,
Name = 0
};

dbi.Size = Marshal.SizeOf(dbi);
IntPtr buffer = Marshal.AllocHGlobal(dbi.Size);
Marshal.StructureToPtr(dbi, buffer, true);

notificationHandle = RegisterDeviceNotification(windowHandle, buffer, 0);
}

/// <summary>
/// Unregisters the window for USB device notifications
/// </summary>
public static void UnregisterUsbDeviceNotification()
{
UnregisterDeviceNotification(notificationHandle);
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);

[DllImport("user32.dll")]
private static extern bool UnregisterDeviceNotification(IntPtr handle);

[StructLayout(LayoutKind.Sequential)]
private struct DevBroadcastDeviceinterface
{
internal int Size;
internal int DeviceType;
internal int Reserved;
internal Guid ClassGuid;
internal short Name;
}
}

Here's how you use it from a WPF Window (Windows Forms is similar):

    protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);

// Adds the windows message processing hook and registers USB device add/removal notification.
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
if (source != null)
{
windowHandle = source.Handle;
source.AddHook(HwndHandler);
UsbNotification.RegisterUsbDeviceNotification(windowHandle);
}
}

/// <summary>
/// Method that receives window messages.
/// </summary>
private IntPtr HwndHandler(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
if (msg == UsbNotification.WmDevicechange)
{
switch ((int)wparam)
{
case UsbNotification.DbtDeviceremovecomplete:
Usb_DeviceRemoved(); // this is where you do your magic
break;
case UsbNotification.DbtDevicearrival:
Usb_DeviceAdded(); // this is where you do your magic
break;
}
}

handled = false;
return IntPtr.Zero;
}

Here's the use example for Windows Forms (even simpler):

public Form1()
{
InitializeComponent();
UsbNotification.RegisterUsbDeviceNotification(this.Handle);
}

protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == UsbNotification.WmDevicechange)
{
switch ((int)m.WParam)
{
case UsbNotification.DbtDeviceremovecomplete:
Usb_DeviceRemoved(); // this is where you do your magic
break;
case UsbNotification.DbtDevicearrival:
Usb_DeviceAdded(); // this is where you do your magic
break;
}
}
}

Detect external devices insertion on Windows

So I've finally found sort of answers to the questions.

  1. The issue was with the GUID I've provided for the NotificationFilter. So I've set it to receive notifications from all devices with this code:
    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;

    ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));

    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    // No need to set up dbcc_classguid as it is ignored when
    // DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is setted (see line down below)

    HDEVNOTIFY dev_notify = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
  2. As far as for other types of notifications in windows that the app could sign up for, I found only one partially deprecated list of all Win32 API functions, where I found some new Notifications with Ctrl+F search :)

Hope this could save somebody's time.



Related Topics



Leave a reply



Submit