Windows程式設計__孫鑫C++Lesson20《HOOK和資料庫訪問》
本節要點:
1.Hook編程回顧Windows訊息傳遞機制
2.安裝內部鉤子
3.安裝全域鉤子
4.資料庫訪問技術初步瞭解
//*********************************************************************************************
1.Hook編程回顧Windows訊息傳遞機制
Windows訊息機制如所示:
2.安裝內部鉤子:和一個指定線程相關的鉤子
(1)hook鏈(hook chain) :多個hook過程,形成一個hook鏈,最後安裝的hook總是在鏈的前面。hook鏈允許對訊息進行多個處理。
(2)安裝鉤子使用函數SetWindowsHookEx,該函數原型為:
HHOOK SetWindowsHookEx(
int idHook, // hook type
HOOKPROC lpfn, // hook procedure
HINSTANCE hMod, // handle to application instance
DWORD dwThreadId // thread identifier
);
parameter 1 :idHook參數指定為安裝的鉤子類型。
parameter 2 :lpfn 指向鉤子過程的一個函數指標。如果dwThread參數被設定為0或者被指定為不同進程建立的線程ID的話,那麼這個參數必須必須設定為指向一個動態連結程式庫(DLL)中的鉤子過程。否則,這個參數可以指向一個和當前進程相關的鉤子過程。
parameter 3 :hMod 參數指定了由lpfn參數指定的鉤子過程的Dll控制代碼,如果dwThread參數被指定為當前進程建立的某個線程的ID並且鉤子過程的執行代碼和當前線程相關的話,這個參數必須設定為NULL。
parameter 4 :dwThreadId參數指定了鉤子過程和哪個線程相關,
當參數指定為0時鉤子過程將和當前調用線程處在同一個案頭上的所有的正在啟動並執行線程相關。
注意三點:
第一:全域鉤子的必須是動態連結程式庫中的鉤子函數。
第二:在調用鉤子過程後,應該調用 CallNextHookEx函數將訊息傳遞到下一個鉤子過程,
除非你不想讓其他其他程式接受這些訊息,但是這樣做可能引起其他程式安裝的鉤子過程出現錯誤的結果。
第三:在程式結束前應該調用UnhookWindowsHookEx函數來釋放和鉤子相關的資源。
(3)鉤子函數編寫 根據不同類型的鉤子編寫不同類型的鉤子過程。
比如滑鼠鉤子的構成函數:
LRESULT CALLBACK MouseProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
);
關於該MouseProc過程的傳回值說明:
如果nCode小於0,鉤子過程必須返回CallNextHookEx函數的傳回值;
如果nCode大於或者等於0並且鉤子過程沒有處理這個訊息的話,強烈建議調用CallNextHookEx函數並返回其值,
否則其他的安裝了滑鼠鉤子的程式將不能正確接收這個訊息可能引起錯誤的結果。如果鉤子過程處理了這個訊息,
那麼返回一個非零值可以阻止系統繼續傳遞該訊息給其他程式。
比如鍵盤鉤子過程的函數原型為:
LRESULT CALLBACK KeyboardProc(
int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam // keystroke-message information
);
對於鍵盤鉤子過程擷取的訊息中,wParam表示了按鍵的虛索引值,利用這個虛索引值可以判斷按鍵。
虛索引值是一組宏(選中虛索引值然後轉到定義可查看),其中'0' 到'9'以及'A' 到'Z'的虛索引值和ASCii碼相同未作宏定義;要判斷是否同步選取多個鍵由參數lParam 來判斷。
(4)對使用者ALT+F4鍵的判斷 關鍵是如何判斷ALT鍵按下
通過鍵盤訊息的lParam參數可以判斷該資訊,鍵盤按鍵訊息的lParam參數圖解如下(由MSDN提供):
如何判斷是通過移位和與操作實現的,具體過程可參見我繪製的:
(5)內部鉤子程式主要代碼(對話方塊程式自動添加的代碼未列出)如下(注意按F6退出程式):
//*****************************************************************
[cpp]
view plaincopyprint?
- // InnerHookDlg.cpp
- HHOOK g_hKeyHook=NULL;
- HHOOK g_hMouseHook=NULL;
- HWND g_hWnd=NULL;//儲存控制代碼
- LRESULT CALLBACK MouseProc(
- int nCode, // hook code
- WPARAM wParam, // message identifier
- LPARAM lParam // mouse coordinates
- )
- {
- return 1;
-
- }
- LRESULT CALLBACK KeyboardProc(
- int code, // hook code
- WPARAM wParam, // virtual-key code
- LPARAM lParam // keystroke-message information
- )
- {
- /*
- if(VK_SPACE==wParam || VK_RETURN==wParam )//屏蔽空格、斷行符號鍵
- if( VK_F4==wParam && (1==( lParam>>29 & 1 )))//屏蔽Alt+F4鍵
- return 1;
- else
- return CallNextHookEx(hHook,code,wParam,lParam);//繼續傳遞訊息到hook鏈
- */
- if(VK_F6==wParam)//F6鍵退出
- {
- ::SendMessage(g_hWnd,WM_CLOSE,0,0);//關閉對話方塊
- UnhookWindowsHookEx(g_hKeyHook);
- UnhookWindowsHookEx(g_hMouseHook);
- }
- return 1;
- }
- BOOL CInnerHookDlg::OnInitDialog()
- {
- ...
- // TODO: Add extra initialization here
- g_hWnd=m_hWnd;//擷取對話方塊控制代碼
- g_hMouseHook = SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());//設定滑鼠鉤子 屏蔽當前進程的訊息
- g_hKeyHook = SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,NULL,GetCurrentThreadId());//設定鍵盤鉤子
- return TRUE; // return TRUE unless you set the focus to a control
- }
//******************************************************************
3.安裝全域鉤子:和所有線程相關的鉤子
全域鉤子過程必須為一個動態連結程式庫(DLL)中的鉤子過程。首先要編寫一個鉤子過程的動態連結程式庫Hook.dll,
然後編寫一個全域鉤子HookTest,調用動態連結程式庫中的鉤子過程。
(1)如何擷取動態連結程式庫中函數模組控制代碼
在動態連結程式庫中編寫鉤子過程時擷取動態連結程式庫的模組的控制代碼的兩種方法:
方法一:使用DllMain函數傳遞的參數擷取控制代碼
方法二:使用GetModuleHandle函數,函數原型為:HMODULE GetModuleHandle(
LPCTSTR lpModuleName // module name 預設檔案尾碼為dll
);
(2)如何儲存當前運行程式的控制代碼
在程式中定義全域變數HWND g_hWnd=NULL;當程式切換到其他視窗後按下自訂的關閉鍵F6無法關閉本程式。
解決方案時使用共用的資料區段,將視窗控制代碼定義為:
#pragma data_seg("MySeg")
HWND g_hWnd=NULL;
#pragma data_seg()
設定節的讀寫及共用屬性:
方法一:在源檔案中完整書寫為
//******************************************************************
#pragma data_seg("MySeg")
HWND g_hWnd=NULL;//初始化之後才會放到segment中 查看命令為 dumpbin -headers Hook.dll
#pragma data_seg()
//#pragma comment(linker,"/section:MySeg,RWS") //設定集的讀寫共用屬性
//******************************************************************
方法二:在模組定義檔案中聲明為:
//******************************************************************
SEGMENTS
MySeg read write shared
//******************************************************************
利用dumpbin.exe 查看程式資料區段使用命令 dumpbin -headers Hook.dll
設定好MySeg資料區段的讀寫和共用屬性後,利用dumpbin.exe查看結果如:
//******************************************************************
#pragma指令還有其他用法,可參見MSDN。
更多關於資料共用的問題可參見部落格:http://www.cnblogs.com/ahuo/archive/2008/04/08/1143488.html
(3)鉤子Dll以及鉤子測試程式的代碼如下:
//******************************************************************
[cpp]
view plaincopyprint?
- //Hook.dll編寫的代碼
- //Hook.def
- LIBRARY Hook
- EXPORTS
- SetHook
- SEGMENTS
- MySeg read write shared
- //******************************************************************
- //Hook.cpp
- #include <Windows.h>
- HHOOK g_hMouseHook=NULL;//滑鼠鉤子過程控制代碼
- HHOOK g_hKeyHook=NULL;//鍵盤鉤子過程控制代碼
- #pragma data_seg("MySeg")
- HWND g_hWnd=NULL;//初始化之後才會放到segment中 查看命令為 dumpbin -headers Hook.dll
- #pragma data_seg()
- //#pragma comment(linker,"/section:MySeg,RWS") //設定集的讀寫共用屬性 如果在模組定義檔案中已經說明時則這裡不需要
- //擷取動態連結程式庫模組控制代碼方法一 使用DllMain
- /*HINSTANCE g_hInst=NULL;//全域執行個體控制代碼變數
- //動態連結程式庫入口函數
- BOOL WINAPI DllMain(
- HINSTANCE hinstDLL, // handle to the DLL module
- DWORD fdwReason, // reason for calling function
- LPVOID lpvReserved // reserved
- )
- {
- g_hInst=hinstDLL;//擷取動態連結程式庫控制代碼
- return true;
- }*/
- LRESULT CALLBACK MouseProc(
- int nCode, // hook code
- WPARAM wParam, // message identifier
- LPARAM lParam // mouse coordinates
- )
- {
- return true;
- }
- LRESULT CALLBACK KeyboardProc(
- int code, // hook code
- WPARAM wParam, // virtual-key code
- LPARAM lParam // keystroke-message information
- )
- {
- if(VK_F6==wParam)
- {
- ::SendMessage(g_hWnd,WM_CLOSE,0,0);
- UnhookWindowsHookEx(g_hMouseHook);
- UnhookWindowsHookEx(g_hKeyHook);
- }
- if('A'==wParam)
- MessageBox(g_hWnd,"按下了a!","鉤子測試",MB_OK);
- return true;
- }
- void SetHook(HWND hWnd)
- {
- g_hWnd=hWnd;//聲明為全域時 切換到其他進程時將會發生改變
- //擷取動態連結程式庫模組控制代碼方法二 使用函數GetModuleHandle
- g_hMouseHook=SetWindowsHookEx(WH_MOUSE,MouseProc,GetModuleHandle("Hook"),0);//安裝滑鼠鉤子
- g_hKeyHook=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,GetModuleHandle("Hook"),0);//安裝鍵盤鉤子
- }
- //******************************************************************
- //鉤子測試的對話方塊程式
- //HookTest.cpp
- _declspec(dllimport) void SetHook(HWND hWnd);//匯入函數 注入加入Hook.lib和Hook.dll檔案到當前工程
- BOOL CHookTestDlg::OnInitDialog()
- {
- ...
- // TODO: Add extra initialization here
- //設定為頂層全屏視窗 使用者無法看到其他程式視窗
- int cxScreen,cyScreen;
- cxScreen=GetSystemMetrics(SM_CXSCREEN);
- cyScreen=GetSystemMetrics(SM_CYSCREEN);
- SetWindowPos(&wndTopMost,0,0,cxScreen,cyScreen,SWP_SHOWWINDOW);
- SetHook(m_hWnd);//啟動鉤子
- return TRUE;
- }
//******************************************************************
程式運行(注意按F6退出程式)效果如所示(當然這裡觀察不到鍵盤和滑鼠的屏蔽效果):
程式運行時360安全衛士提示警告資訊如所示:
//******************************************************************
4.資料庫訪問技術初步瞭解
(1)資料庫訪問技術 資料庫訪問技術介紹如所示:
(2)在vc中使用ADO訪問資料庫
首先要匯入ADO庫,使用一下語句:
#import "C:\Program Files\Common Files\System\ado\msado15.dll" no_namespace rename("EOF","rsEOF");
VC產生的檔案,tlh可看做標頭檔,tli檔案可看做源檔案。擷取資料庫連接字串的方便方式是建立一個txt檔案將尾碼改為udl,
然後開啟它就可選擇串連資料庫,選擇完畢後以txt形式開啟就可擷取資料庫連接字串。
由於系統安裝原因,這裡只是瞭解了一下,實驗在以後的過程中再做學習。
//*********************************************************************************************
本節小結:
通過本節的學習掌握了內部鉤子和全域鉤子的編寫,要編寫不同功能的鉤子可以採用不同的參數賦給建立鉤子的函數SetWindowsHookEx;
同時瞭解了資料庫訪問的幾種不同技術,注意在以後的編程過程中合理選擇資料庫的訪問技術。