首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

不需 DLL 也能運作的全域鉤子:WH_KEYBOARD_LL 與 WH_MOUSE_LL 實戰

     編輯:VC 教學

前言

許多開發者認為全域鉤子一定要依賴 DLL 才能將程式碼注入其他處理序。實際上,Windows 提供了四種特殊的鉤子類型——WH_JOURNALPLAYBACK、WH_JOURNALRECORD、WH_KEYBOARD_LL、WH_MOUSE_LL——它們的回呼函數會在「安裝鉤子的執行緒」內執行,因此完全不需要 DLL 就能運作。本文將以低階鍵盤鉤子(WH_KEYBOARD_LL)為例,示範如何在單一執行檔中實作全域鍵盤監聽。

為什麼不需要 DLL?

根據 MSDN 文件,這四種鉤子的描述皆為:「This hook is called in the context of the thread that installed it.」(鉤子函數在安裝它的執行緒上下文中被呼叫)。換句話說,系統不會將鉤子程式碼映射到目標處理序的位址空間,而是直接在安裝者執行緒中呼叫回呼。因此:

  • 無法達到程式碼注入效果(無法在別處理序執行自訂邏輯)。
  • 不需建立獨立 DLL,大幅簡化部署與除錯。
  • 適合用於全域熱鍵、輸入記錄、UI 自動化等單一處理序即可完成的場景。

完整程式碼範例:低階鍵盤鉤子

// kbhook.cpp
#define _WIN32_WINNT 0x0400
#define STRICT
#define WIN32_LEAN_AND_MEAN

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

DWORD g_main_tid = 0;
HHOOK g_kb_hook = 0;

BOOL CALLBACK con_handler(DWORD)
{
    PostThreadMessage(g_main_tid, WM_QUIT, 0, 0);
    return TRUE;
}

LRESULT CALLBACK kb_proc(int code, WPARAM w, LPARAM l)
{
    if (code < 0) return CallNextHookEx(g_kb_hook, code, w, l);

    PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)l;
    const char *info = NULL;

    if (w == WM_KEYDOWN)         info = "key dn";
    else if (w == WM_KEYUP)      info = "key up";
    else if (w == WM_SYSKEYDOWN) info = "sys key dn";
    else if (w == WM_SYSKEYUP)   info = "sys key up";

    if (info)
        printf("%s - vkCode [%04x], scanCode [%04x]\n", info, p->vkCode, p->scanCode);

    return CallNextHookEx(g_kb_hook, code, w, l);
}

int main(void)
{
    g_main_tid = GetCurrentThreadId();
    SetConsoleCtrlHandler(con_handler, TRUE);

    // 注意:hMod 必須傳 GetModuleHandle(NULL),不可為 NULL
    g_kb_hook = SetWindowsHookEx(WH_KEYBOARD_LL, kb_proc, GetModuleHandle(NULL), 0);
    if (!g_kb_hook) {
        fprintf(stderr, "SetWindowsHookEx failed with error %d\n", GetLastError());
        return 1;
    }

    // 訊息迴圈是必要的,讓系統能呼叫回呼函數
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    UnhookWindowsHookEx(g_kb_hook);
    return 0;
}

關鍵點說明

  1. hMod 參數:必須傳遞 GetModuleHandle(NULL)(目前模組控制代碼),若傳 NULL 會導致 SetWindowsHookEx 失敗。
  2. 訊息迴圈:低階鉤子依賴執行緒的訊息佇列來派送回呼,因此主執行緒必須跑 GetMessage 迴圈。
  3. 控制台關閉處理:使用 SetConsoleCtrlHandler 捕捉 Ctrl+C,發送 WM_QUIT 讓迴圈正常退出並釋放鉤子。
  4. 回呼簽名:LRESULT CALLBACK kb_proc(int nCode, WPARAM wParam, LPARAM lParam),lParam 指向 KBDLLHOOKSTRUCT 結構,包含虛擬鍵碼、掃描碼等資訊。

適用場景與限制

  • 適用:全域熱鍵、鍵盤/滑鼠宏錄製、無障礙輔助工具、UI 測試自動化。
  • 不適用:需要在目標處理序內執行程式碼(如 API Hooking、記憶體修改)、跨 Session 隔離(UAC 高權限下無法捕捉低權限處理序輸入)。

結語

善用 WH_KEYBOARD_LL 與 WH_MOUSE_LL 可在無需 DLL 的情況下實現全域輸入監聽,程式碼更簡潔、部署更輕鬆。若需更深層的處理序間介入,仍須採用傳統的 DLL 注入式鉤子(WH_KEYBOARD、WH_MOUSE 等)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved