Je voudrais savoir s'il existe un moyen de déclencher un événement lorsqu'un périphérique est ajouté ou supprimé du système. Je veux pouvoir détecter si, disons, un lecteur flash USB a été ajouté, ou une souris, ou autre chose. J'ai essayé de chercher autour, mais je ne trouve rien qui dise comment faire ça.
Des idées?
Si vous avez une fenêtre dans votre application, vous pouvez utiliser quelque chose comme ceci:
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;
}
}
Voici comment vous l'utilisez à partir d'une fenêtre WPF (Windows Forms est similaire):
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;
}
Voici l'exemple d'utilisation des formulaires Windows (encore plus 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 réponse acceptée est excellente, mais elle ne fonctionne qu'avec des périphériques USB.
Pour le faire fonctionner avec tous les appareils (et éventuellement filtrer USB), utilisez la classe légèrement modifiée suivante:
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;
/// <summary>
/// Registers a window to receive notifications when devices are plugged or unplugged.
/// </summary>
/// <param name="windowHandle">Handle to the window receiving notifications.</param>
/// <param name="usbOnly">true to filter to USB devices only, false to be notified for all devices.</param>
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);
}
/// <summary>
/// Unregisters the window for device notifications
/// </summary>
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;
}
}
Le changement de clé est le paramètre Flags
lors de l'appel de RegisterDeviceNotification
(voir https://msdn.Microsoft.com/en-us/library/aa363431 (v = vs.85) .aspx ), qui, s'il est défini sur 4
au lieu de 0
ignorera le paramètre ClassGuid
et s'enregistrera pour tous les périphériques.
Je suis venu à ce poste pour un cas plus spécifique que la question d'origine en ce sens que je veux être averti chaque fois qu'un port est ajouté ou supprimé. Pour cette situation, la réponse est beaucoup plus simple et ne nécessite pas d'appeler RegisterDeviceNotification:
Les événements DBT_DEVICEARRIVAL et DBT_DEVICEREMOVECOMPLETE sont automatiquement diffusés à toutes les fenêtres de niveau supérieur pour les périphériques de port. Par conséquent, il n'est pas nécessaire d'appeler RegisterDeviceNotification pour les ports ....
https://docs.Microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-registerdevicenotificationa
La solution devient donc quelque chose comme:
using System.Runtime.InteropServices;
//Put all of the following code inside your Form's partial class:
private struct DEV_BROADCAST_HDR {
internal UInt32 dbch_size;
internal UInt32 dbch_devicetype;
internal UInt32 dbch_reserved;
};
protected override void WndProc(ref Message m) {
base.WndProc(ref m); //This allows window default behavior of base class to be executed
if (m.Msg == 0x0219) { //WM_DEVICECHANGE = 0x0219
DEV_BROADCAST_HDR dbh;
switch ((int)m.WParam) {
case 0x8000: //DBT_DEVICEARRIVAL = 0x8000
dbh = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
if (dbh.dbch_devicetype == 0x00000003) { //DBT_DEVTYP_PORT = 0x00000003
Console.WriteLine("Port added!");
//TODO
}
break;
case 0x8004: //DBT_DEVICEREMOVECOMPLETE = 0x8004
dbh = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
if (dbh.dbch_devicetype == 0x00000003) { //DBT_DEVTYP_PORT = 0x00000003
Console.WriteLine("Port removed!");
//TODO
}
break;
}
}
}