C++ Win32 未收到DBT_DEVICEARRIVAL或DBT_DEVICEREMOVECOMPLETE WM_D
C++ Win32 Not receiving DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE on WM_DEVICECHANGE
我一直在努力检测USB插入/移除。 我已经使用CreateWindowEx()实现了代码,并通过我的窗口进程回调传递了一个WNCLASSEX。 插入和取出 USB 时,我成功收到WM_DEVICECHANGE消息,但 wParam 始终设置为 DBT_DEVNODES_CHANGED。
我从来不会DBT_DEVICEARRIVAL,也从来不会DBT_DEVICEREMOVECOMPLETE。 我一直在使用我得到的东西,但我真的需要能够区分设备到达和移除,以便我可以根据我收到的采取不同的操作。
现在,我必须在收到DBT_DEVNODES_CHANGED后放置一个计时器,然后测试以查看系统上是否有任何新的可移动对象,或者我的列表中是否不再存在任何可移动对象。 我确定这是不对的,所以我想我会问。 我宁愿摆脱计时器,只接收这两条消息。 这对我必须做的事情有很大帮助。 有什么建议吗?
这是我注册回调的代码,以及回调本身:
注意:2015 年 3 月 12 日:代码已更新,以显示实际的 GUID 和 DoRegisterDeviceInterfaceToHwnd() 函数的定义。
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72, 0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
//GUID WusbrawGUID = {0xa5dcbf10, 0x6530, 0x11d2, 0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed };
//GUID WusbGUID = {0x88BAE032, 0x5A81, 0x49f0, 0xBC, 0x3D, 0xA4, 0xFF, 0x13, 0x82, 0x16, 0xD6 };
INT_PTR WINAPI WinProcCallback(HWND __hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL DoRegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND __hWnd, HDEVNOTIFY *hDeviceNotify);
bool UsbController::startNotifyUsbAddedRemoved(QString &errmsg)
{
WNDCLASSEX wndClass;
wndClass.cbSize = sizeof(wndClass);
wndClass.style = 0;
wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback);
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wndClass.hCursor = LoadCursor(0, IDC_ARROW);
wndClass.lpszClassName = WND_CLASS_NAME;
wndClass.lpszMenuName = NULL;
wndClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
if (!RegisterClassEx(&wndClass))
{
FormatErrorMsg("RegisterClassEx: ", errmsg);
return false;
}
HINSTANCE hInstance = (HINSTANCE)::GetModuleHandle(NULL);
__hWnd = CreateWindowEx(
WS_EX_CLIENTEDGE | WS_EX_APPWINDOW,
WND_CLASS_NAME,
WND_APP_NAME,
WS_OVERLAPPEDWINDOW, // style
CW_USEDEFAULT, 0,
0, 0,
NULL, NULL,
hInstance,
NULL);
if ( __hWnd == NULL )
{
FormatErrorMsg("CreateWindowEx: ", errmsg);
return false;
}
return true;
}
INT_PTR WINAPI WinProcCallback(HWND __hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT lRet = 1;
static HDEVNOTIFY hDeviceNotify;
static HWND hEditWnd;
static ULONGLONG msgCount = 0;
switch (message)
{
case WM_CREATE:
//
// This is the actual registration., In this example, registration
// should happen only once, at application startup when the window
// is created.
//
// If you were using a service, you would put this in your main code
// path as part of your service initialization.
//
if ( ! DoRegisterDeviceInterfaceToHwnd( WceusbshGUID, __hWnd, &hDeviceNotify) )
{
// Terminate on failure.
//ErrorHandler(TEXT("DoRegisterDeviceInterfaceToHwnd"));
ExitProcess(1);
}
//
// Make the child window for output.
//
hEditWnd = CreateWindow(TEXT("EDIT"),// predefined class
NULL, // no window title
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
0, 0, 0, 0, // set size in WM_SIZE message
__hWnd, // parent window
(HMENU)1, // edit control ID
(HINSTANCE) GetWindowLong(__hWnd, GWL_HINSTANCE),
NULL); // pointer not needed
if ( hEditWnd == NULL )
{
// Terminate on failure.
ExitProcess(1);
}
// Add text to the window.
SendMessage(hEditWnd, WM_SETTEXT, 0,
(LPARAM)TEXT("Registered for USB device notification...n"));
break;
case WM_SETFOCUS:
SetFocus(hEditWnd);
break;
case WM_SIZE:
// Make the edit control the size of the window's client area.
MoveWindow(hEditWnd,
0, 0, // starting x- and y-coordinates
LOWORD(lParam), // width of client area
HIWORD(lParam), // height of client area
TRUE); // repaint window
break;
case WM_DEVICECHANGE:
{
//
// This is the actual message from the interface via Windows messaging.
// This code includes some additional decoding for this particular device type
// and some common validation checks.
//
// Note that not all devices utilize these optional parameters in the same
// way. Refer to the extended information for your particular device type
// specified by your GUID.
//
// Output some messages to the window.
UsbController *pusbctl;
switch (wParam)
{
case DBT_DEVICEARRIVAL:
msgCount++;
pusbctl = UsbController::instance();
pusbctl->signalDeviceArrival();
break;
case DBT_DEVICEREMOVECOMPLETE:
msgCount++;
pusbctl = UsbController::instance();
pusbctl->signalDeviceRemoval();
break;
case DBT_DEVNODES_CHANGED:
msgCount++;
pusbctl = UsbController::instance();
pusbctl->signalDeviceAddedRemoved();
break;
default:
msgCount++;
break;
}
}
break;
default:
// Send all other messages on to the default windows handler.
lRet = DefWindowProc(__hWnd, message, wParam, lParam);
break;
}
return lRet;
}
BOOL DoRegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND __hWnd, HDEVNOTIFY *hDeviceNotify)
{
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
//NotificationFilter.dbcc_devicetype = DEVICE_NOTIFY_ALL_INTERFACE_CLASSES;
NotificationFilter.dbcc_classguid = InterfaceClassGuid;
*hDeviceNotify = RegisterDeviceNotification(
__hWnd, // events recipient
&NotificationFilter, // type of device
DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
);
if ( NULL == *hDeviceNotify )
{
return FALSE;
}
return TRUE;
}
如果您阅读 MSDN 的文档,它会说:
检测介质插入或取出
当添加新设备或媒体(如 CD 或 DVD)并使其可用时,以及删除现有设备或媒体时,Windows 会向所有顶级窗口发送一组默认WM_DEVICECHANGE消息。您无需注册即可接收这些默认消息。有关默认发送哪些消息的详细信息,请参阅 RegisterDeviceNotification 中的"备注"部分。
注册设备通知功能
具有顶级窗口的任何应用程序都可以通过处理WM_DEVICECHANGE消息来接收基本通知。应用程序可以使用注册设备通知功能进行注册以接收设备通知。
...
DBT_DEVICEARRIVAL和DBT_DEVICEREMOVECOMPLETE事件会自动广播到端口设备的所有顶级窗口。因此,没有必要为端口调用 RegisterDeviceNotification,如果dbch_devicetype成员DBT_DEVTYP_PORT,则该函数将失败。
DEV_BROADCAST_HDR结构
DBT_DEVTYP_PORT 0x00000003端口设备(串行或并行)。此结构是DEV_BROADCAST_PORT结构。
USB 设备不是串行/并行端口。 它是一个设备接口(DBT_DEVTYP_DEVICEINTERFACE
)。 默认情况下,不会为DBT_DEVTYP_DEVICEINTERFACE
设备发送DBT_DEVICEARRIVAL
/DBT_DEVICEREMOVECOMPLETE
。 如果你想要它们,你必须使用RegisterDeviceNotification()
来请求它们。
看起来您的代码基于以下 MSDN 示例:
注册设备通知
在该代码中,WceusbshGUID
被定义为 {25dbce51-6c8f-4a72-8a6d-b54c2b4fc835}
,它被注释为 USB 串行主机 PnP 驱动程序的类 guid。 根据此 MSDN 页面:
可供供应商使用的系统定义的设备安装程序类
该 guid 是 Windows CE USB ActiveSync 设备的类 GUID(与代码中使用的 Wceusb...
前缀更一致)。 在同一页上还{88BAE032-5A81-49f0-BC3D-A4FF138216D6}
USB 设备(不属于其他类的所有 USB 设备)。
以下代码项目文章:
检测硬件插入和/或移除
提及 USB 原始设备的{a5dcbf10-6530-11d2-901f-00c04fb951ed}
。 相同的 guid 在 MSDN 上记录为 GUID_DEVINTERFACE_USB_DEVICE
(其命名可能可以追溯到 XP 之前的时代,当时类 guid 和接口 guid 的命名没有很好地分开)。
因此,使用特定类 guid 调用RegisterDeviceNotification()
时,请确保它是正确的类 guid,因为你将仅获取该特定类型设备的设备事件。 您的 USB 设备使用的类 guid 可能与您正在注册的类 guid 不同,这就是您没有获得预期设备事件的原因。
如果要检测任何 USB 设备,而不考虑其类 guid(并且定义了多个 USB 类 GUID),则可以在调用 RegisterDeviceNotification()
时使用 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES
标志,则类 guid 将被忽略。在DBT_DEVICEARRIVAL
和DBT_DEVICEREMOVECOMPLETE
消息中(假设你现在能够获取它们),报告的dbcc_classguid
将告诉你实际的类 guid,报告的dbcc_name
将以\?USB:
前缀开头。
最后一件事 - 只有在应用程序已在运行时插入/移除 USB 设备时,您才会收到DBT_DEVICE...
消息。 若要检测应用启动时 USB 设备是否已插入,必须使用 SetupAPI 函数(SetupDiGetClassDevs()
、SetupDiEnumDeviceInterfaces()
、SetupDiGetDeviceInterfaceDetail()
等)来枚举可用设备。