WPF应用如何捕获SD卡经程序弹出后的物理移除事件?
捕获SD卡物理移除事件的解决方案
一、通过WM_DEVICECHANGE窗口消息捕获(推荐,更可靠)
WPF窗口基于Win32窗口实现,重写WndProc方法直接监听Windows原生设备变更消息,比WMI更灵敏,尤其适合卷已卸载后的硬件移除场景。
实现步骤
- 在WPF窗口类中重写
WndProc,监听WM_DEVICECHANGE消息(消息ID为0x0219)。 - 处理
DBT_DEVICEREMOVECOMPLETE事件(事件类型为0x8004),解析设备信息判断是否为目标SD卡的物理移除。
代码示例
protected override void WndProc(ref Message m) { const int WM_DEVICECHANGE = 0x0219; const int DBT_DEVICEREMOVECOMPLETE = 0x8004; if (m.Msg == WM_DEVICECHANGE) { int eventType = Marshal.ReadInt32(m.LParam, 0); if (eventType == DBT_DEVICEREMOVECOMPLETE) { DEV_BROADCAST_HDR hdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR)); if (hdr.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { DEV_BROADCAST_DEVICEINTERFACE deviceInterface = (DEV_BROADCAST_DEVICEINTERFACE)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_DEVICEINTERFACE)); string devicePath = deviceInterface.dbcc_name; // 替换为你的SD卡/读卡器的VID/PID特征 if (devicePath.Contains("VID_XXXX&PID_XXXX")) { // 切换到UI线程执行操作 Dispatcher.Invoke(() => { /* 你的UI更新逻辑 */ }); } } } } base.WndProc(ref m); } // 定义Win32结构体 [StructLayout(LayoutKind.Sequential)] private struct DEV_BROADCAST_HDR { public int dbch_size; public int dbch_devicetype; public int dbch_reserved; } private const int DBT_DEVTYP_DEVICEINTERFACE = 0x00000005; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DEV_BROADCAST_DEVICEINTERFACE { public int dbcc_size; public int dbcc_devicetype; public int dbcc_reserved; public Guid dbcc_classguid; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)] public string dbcc_name; }
二、修正WMI查询方案
之前的WMI查询未生效,是因为监听的事件类型不对。SD卡物理移除属于PnP设备实例删除事件,需监听Win32_PnPEntity的InstanceDeletionEvent,而非逻辑磁盘或通用设备变更事件。
实现步骤
- 使用
ManagementEventWatcher监听Win32_PnPEntity的实例删除事件。 - 通过设备的
PNPDeviceID或HardwareID过滤目标SD卡设备。
代码示例
using System.Management; private void SetupWmiWatcher() { string queryString = "SELECT * FROM InstanceDeletionEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_PnPEntity'"; ManagementEventWatcher watcher = new ManagementEventWatcher(new WqlEventQuery(queryString)); watcher.EventArrived += Watcher_EventArrived; watcher.Start(); } private void Watcher_EventArrived(object sender, EventArrivedEventArgs e) { ManagementBaseObject targetInstance = (ManagementBaseObject)e.NewEvent["TargetInstance"]; string pnpDeviceId = targetInstance["PNPDeviceID"]?.ToString(); // 匹配你的SD卡设备的PNPDeviceID特征 if (pnpDeviceId != null && pnpDeviceId.Contains("VID_XXXX&PID_XXXX")) { // 切换到UI线程执行操作 Dispatcher.Invoke(() => { /* 你的UI更新逻辑 */ }); } }
关键注意事项
- 区分卷卸载和物理移除:卷卸载是软件层面的逻辑磁盘移除,物理移除是硬件层面的PnP设备消失,两者触发的事件类型不同。
- 设备匹配:提前通过设备管理器获取目标SD卡/读卡器的
VID/PID或PNPDeviceID,确保事件过滤的准确性。 - 线程安全:WMI事件和WM_DEVICECHANGE消息均可能在非UI线程触发,必须通过
Dispatcher.Invoke切换到UI线程执行UI操作。
内容的提问来源于stack exchange,提问作者Raj
相关产品推荐
相关产品推荐

