C# 检查设备更改(添加/删除)事件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16245706/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Check for device change (add/remove) events
提问by Andi Jay
I'd like to know if there's a way to trigger an event when a device is added or removed from the system. I want to be able to detect if say, a USB flash drive has been added, or a mouse, or whatever else. I tried searching around, but I can't find anything that say's how to do this.
我想知道是否有办法在系统中添加或删除设备时触发事件。我希望能够检测到是否添加了 USB 闪存驱动器、鼠标或其他任何东西。我尝试四处寻找,但找不到任何说明如何执行此操作的内容。
Any ideas?
有任何想法吗?
采纳答案by Darko Kenda
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):
以下是从 WPF 窗口(Windows 窗体类似)中使用它的方法:
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):
这是 Windows 窗体的使用示例(甚至更简单):
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;
}
}
}
回答by Erwin Mayer
The accepted answer is excellent, however it only works with USB devices.
接受的答案非常好,但它仅适用于 USB 设备。
To make it work with all devices (and optionally filter USB), use the following slightly modified class:
要使其适用于所有设备(并可选择过滤 USB),请使用以下稍作修改的类:
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;
}
}
The key change is the Flagsparameter when calling RegisterDeviceNotification(see https://msdn.microsoft.com/en-us/library/aa363431(v=vs.85).aspx), which if set to 4instead of 0will ignore the ClassGuidparameter and register for all devices.
关键的变化是Flags调用时的参数RegisterDeviceNotification(参见https://msdn.microsoft.com/en-us/library/aa363431(v=vs.85).aspx),如果设置为4而不是0将忽略ClassGuid参数并注册适用于所有设备。
回答by DatuPuti
I came to this post for a more specific case than the original question in that I want to be notified anytime a port is added or removed. For this situation the answer is much more simple and does not require calling RegisterDeviceNotification:
我来到这篇文章是为了一个比原始问题更具体的案例,因为我希望在添加或删除端口时随时收到通知。对于这种情况,答案要简单得多,不需要调用 RegisterDeviceNotification:
The DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE events are automatically broadcast to all top-level windows for port devices. Therefore, it is not necessary to call RegisterDeviceNotification for ports....
DBT_DEVICEARRIVAL 和 DBT_DEVICEREMOVECOMPLETE 事件会自动广播到端口设备的所有顶级窗口。因此,没有必要为端口调用 RegisterDeviceNotification....
https://docs.microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-registerdevicenotificationa
https://docs.microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-registerdevicenotificationa
So the solution becomes something like:
所以解决方案变成了这样:
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;
}
}
}

