Windows实现USB设备热插拔功能
2025-05-30 18:38 一只老老老菜鸟 阅读(281) 评论(0) 收藏 举报https://learn.microsoft.com/zh-cn/windows/win32/devio/detecting-media-insertion-or-removal
### 实现步骤
1. **注册设备通知**:
- 使用`RegisterDeviceNotification`注册要监听的设备类型
2. **添加设备变更消息处理**:
- 在窗口类中处理`WM_DEVICECHANGE`消息
- 判断设备类型(如USB设备)和事件类型(设备插入、移除)
- 通过pidvid找到对应设备
- 调用回调或发送自定义消息到ui,更新ui
3.如需实现其他类型设备监听,更改设备接口类GUID和判断设备类型
完整实现代码
DeviceMonitor.h 头文件
#pragma once #include <Dbt.h> #include <SetupAPI.h> #include <cfgmgr32.h> #include <string> #include <vector> #pragma comment(lib, "setupapi.lib") // 设备信息结构 struct DeviceInfo { std::wstring devicePath; // 设备路径 std::wstring description; // 设备描述 std::wstring manufacturer; // 制造商 std::wstring instanceId; // 设备实例ID }; class CDeviceMonitor { public: CDeviceMonitor(); virtual ~CDeviceMonitor(); // 初始化设备监控 BOOL Initialize(CWnd* pNotifyWnd); // 获取当前连接的设备列表 std::vector<DeviceInfo> GetConnectedDevices() const; // 处理设备变更消息 LRESULT OnDeviceChange(WPARAM wParam, LPARAM lParam); private: // 注册设备通知 BOOL RegisterDeviceNotification(); // 注销设备通知 void UnregisterDeviceNotification(); // 获取设备详细信息 DeviceInfo GetDeviceInfo(const wchar_t* devicePath) const; // 枚举所有连接的设备 void EnumerateConnectedDevices(); private: CWnd* m_pNotifyWnd; // 接收通知的窗口 HDEVNOTIFY m_hDevNotify; // 设备通知句柄 GUID m_deviceInterfaceGUID; // 要监控的设备类GUID std::vector<DeviceInfo> m_connectedDevices; // 已连接设备列表 mutable CCriticalSection m_csDeviceList; // 设备列表锁 };
DeviceMonitor.cpp 实现文件
#include "stdafx.h" #include "DeviceMonitor.h" #include <initguid.h> // 必须包含以使用GUID定义 // 定义USB设备接口GUID DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED); CDeviceMonitor::CDeviceMonitor() : m_pNotifyWnd(nullptr) , m_hDevNotify(nullptr) { // 设置监控USB设备 m_deviceInterfaceGUID = GUID_DEVINTERFACE_USB_DEVICE; } CDeviceMonitor::~CDeviceMonitor() { UnregisterDeviceNotification(); } // 初始化设备监控 BOOL CDeviceMonitor::Initialize(CWnd* pNotifyWnd) { if (!pNotifyWnd || !pNotifyWnd->GetSafeHwnd()) { return FALSE; } m_pNotifyWnd = pNotifyWnd; // 枚举当前已连接的设备 EnumerateConnectedDevices(); // 注册设备通知 return RegisterDeviceNotification(); } // 注册设备通知 BOOL CDeviceMonitor::RegisterDeviceNotification() { if (!m_pNotifyWnd) return FALSE; DEV_BROADCAST_DEVICEINTERFACE notificationFilter; ZeroMemory(¬ificationFilter, sizeof(notificationFilter)); notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; notificationFilter.dbcc_classguid = m_deviceInterfaceGUID; // 注册设备通知 m_hDevNotify = ::RegisterDeviceNotification( m_pNotifyWnd->GetSafeHwnd(), ¬ificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE ); return (m_hDevNotify != nullptr); } // 注销设备通知 void CDeviceMonitor::UnregisterDeviceNotification() { if (m_hDevNotify) { ::UnregisterDeviceNotification(m_hDevNotify); m_hDevNotify = nullptr; } } // 处理设备变更消息 LRESULT CDeviceMonitor::OnDeviceChange(WPARAM wParam, LPARAM lParam) { if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) { PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam; if (pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; // 获取设备路径 std::wstring devicePath = pDevInf->dbcc_name; if (wParam == DBT_DEVICEARRIVAL) { // 设备插入 DeviceInfo info = GetDeviceInfo(devicePath.c_str()); CSingleLock lock(&m_csDeviceList, TRUE); m_connectedDevices.push_back(info); // 发送自定义消息通知UI更新 if (m_pNotifyWnd) { m_pNotifyWnd->PostMessage(WM_DEVICE_ARRIVED, (WPARAM)new DeviceInfo(info)); } } else if (wParam == DBT_DEVICEREMOVECOMPLETE) { // 设备移除 CSingleLock lock(&m_csDeviceList, TRUE); // 从列表中移除设备 auto it = std::remove_if(m_connectedDevices.begin(), m_connectedDevices.end(), [&devicePath](const DeviceInfo& info) { return _wcsicmp(info.devicePath.c_str(), devicePath.c_str()) == 0; }); if (it != m_connectedDevices.end()) { DeviceInfo removedDevice = *it; m_connectedDevices.erase(it, m_connectedDevices.end()); // 发送自定义消息通知UI更新 if (m_pNotifyWnd) { m_pNotifyWnd->PostMessage(WM_DEVICE_REMOVED, (WPARAM)new DeviceInfo(removedDevice)); } } } } } return TRUE; } // 获取设备详细信息 DeviceInfo CDeviceMonitor::GetDeviceInfo(const wchar_t* devicePath) const { DeviceInfo info; info.devicePath = devicePath; // 获取设备信息集句柄 HDEVINFO hDevInfo = SetupDiGetClassDevs( &m_deviceInterfaceGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE ); if (hDevInfo == INVALID_HANDLE_VALUE) { return info; } SP_DEVICE_INTERFACE_DATA devInterfaceData = {0}; devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); DWORD memberIndex = 0; while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &m_deviceInterfaceGUID, memberIndex, &devInterfaceData)) { DWORD requiredSize = 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterfaceData, NULL, 0, &requiredSize, NULL); PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize); if (pDetail == NULL) { memberIndex++; continue; } pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); SP_DEVINFO_DATA devInfoData = {0}; devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterfaceData, pDetail, requiredSize, NULL, &devInfoData)) { if (_wcsicmp(pDetail->DevicePath, devicePath) == 0) { // 获取设备描述 WCHAR desc[256] = {0}; if (SetupDiGetDeviceRegistryProperty( hDevInfo, &devInfoData, SPDRP_DEVICEDESC, NULL, (PBYTE)desc, sizeof(desc), NULL)) { info.description = desc; } // 获取制造商 WCHAR mfg[256] = {0}; if (SetupDiGetDeviceRegistryProperty( hDevInfo, &devInfoData, SPDRP_MFG, NULL, (PBYTE)mfg, sizeof(mfg), NULL)) { info.manufacturer = mfg; } // 获取设备实例ID WCHAR instanceId[256] = {0}; if (CM_Get_Device_ID(devInfoData.DevInst, instanceId, sizeof(instanceId)/sizeof(WCHAR), 0) == CR_SUCCESS) { info.instanceId = instanceId; } free(pDetail); break; } } free(pDetail); memberIndex++; } SetupDiDestroyDeviceInfoList(hDevInfo); return info; } // 枚举所有连接的设备 void CDeviceMonitor::EnumerateConnectedDevices() { HDEVINFO hDevInfo = SetupDiGetClassDevs( &m_deviceInterfaceGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE ); if (hDevInfo == INVALID_HANDLE_VALUE) { return; } SP_DEVICE_INTERFACE_DATA devInterfaceData = {0}; devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); DWORD memberIndex = 0; while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &m_deviceInterfaceGUID, memberIndex, &devInterfaceData)) { DWORD requiredSize = 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterfaceData, NULL, 0, &requiredSize, NULL); PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize); if (pDetail == NULL) { memberIndex++; continue; } pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); SP_DEVINFO_DATA devInfoData = {0}; devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterfaceData, pDetail, requiredSize, NULL, &devInfoData)) { DeviceInfo info = GetDeviceInfo(pDetail->DevicePath); m_connectedDevices.push_back(info); } free(pDetail); memberIndex++; } SetupDiDestroyDeviceInfoList(hDevInfo); } // 获取当前连接的设备列表 std::vector<DeviceInfo> CDeviceMonitor::GetConnectedDevices() const { CSingleLock lock(&m_csDeviceList, TRUE); return m_connectedDevices; }
MyDialog.h 对话框头文件
#pragma once #include "DeviceMonitor.h" // 自定义消息 #define WM_DEVICE_ARRIVED (WM_USER + 200) #define WM_DEVICE_REMOVED (WM_USER + 201) class CMyDialog : public CDialogEx { DECLARE_DYNAMIC(CMyDialog) public: CMyDialog(CWnd* pParent = nullptr); virtual ~CMyDialog(); protected: virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog(); // 设备监控器 CDeviceMonitor m_deviceMonitor; // 消息处理 afx_msg void OnPaint(); afx_msg LRESULT OnDeviceChange(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnDeviceArrived(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnDeviceRemoved(WPARAM wParam, LPARAM lParam); // 更新UI void UpdateDeviceList(); DECLARE_MESSAGE_MAP() private: CListBox m_deviceListBox; // 设备列表控件 };
MyDialog.cpp 对话框实现文件
#include "stdafx.h" #include "MyDialog.h" #include "resource.h" IMPLEMENT_DYNAMIC(CMyDialog, CDialogEx) CMyDialog::CMyDialog(CWnd* pParent) : CDialogEx(IDD_MYDIALOG, pParent) { } CMyDialog::~CMyDialog() { } void CMyDialog::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_DEVICE_LIST, m_deviceListBox); } BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx) ON_WM_PAINT() ON_MESSAGE(WM_DEVICECHANGE, OnDeviceChange) ON_MESSAGE(WM_DEVICE_ARRIVED, OnDeviceArrived) ON_MESSAGE(WM_DEVICE_REMOVED, OnDeviceRemoved) END_MESSAGE_MAP() BOOL CMyDialog::OnInitDialog() { CDialogEx::OnInitDialog(); // 设置对话框标题 SetWindowText(_T("设备热插拔监控")); // 初始化设备监控 if (!m_deviceMonitor.Initialize(this)) { AfxMessageBox(_T("无法初始化设备监控")); } // 更新设备列表 UpdateDeviceList(); return TRUE; } void CMyDialog::OnPaint() { CPaintDC dc(this); // 绘制代码... } // 处理设备变更消息 LRESULT CMyDialog::OnDeviceChange(WPARAM wParam, LPARAM lParam) { return m_deviceMonitor.OnDeviceChange(wParam, lParam); } // 处理设备插入消息 LRESULT CMyDialog::OnDeviceArrived(WPARAM wParam, LPARAM lParam) { DeviceInfo* pInfo = reinterpret_cast<DeviceInfo*>(wParam); if (pInfo) { CString msg; msg.Format(_T("设备插入: %s"), pInfo->description.c_str()); AfxMessageBox(msg); // 更新设备列表 UpdateDeviceList(); delete pInfo; } return 0; } // 处理设备移除消息 LRESULT CMyDialog::OnDeviceRemoved(WPARAM wParam, LPARAM lParam) { DeviceInfo* pInfo = reinterpret_cast<DeviceInfo*>(wParam); if (pInfo) { CString msg; msg.Format(_T("设备移除: %s"), pInfo->description.c_str()); AfxMessageBox(msg); // 更新设备列表 UpdateDeviceList(); delete pInfo; } return 0; } // 更新设备列表 void CMyDialog::UpdateDeviceList() { m_deviceListBox.ResetContent(); std::vector<DeviceInfo> devices = m_deviceMonitor.GetConnectedDevices(); for (const auto& device : devices) { CString entry; entry.Format(_T("%s [%s]"), device.description.c_str(), device.manufacturer.c_str()); m_deviceListBox.AddString(entry); } }
解决方案设计说明
1. 设备监控核心类 (CDeviceMonitor)
-
设备注册:使用
RegisterDeviceNotification注册设备通知 -
设备枚举:通过
SetupDiGetClassDevs和SetupDiEnumDeviceInterfaces枚举已连接设备 -
设备信息获取:使用
SetupDiGetDeviceRegistryProperty获取设备详细信息 -
线程安全:使用
CCriticalSection保护设备列表
2. 消息处理流程

浙公网安备 33010602011771号