控制計算機使用研究論文

時間:2022-10-11 11:26:00

導語:控制計算機使用研究論文一文來源于網友上傳,不代表本站觀點,若需要原創文章可咨詢客服老師,歡迎參考。

控制計算機使用研究論文

基于WindowsNT/2000的應用系統中,一些關鍵的人機交互工作站,常需要了解并記錄所有操作人員操作計算機的情況。如:在工業控制領域,一些使用計算機對設備進行監視和控制的工作站,需要非常高的可靠性和安全性。在這些工作站上通常要求只能運行系統所要求的應用程序,不能運行與系統無關的程序,同時要求對計算機操作人員的所有原始輸入進行記錄,以便出現事故(如操作故障、程序異常退出)的時候,用來分析是人為原因,還是系統原因造成的。

基于以上需求,我們必須解決兩個問題,一個是如何記錄操作人員的輸入,另一個是如何控制操作人員的輸入。在DOS、Windows3.1、Windows95/98中都可以編寫響應鍵盤和鼠標輸入的中斷處理程序,截取來自鍵盤和鼠標的輸入,記錄、分析后依情況分別處理。但是在WindowsNT和Windows2000中,這樣的解決方法將不再行得通,這是因為WindowsNT/2000操作系統為了提高系統的可靠性,不再允許應用程序直接對系統設備的底層進行操作。這樣,用戶的應用程序將不能夠對計算機的端口地址進行讀寫操作,所以在WindowsNT/2000操作系統中對計算機端口的讀寫是無效的。另外一種方法能夠非常完美地解決這個問題,就是可以編寫操作系統的設備驅動程序來解決,但是要編寫系統的設備驅動程序,必須對WindowsNT/2000的系統底層以及整個系統架構有比較深入的了解。而且設備驅動程序的編寫、調試都比較困難,同時這方面的資料也比較少。所以本文沒有采取這種方法,而是采用微軟公布的標準Win32函數和鉤子技術來解決這個問題,比較方便而且快捷。

在WindowsNT/2000操作系統中,稱各種輸入為事件(Event),所有的鍵盤、鼠標輸入事件以及其他事件都是通過消息傳遞處理機制來得到響應的。控制、監視計算機實際上是控制、監視事件消息流。Windows操作系統為這種應用提供鉤子(Hook)技術。這種技術的要點就是在操作系統的消息傳遞處理機制上外掛一個我們定義的函數,可以使用這個函數來監視、控制系統的事件消息流。本文采用的就是這種方法,這種方法要求將所有的程序代碼放入系統可以加載的動態鏈接庫中。下面我們以鍵盤輸入的監視和控制為例詳細敘述這種方法。其總體思路如下:

首先,定義自己的鉤子函數。

其次,安裝自定義的鉤子函數,此后鉤子函數在后臺開始工作。一旦系統發現擊鍵動作或者鼠標動作,系統將馬上調用該自定義的鉤子函數,并將事件消息傳入,供程序分析判斷。它可以監視所有的擊鍵和鼠標動作,與DOS時代的中斷調用有非常相似的地方。

最后,卸載自定義的鉤子函數。

鉤子函數的定義

微軟的鉤子技術的原理就是應用程序可以在系統的消息處理機制上外掛一個子程序,在消息尚未到達目的地之前,用該子程序來截獲此消息,以進行監視和控制。我們這里使用的是WH_KEYBOARD_LL類型的鉤子函數,這種類型的鉤子函數可以截獲所有的鍵盤事件,即敲擊鍵盤上的任何一個鍵,我們自定義的鉤子函數都可以知道。該類型鉤子函數要求安裝自定義的鉤子函數必須是以下原型:

LRESULTCALLBACKLowLevelKeyboard-

Proc(

intnCode,

WPARAMwParam,

LPARAMlParam

);

其中各參數的含義如下:

intnCode:用來決定鉤子函數如何處理事件消息的代碼,參數的取值為HC_ACTION時,參數wParam、lParam包含了所需的鍵盤消息事件信息。

WPARAMwParam:鍵盤消息事件的類型ID。該參數有四種可能的消息類型取值:WM_KEY-

DOWN,WM_KEYUP,WM_SYSKEYDOWN,WM_SYSKEYUP.

LPARAMlParam:指向一個類型為KBDLLHOOKSTRUCT的結構指針。該結構容納了底層鍵盤輸入事件的詳細信息,它的定義如下:

typedefstructtagKBDLLHOOKSTRUCT{

DWORDvkCode;

//一個范圍從1到254的虛擬鍵碼

DWORDscanCode;

//鍵盤的硬件掃描碼

DWORDflags;

//一系列的標志位

//0比特位指示該鍵是不是擴展鍵(如:功能鍵,或數字小鍵盤上的鍵),1表示是,0表示否

//1~3比特位保留

//4比特位用來區分該事件是否來自Win32函數keybd_event()調用,1表示是,0表示否

//5比特位為狀態描述碼,如果ALT鍵按下,該位是1,否則是0。

//6比特位保留。

//7比特位是變換狀態位,鍵被按下為0,鍵被釋放為1。

DWORDtime;

//該消息事件的時間標記。

DWORDdwExtraInfo;

//該消息的其他擴展信息。

}KBDLLHOOKSTRUCT,FAR*LPKBDLLHOOK

