Проблемы с клавиатурой Hook - PullRequest
6 голосов
/ 19 января 2011

Я делаю приложение для голосового чата, в котором используется клавиша PTT.Я сделал хук, чтобы он также регистрировал push-to-talk вне приложения.

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) Иногда (например, первое выполнение процедуры вприложение), процесс вызывает 5-секундное зависание системы, прежде чем продолжить.Почему?

2) Хук работает только с процессом, который был запущен до запуска моего приложения. Если я запускаю текстовую программу после запуска моего приложения, хуки не регистрируются.Есть ли какое-то решение для этого?

3) Если я удерживаю клавишу в течение ~ 3 секунд, очевидно, что появляется много MessageBoxes, но после этого процесс никогда не зарегистрирует нажатие другой клавиши, так что я думаю,Я как-то отключаюсь от цепочки хуков?

Приветствия

РЕДАКТИРОВАТЬ: Вот основной цикл сообщений для приложения

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 - это подкласс / суперкласс, предназначенный дляперехватывать клавиши ввода при нахождении внутри элемента управления редактирования «отправить» Помогает ли это?

Вот цикл сообщений;это стандарт, так что ничего сложного, что может пойти не так, как надо :))

while (GetMessage(&msg, NULL,0,0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

1 Ответ

3 голосов
/ 21 января 2011

Вы звоните CallNextHookEx слишком много раз. Если key < 0 return CallNextHookEx, в противном случае return 0.

Проблема, с которой вы сталкиваетесь, связана с повторениями клавиатуры, а методы MessageBox или MessageBeep являются очень и очень дорогими вызовами. Попробуйте этот тест:

HHOOK hHook;
BOOL bTalkEnabled = FALSE;

LRESULT CALLBACK pushtotalk(int key, WPARAM wParam, LPARAM lParam)
{
    if (key < 0)
        return CallNextHookEx(hHook, key, wParam, lParam);

    KBDLLHOOKSTRUCT* kbdll  = (KBDLLHOOKSTRUCT*)lParam;
    if (kbdll->vkCode == VK_F11)
    {
        BOOL bStarted = FALSE;
        BOOL bStopped = FALSE;

        if (wParam == WM_KEYDOWN)
        {
            if (!bTalkEnabled)
            {
                bStarted = TRUE;
                bTalkEnabled = TRUE;
            }
        }
        else if (wParam == WM_KEYUP)
        {
            if (bTalkEnabled)
            {
                bStopped = TRUE;
                bTalkEnabled = FALSE;
            }
        }

        if (bStarted)
            OutputDebugString(L"Pushed\r\n");
        if (bStopped)
            OutputDebugString(L"Released\r\n");
    }

    return 0;
}

Вы можете отслеживать строки отладки, запустив приложение под отладчиком (проверьте окно вывода), или вы можете получить DebugView и посмотреть его.

Обратите внимание, что я не проверяю connected, как вы. Вы не хотите выполнять эту проверку на крючке, делайте это вне крючка и используйте крючок только для определения, нажата ли клавиша или нет.

...