1. 程式人生 > 實用技巧 >第21章:Windows訊息鉤取

第21章:Windows訊息鉤取

Windows向用戶提供GUI,以事件驅動的方式工作.

常規的Windows訊息流:

發生鍵盤輸入事件時,WM_KeyDown訊息被新增到OS訊息佇列.

OS判斷哪個應用程式發生了事件,然後從OS訊息佇列重取出訊息,新增到應用程式的訊息佇列中.

應用程式監視自身的訊息佇列,發現新新增的訊息後,呼叫相應的事件處理程式處理.

SetWindowsHookEx()可以實現訊息鉤取.

第一個引數是鉤子型別,例如可以為 WH_KeyBoard ,鍵盤鉤子.

第二個引數是鉤子過程,是由作業系統呼叫的回撥函式,說白了就是處理程式.

第三個引數是鉤子過程所屬的DLL控制代碼,因為鉤子過程需要存在於某個DLL內部

.

第四個引數代表想要影響的程序,若為0則影響所有程序.

可執行程式載入並安裝鉤子 -> 其它程序發生硬碟輸入事件 -> OS將Dll檔案載入到相應得程序記憶體 -> 呼叫回撥函式

重點看一下HookMain.exe檔案的原始碼.

#include "stdio.h"
#include "conio.h"
#include "windows.h"

#define    DEF_DLL_NAME        "KeyHook.dll"
#define    DEF_HOOKSTART        "HookStart"
#define    DEF_HOOKSTOP        "HookStop"

typedef 
void (*PFN_HOOKSTART)(); typedef void (*PFN_HOOKSTOP)(); //宣告一個返回值為NULL的函式指標
void main() { HMODULE hDll = NULL; PFN_HOOKSTART HookStart = NULL; //定義函式指標 PFN_HOOKSTOP HookStop = NULL;    //函式指標賦值為NULL,避免成為野指標 char ch = 0; // 載入 KeyHook.dll hDll = LoadLibraryA(DEF_DLL_NAME);
if( hDll == NULL ) { printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError()); return; } // 獲取匯出函式地址 HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART); // 將函式返回值轉換為自定義的型別 HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP); // 開始鉤取 HookStart();
printf("press 'q' to quit!\n"); while( _getch() != 'q' ) ; // 終止鉤取 HookStop(); // 解除安裝KeyHook.dll FreeLibrary(hDll); }
#include "stdio.h"
#include "windows.h"

#define DEF_PROCESS_NAME   "notepad.exe"

HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
    switch( dwReason )
    {
        case DLL_PROCESS_ATTACH:
            g_hInstance = hinstDLL;
            break;

        case DLL_PROCESS_DETACH:
            break;    
    }

    return TRUE;
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)    //此處猜測是覆寫了KeyBoardProc()函式
{
    char szPath[MAX_PATH] = {0,};
    char *p = NULL;

    if( nCode >= 0 )
    {
        // bit 31 : 0 => press, 1 => release
        if( !(lParam & 0x80000000) )
        {
            GetModuleFileNameA(NULL, szPath, MAX_PATH);
            p = strrchr(szPath, '\\');     //返回字串中從右側搜尋首次出現字元 \\ 的地址。

           
            if( !_stricmp(p + 1, DEF_PROCESS_NAME) )    //字串相等時,返回值為0
                return 1;                               //程式為notepad.exe時,直接返回1即不將鍵盤按鍵資料傳到應用程式
        }
    }

    // 若當前程序名稱不是notepad.exe則呼叫 CallNextHookEx() 函式,將訊息傳遞給應用程式或下一個鉤子
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

#ifdef __cplusplus
extern "C" {
#endif
    __declspec(dllexport) void HookStart()             //declspec是針對編譯器的關鍵字,指出相應函式為匯出函式
    {
        g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
    }

    __declspec(dllexport) void HookStop()
    {
        if( g_hHook )
        {
            UnhookWindowsHookEx(g_hHook);
            g_hHook = NULL;
        }
    }
#ifdef __cplusplus
}
#endif

安裝好鍵盤鉤子之後,無論哪個程序發生鍵盤輸入事件,OS會將KeyHook.dll注入到相應程序,而此程序發生鍵盤事件時也會首先呼叫執行KeyHook.KeyboardProc()

Printf()函式看著簡單,其實挺複雜的.