STRUCT,*PKBDLLHOOKSTRUCT;

實際的鉤子函數的框架如下:

LRESULTCALLBACKMyLowLevelKeyboard

Proc(intnCode,WPARAMwParam,LPARAMlParam)

{

BOOLbSkipThisEvent=FALSE;

HWNDhwndForeground;

HWNDhwndFocus;

DWORDdwCurrentThreadId;

DWORDdwWindowThreadId;

if(nCode==HC_ACTION){

PKBDLLHOOKSTRUCTp=(PKBDLLHOOKSTRUCT)lParam;

//系統傳遞來的鍵盤輸入事件信息指針

switch(wParam){

caseWM_SYSKEYUP:

caseWM_KEYUP://ifkeyup

/*這段代碼用來獲得當前擁有輸入焦點的窗口的窗口句柄,以便獲得該窗口的相關信息*/

/*獲得前端窗口(即用戶當前正在工作的窗口)的句柄,創建該窗口的線程通常擁有比其他線程稍微高一些的優先級。*/

hwndForegroud=::GetForegroundWindow();dwCurrentThreadId=::GetCurrentThreadId();//當前線程的Id

//獲得產生前端窗口hwndForeground的線程Id值,用來惟一表示一個線程

dwWindowThreadId=::GetWindowThread-

ProcessId(hwndForegroud,NULL);

/*下面這一行代碼非常重要,它的作用是使當前線程(dwCurrentThreadId)的輸入處理機制依附到創建前端窗口的線程(dwWindowThreadId)的輸入機制上,否則你將得不到當前擁有鍵盤輸入焦點的窗口句柄。這是因為在WindowsNT/2000操作系統通常創建不同的線程來處理相互獨立的輸入過程,每一個輸入過程都擁有自己的輸入狀態(焦點、鍵盤狀態、隊列狀態等),通過AttachThreadInput調用,操作系統將允許調用線程獲得或者設置其他線程生成窗口的輸入狀態信息。只有執行該系統調用,才能夠得到當前擁有鍵盤輸入焦點的窗口的窗口句柄,否則GetFocus()系統調用將返回NULL。在這一點上WindowsNT/2000與Windows9X操作系統有很大不同,這也正是WindowsNT/2000比Windows95/98操作系統可靠性、安全性更好的一個原因*/

AttachThreadInput(dwCurrentThreadId,dwWindowThreadId,TRUE);//

//獲得擁有鍵盤輸入焦點的窗口的窗口句柄

hwndFocus=::GetFocus();

if(hwndFocus||hwndForeground)

{

charwnm[256];

wnm[0]=0;

//變量定義后,使用之前一定要初始化。

//獲得該窗口的窗口標題,就是在窗口標題欄上顯示的內容

::GetWindowText(hwndForeground,wnm,255);

charclsnm[266];

clsnm[0]=0;

//獲得該窗口的類名字

::GetClassName(hwndFocus,clsnm,255);

//獲得該窗口的風格

LONGstyle=::GetWindowLong(hwfocus,GWL_STYLE);

/*如果你只對部分窗口感興趣的話,可以通過下面的形式進行過濾,從而只處理你所感興趣的某些窗口的輸入事件*/

if((stricmp(clsnm,“edit”)==0)

||(0x0020&style)

||strstr(wnm,“MyTestWnd”))

{//你可以在此記錄供以后分析使用的相關信息

LLKEY_OutputToLog(hwfore,hwfocus,wnm,clsnm,p);

}

}

caseWM_KEYDOWN:

caseWM_SYSKEYDOWN:

if(p->vkCode==VK_LWIN)

/*該行代碼用來將鍵盤上的左Win系統鍵(就是帶有微軟旗幟圖案的那個鍵,注意是左邊的那個)屏蔽掉,如果在代碼執行后,用戶敲擊鍵盤上的左Win系統鍵,將不會彈出Windows的開始菜單。你可以根據你的需要屏蔽任何你所要屏蔽的鍵,從而達到控制計算機使用的目的。*/

bSkipThisEvent=TRUE;

break;

}endofswitch

}//endofif

if(bSkipThisEvent)//如果是需要屏蔽的鍵,一定要返回1給操作系統,切記。

return1;

else

returnCallNextHookEx(NULL,nCode,wParam,lParam);

/*調用鉤子函數鏈,以便使其他應用程序能夠正常工作*/

}

