【发布时间】:2023-03-13 08:44:01
【问题描述】:
我正在做一个使用一键通键的语音聊天应用程序。我已经做了一个钩子,所以它也会在应用程序之外注册一键通。
HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC)pushtotalk,0,0);
LRESULT CALLBACK pushtotalk(int key, WPARAM wParam,LPARAM lParam) {
if (key < 0) {
return (CallNextHookEx(hook,key,wParam,lParam));
}
else if (connected) {
KBDLLHOOKSTRUCT* kbdll = (KBDLLHOOKSTRUCT*)lParam;
if (kbdll ->vkCode == 75 && wParam == WM_KEYDOWN) {
MessageBox(mainhWnd,"KEYSTART","KEYSTART",0);
}
else if (kbdll ->vkCode == 75 && wParam == WM_KEYUP) {
MessageBox(mainhWnd,"KEYSTOP","KEYSTOP",0);
}
}
return (CallNextHookEx(hook,key,wParam,lParam));
}
问题;
1) 有时,(例如应用程序中第一次执行 proc),proc 在继续之前会导致系统冻结 5 秒。为什么?
2) 该挂钩仅适用于在我的应用程序启动之前启动的进程,如果我在启动应用程序后启动文本程序,则挂钩不会注册。有解决办法吗?
3) 如果我按住键约 3 秒,很多 MessageBoxes 显然会显示,但在那之后,proc 将永远不会注册另一个被按下的键,所以我想我不知何故与钩子链断开了连接?
干杯
编辑:这是应用程序的主要消息循环
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch(message) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_MENU_EXIT:
SendMessage(hWnd,WM_CLOSE,0,0);
break;
case ID_MENU_PREFERENCES:
voiceManager->send((void*) "1");
break;
case ID_BUTTON_CONNECT:
onConnect(hWnd);
break;
case ID_BUTTON_DISCONNECT:
onDisconnect(hWnd);
break;
case ID_BUTTON_SEND:
onSendText(hWnd);
break;
default:
break;
}
break;
case SOCKET_TCP:
switch (lParam) {
case FD_READ:
{
// Disable repeated FD_READ call while we process message
WSAAsyncSelect(wParam,hWnd,SOCKET_TCP, FD_WRITE | FD_ACCEPT | FD_CLOSE);
// first four bytes is packet size
// second four bytes are used to identify type of msg
char* psize = (char*)malloc(5);
char* ptype = (char*)malloc(5);
psize[4] = '\0';
ptype[4] = '\0';
recv(wParam,psize,4,0);
recv(wParam,ptype,4,0);
// allocate memory for the buffer
int size_to_recv = atoi(psize);
char* textbuff = (char*)malloc(size_to_recv);
// receive
int i = size_to_recv;
while (i > 0) {
int read = recv(wParam,textbuff,i,0);
i = i - read;
}
// handle msg depending on type
switch(identifyMsg(ptype)) {
case 1:
// handle 'text' msg
onReadText(hWnd,textbuff);
break;
case 2:
// handle 'name' msg
onReadName(hWnd,textbuff);
break;
case 3:
// handle 'list' msg
onReadList(hWnd,textbuff);
break;
case 4:
// handle 'remv' msg
onReadRemv(hWnd,textbuff,size_to_recv);
break;
case 5:
// handle 'ipad' msg -- add ip
voiceManager->addParticipant(inet_addr(textbuff));
break;
case 6:
// handle 'iprm' msg -- remove ip
voiceManager->removeParticipant(inet_addr(textbuff));
break;
default:
break;
}
// re-enable FD_READ
WSAAsyncSelect(wParam,hWnd,SOCKET_TCP, FD_WRITE | FD_ACCEPT | FD_READ | FD_CLOSE);
// free resources
free(psize);
free(ptype);
free(textbuff);
break;
}
case FD_WRITE:
break;
case FD_CONNECT:
break;
case FD_CLOSE:
onDisconnect(hWnd);
break;
default:
break;
}
break;
case WM_PAINT:
paintText(hWnd);
break;
case WM_DESTROY:
shutdownConnection(hWnd);
// reset window procs
SetWindowLong(GetDlgItem(hWnd,ID_EDIT_SEND), GWL_WNDPROC,(LONG) OriginalEditProc);
SetWindowLong(GetDlgItem(hWnd,ID_EDIT_IP), GWL_WNDPROC,(LONG) OriginalEditProc);
PostQuitMessage(0);
return 0;
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
default:
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK sendEditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_CHAR) {
if (wParam == VK_RETURN) {
onSendText(GetParent(hWnd));
return 0;
}
}
if (message == WM_KEYUP || message == WM_KEYDOWN) {
if (wParam == VK_RETURN) {
return 0;
}
}
return CallWindowProc(OriginalEditProc, hWnd, message, wParam,lParam);
}
其中 sendEditProc 是一个子类/超类,旨在在编辑控件“发送”内截取“输入”键 这有帮助吗?
这是消息循环;这是标准,所以没有什么花哨的东西会出错 afaik :)
while (GetMessage(&msg, NULL,0,0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
【问题讨论】:
-
不,因为我使用了 WH_KEYBOARD_LL,所以我将 proc 放在了主应用程序中。
-
如果你把MessageBox调用注释掉(或者用MessageBeep之类的东西代替),你还有同样的问题吗?
-
是的,同样的问题又出现了
-
请编辑您的问题以包含您的应用程序的消息循环。 (如果您没有,那可能就是问题所在。)在您发布的代码中,我没有看到任何突如其来的东西——消息循环可能有助于重现问题。
-
今天早上偶然发现了这个 - blogs.msdn.com/b/alejacma/archive/2010/10/14/…
标签: c++ winapi input keyboard hook