昨天我遇到了我見過的最奇怪的問題。 我寫了一個應該得到USB插頭通知的模塊。 爲此,我創建了一個虛擬窗口,並使用一些界面的GUID
將其註冊到設備更改通知。PeekMessage觸發WndProc回調
當調用PeekMessage
時會發生奇怪的錯誤。 在這一點上,有些爲什麼,窗口的WndProc回調被調用,只有當被偷看的消息是WM_DEVICECHANGE
(我們註冊到上面的代碼)。 在任何其他消息上,DispatchMessage按預期觸發回調。
代碼:
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = guid;
not = RegisterDeviceNotification(
hWnd, // events recipient
&NotificationFilter, // type of device
DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
);
爲了將這個模塊的我的代碼是異步的休息,使用Reactor
設計模式與Windows Events
,並按照計算器社區成員的意見,我爲了納入MsgWaitForMultipleObjects
以偵聽事件和Windows消息。
代碼:
for (;;)
{
dwRetval = MsgWaitForMultipleObjects(cntEvents, arrEvents, FALSE, INFINITE, QS_ALLINPUT);
switch (dwRetval)
{
case WAIT_FAILED:
// failed. TODO: status
break;
// TODO: handle abandoned.
default:
if (dwRetval == cntEvents)
{
// Message has popped.
BOOL x = PeekMessage(&tMsg, hWnd, 0, 0, PM_REMOVE); <---- WM_DEVICECHANGE triggers the callback
if (x)
{
TranslateMessage(&tMsg);
DispatchMessage(&tMsg);
}
}
else if (dwRetval < cntEvents)
{
// event signaled
}
else
{
// TODO: status. unexpected.
return FALSE; // unexpected failure
}
break;
}
}
我拆解的代碼,並比較寄存器任何調用之前NtUserPeekMessage
成功的拒收訊息登記冊:
RAX = 00000059A604EFB0 RBX = 0000000000000000 RCX = 00000059A604EF18
RDX = 0000000000070C62 RSI = 00000059A604EF18 RDI = 0000000000070C62
R8 = 0000000000000000 R9 = 0000000000000000 R10 = 00007FF71A65D800
R11 = 0000000000000246 R12 = 0000000000000000 R13 = 0000000000000000
R14 = 0000000000000000 R15 = 0000000000000000 RIP = 00007FF954562AA1
RSP = 00000059A604EE70 RBP = 0000000000000000 EFL = 00000200
寄存器未知回調觸發電話:
RAX = 00000059A604EFB0 RBX = 0000000000000000 RCX = 00000059A604EF18
RDX = 0000000000070C62 RSI = 00000059A604EF18 RDI = 0000000000070C62
R8 = 0000000000000000 R9 = 0000000000000000 R10 = 00007FF71A65D800
R11 = 0000000000000246 R12 = 0000000000000000 R13 = 0000000000000000
R14 = 0000000000000000 R15 = 0000000000000000 RIP = 00007FF954562AA1
RSP = 00000059A604EE70 RBP = 0000000000000000 EFL = 00000200
寄存器完全一樣!(無參數傳遞到堆棧,64位..)
在這兩種情況下(奇怪的錯誤和流量預計)我走進在NtUserPeekMessage
,事實證明,在WndProc
回調從纔會觸發內部系統調用!
00007FF954562A80 mov r10,rcx
00007FF954562A83 mov eax,1003h
00007FF954562A88 syscall
我找不到MSDN上的任何文檔或在互聯網上解釋現象。
我真的很喜歡一些幫助, 在此先感謝。
WM_DEVICECHANGE總是發送,絕不會發布。 SendMessage()不會任意中斷程序的UI線程,這會導致可怕的重入問題。它需要等待,直到它得到一個線程閒置的信號,並直接調用窗口過程不會引起任何麻煩。 Get/PeekMessage()是那個信號。 – 2015-03-13 13:43:10
@HansPassant非常感謝你:) – CodeNinja 2015-03-13 13:58:50