這里需要注意的是,如果nCode小于零,鉤子函數必須返回CallNextHookEx函數調用的返回值。如果nCode>=0,建議仍然返回CallNextHookEx函數調用的返回值,否則其他安裝了WH_KEYBOARD_LL鉤子函數的應用程序將收不到系統發送的鉤子通知,從而使其他應用程序功能發生異常。不過我們也可以利用系統的這一個特點,來屏蔽一些功能鍵,禁止某些系統特性,實現控制計算機使用的想法。

安裝鉤子函數

接下來的工作就是將我們定義好的鉤子函數安裝到系統中。用來安裝用戶自定義鉤子函數的Win32函數是SetWindowsHookEx,該函數的原型如下:

HHOOKSetWindowsHookEx(

intidHook,

//將要安裝的鉤子函數的鉤子類型

HOOKPROClpfn,

//我們自定義的鉤子函數的函數地址

HINSTANCEhMod,

//應用程序的實例句柄,即容納了鉤子函數的動態鏈接庫的句柄。如果鉤子函數所在地址空間在當前進程的地址空間,hMod應該為NULL.

DWORDdwThreadId

//鉤子起作用的線程Id,如果該值為零,則對系統中所有線程都起作用

);

其中idHook指定了安裝的鉤子函數的類型,不同類型的鉤子函數可完成不同應用功能,而且不同版本的Windows操作系統支持的鉤子函數的種類也不盡相同,在本文我們主要介紹的是WindowsNT/2000操作系統中目前公開支持的最底層的兩種鉤子類型:WH_KEYBOARD_LL和WH_MOUSE_LL。這兩種類型的鉤子函數在WindowsNTServicesPack3及其以后的版本,包括Windows2000Professional中得到很好的支持。這兩種類型的鉤子函數可以分別監視底層的鍵盤和鼠標輸入事件,在系統將事件分發到相應的接收目的地之前將它截獲,交給用戶自定義的鉤子函數來處理。下面以鍵盤鉤子為例詳細說明,鼠標鉤子與此類似,不再贅談。

在這里,我們的安裝函數和我們定義的鉤子函數放在同一個動態鏈接庫中。其中g_hWin32NT_

DllHandle是我們定義的全局變量,類型是Handle,在系統調用動態鏈接庫的入口函數時,將hModule的值賦給g_hWin32NT_DllHandle。

//下面是動態鏈接庫的入口函數

DllMain(HANDLEhModule,DWORDul_reason_for_call,LPVOIDlpReserved)

{

g_hWin32NT_DllHandle=hModule;

/*保存該值,以后在安裝自定義鉤子函數的時候要使用該值*/

returnTRUE;

}

//下面的函數用來安裝我們自定義的鉤子函數

HHOOKg_hhkLowLevelKybd;

//以后在卸載自定義鉤子函數時,要用到該值

voidStartMyHook(void)

{

g_hhkLowLevelKybd=SetWindowsHook

Ex(WH_KEYBOARD_LL,

::MyLowLevelKeyboardProc,(HINSTANCE)g_hWin32NT_DllHandle,0);

}

/*g_hWin32NT_DllHandle是動態鏈接庫的句柄,我們定義的鉤子函數放在該動態鏈接庫中。該句柄是在操作系統加載動態鏈接庫時,由操作系統調用DllMain()傳入的*/

如果安裝成功,系統調用將返回一個鉤子函數的句柄;如果失敗,將返回NULL。將來在卸載我們自定義的鉤子函數時要使用該句柄。所以必須將返回值保存到一個全局變量中。安裝成功后,該函數返回。從現在開始,鍵盤的任何擊鍵動作都將被我們定義的鉤子函數捕捉到,包括各種系統功能鍵。操作系統在后臺將自動異步地調用我們自定義的鉤子函數進行處理,并且不會影響任何當前正在進行的各種工作,也不會對系統有任何不良影響。

卸載鉤子函數

當我們的應用程序退出時,或者不再需要鉤子函數的處理時,必須卸載我們自定義的鉤子函數。

//下面的Win32函數用來卸載我們自定義的鉤子函數

UnhookWindowsHookEx(g_hhkLowLevel

Kybd);

至此,我們已經比較完整地介紹了底層鍵盤鉤子技術的應用方法,對于鼠標輸入事件的控制與監視的方法,與此完全類似,讀者可依照本文完成自己的功能。并且在你的應用程序中,可以根據需要,多次安裝、卸載鉤子函數。不過有一點讀者要注意,就是在程序調試時,一定不要同時將這兩種類型的鉤子函數都進行設置,因為一旦程序處理上有錯誤,操作系統將不能獲得任何操作消息,計算機只有重新啟動。讀者可以將本文介紹的方法應用到許多場合,比如在運行某些關鍵的任務時,為避免人機交互的干擾,可以鎖定鍵盤和鼠標的輸入。待任務完成時,再恢復正常的鍵盤和鼠標的輸入。另外,屏蔽某些不想讓用戶使用的功能鍵等等。