Verificar eventos de cambio de dispositivo (agregar / eliminar)

Me gustaría saber si hay una forma de activar un evento cuando se agrega o elimina un dispositivo del sistema. Quiero poder detectar, por ejemplo, si se ha agregado una unidad flash USB, o un mouse, o lo que sea. Intenté buscar, pero no encuentro nada que diga cómo hacerlo.

¿Algunas ideas?

Si tiene una ventana en su aplicación, puede usar algo como esto:

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; ///  /// Registers a window to receive notifications when USB devices are plugged or unplugged. ///  /// Handle to the window receiving notifications. 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); } ///  /// Unregisters the window for USB device notifications ///  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; } } 

Así es como lo usa desde una ventana de WPF (Windows Forms es 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); } } ///  /// Method that receives window messages. ///  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; } 

Aquí está el ejemplo de uso para Windows Forms (aún más simple):

 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; } } } 

La respuesta aceptada es excelente, sin embargo, solo funciona con dispositivos USB.

Para que funcione con todos los dispositivos (y opcionalmente filtro USB), use la siguiente clase ligeramente modificada:

 static class DeviceNotification { //https://msdn.microsoft.com/en-us/library/aa363480(v=vs.85).aspx public const int DbtDeviceArrival = 0x8000; // system detected a new device public const int DbtDeviceRemoveComplete = 0x8004; // device is gone public const int DbtDevNodesChanged = 0x0007; //A device has been added to or removed from the system. public const int WmDevicechange = 0x0219; // device change event private const int DbtDevtypDeviceinterface = 5; //https://msdn.microsoft.com/en-us/library/aa363431(v=vs.85).aspx private const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 4; private static readonly Guid GuidDevinterfaceUSBDevice = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED"); // USB devices private static IntPtr notificationHandle; ///  /// Registers a window to receive notifications when devices are plugged or unplugged. ///  /// Handle to the window receiving notifications. /// true to filter to USB devices only, false to be notified for all devices. public static void RegisterDeviceNotification(IntPtr windowHandle, bool usbOnly = false) { var 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, usbOnly ? 0 : DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); } ///  /// Unregisters the window for device notifications ///  public static void UnregisterDeviceNotification() { 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; } } 

El cambio de clave es el parámetro Flags al llamar a RegisterDeviceNotification (consulte https://msdn.microsoft.com/en-us/library/aa363431(v=vs.85).aspx ), que si se establece en 4 lugar de 0 ignorará el parámetro ClassGuid y registrarse para todos los dispositivos.