使用Windows Hooks创建自定义C++热键

3

我希望能够创建一些自定义热键,在任何程序中,通过特定的键组合来调用不同的程序。我研究了如何使用挂钩,并得到了以下示例。

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
    // Set windows hook
    HHOOK keyboardHook = SetWindowsHookEx(
        WH_KEYBOARD_LL,
        keyboardHookProc,
        hInstance,
        0);
    MessageBox(NULL, "Press OK to stop hot keys", "Information", MB_OK);
    return 0;
}

我想让这个程序在后台运行,所以不想使用消息框,我尝试使用循环,但是没有任何一种方法能够像消息框那样成功地运行。你有什么想法吗?

4个回答

6

下面这段好的代码是一个热键应用程序,它在后台监听 CTRL-y 组合键,您可以修改或添加任何更多的组合键到应用程序中。当隐藏时使用 CTRL-q 来退出应用程序。

如果您希望完全隐藏控制台窗口,则在 main() 中取消注释此行://ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false)。享受吧。

if (CTRL_key !=0 && key == 'y' )
{
   MessageBox(NULL, "CTRL-y was pressed\nLaunch your app here", "H O T K E Y", MB_OK); 
   CTRL_key=0;
}

完整的代码清单:

#define _WIN32_WINNT 0x0400
#pragma comment( lib, "user32.lib" )

#include <windows.h>
#include <stdio.h>

HHOOK hKeyboardHook;

__declspec(dllexport) LRESULT CALLBACK KeyboardEvent (int nCode, WPARAM wParam, LPARAM lParam)
{
    DWORD SHIFT_key=0;
    DWORD CTRL_key=0;
    DWORD ALT_key=0;

    if  ((nCode == HC_ACTION) &&   ((wParam == WM_SYSKEYDOWN) ||  (wParam == WM_KEYDOWN)))      
    {
        KBDLLHOOKSTRUCT hooked_key =    *((KBDLLHOOKSTRUCT*)lParam);
        DWORD dwMsg = 1;
        dwMsg += hooked_key.scanCode << 16;
        dwMsg += hooked_key.flags << 24;
        char lpszKeyName[1024] = {0};
        lpszKeyName[0] = '[';

        int i = GetKeyNameText(dwMsg,   (lpszKeyName+1),0xFF) + 1;
        lpszKeyName[i] = ']';

        int key = hooked_key.vkCode;

        SHIFT_key = GetAsyncKeyState(VK_SHIFT);
        CTRL_key = GetAsyncKeyState(VK_CONTROL);
        ALT_key = GetAsyncKeyState(VK_MENU);

        if (key >= 'A' && key <= 'Z')   
        {

            if  (GetAsyncKeyState(VK_SHIFT)>= 0) key +=32;

            if (CTRL_key !=0 && key == 'y' )
            {
               MessageBox(NULL, "CTRL-y was pressed\nLaunch your app here", "H O T K E Y", MB_OK); 
               CTRL_key=0;
            }

            if (CTRL_key !=0 && key == 'q' )
            {
                MessageBox(NULL, "Shutting down", "H O T K E Y", MB_OK); 
               PostQuitMessage(0);
            }




            printf("key = %c\n", key);

            SHIFT_key = 0;
            CTRL_key = 0;
            ALT_key = 0;

        }

        printf("lpszKeyName = %s\n",  lpszKeyName );
    }
    return CallNextHookEx(hKeyboardHook,    nCode,wParam,lParam);
}

void MessageLoop()
{
    MSG message;
    while (GetMessage(&message,NULL,0,0)) 
    {
        TranslateMessage( &message );
        DispatchMessage( &message );
    }
}

DWORD WINAPI my_HotKey(LPVOID lpParm)
{
    HINSTANCE hInstance = GetModuleHandle(NULL);
    if (!hInstance) hInstance = LoadLibrary((LPCSTR) lpParm); 
    if (!hInstance) return 1;

    hKeyboardHook = SetWindowsHookEx (  WH_KEYBOARD_LL, (HOOKPROC) KeyboardEvent,   hInstance,  NULL    );
    MessageLoop();
    UnhookWindowsHookEx(hKeyboardHook);
    return 0;
}

int main(int argc, char** argv)
{
    HANDLE hThread;
    DWORD dwThread;

    hThread = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)   my_HotKey, (LPVOID) argv[0], NULL, &dwThread);

    //ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false);

    if (hThread) return WaitForSingleObject(hThread,INFINITE);
    else return 1;

}

3

除非绝对必要,否则不要使用Windows钩子。在您的情况下,您可以通过调用RegisterHotKey函数安装热键,这样做更简单,因为您不需要开发进程间通信(即在您的具有钩子过程的DLL和主应用程序之间)。


1
但作为开发人员,我不明白为什么 MessageBox 能够工作而 while(true){} 却不能?我一定会研究 RegisterHotKey,只是出于好奇想知道。 - user781439
@user781439:因为MessageBox在内部运行一个消息循环。 - Yakov Galka

1
"

此钩子在安装它的线程上下文中被调用。通过向安装钩子的线程发送消息来进行调用。因此,安装钩子的线程必须拥有一个消息循环。" - LowLevelKeyboardProc MSDN

您需要创建一个不可见的窗口。

"

0

我不确定,但是你可能需要一个完整的消息循环而不是普通的空无限循环,尤其是如果你像ybungalobill建议的那样使用RegisterHotKey函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接