按鍵精靈是一類很普遍的游戲插件,wow raid過程中部分職業(yè)的某些操作就需要此類插件(文俊的牧師號在打某些BOSS時驅(qū)散用到過)。類似的插件無非是用程序來模擬重復(fù)性的鍵盤動作。 游戲中進行鍵盤操作的程序可以理解為: 按下某鍵——鍵盤驅(qū)動程序?qū)⒋耸录鬟f給操作系統(tǒng)——操作系統(tǒng)將此事件插入消息隊列——此消息被發(fā)送到當(dāng)前活動窗口。 按照這個過程來理解程序,剩下的只不過要將抽象化的語句翻譯成代碼。當(dāng)然很多過程編程工具(.NET)會自動處理,而不需你去理會。(如如何插入消息隊列,消息隊列的管理,無用對象的釋放)。 第一個問題,按下如何用程序來模擬在鍵盤上按下某鍵。 日常操作中,當(dāng)用戶在鍵盤上按下某個按鍵時,鍵盤內(nèi)芯片會檢測到這個動作,并將這個信號傳遞給計算機。對于每個按鍵,鍵盤分配給它不同的編碼,稱做鍵盤掃描碼。當(dāng)敲擊鍵盤時,底層上實際傳遞給計算機消息隊列的是該按鍵的鍵盤掃描碼,所以知道了欲按鍵的掃描碼,就可以將該信息傳遞給電腦,從而達到模擬按鍵的功能。 第二個問題,鍵盤驅(qū)動程序如何把此事件傳遞給操作系統(tǒng)。 解決第一個問題的關(guān)鍵是必須知道按鍵的鍵盤掃描碼,但是僅僅知道鍵盤掃描碼不夠。因為操作系統(tǒng)需要得到的信息的并不是鍵盤掃描碼。因為鍵盤掃描碼是跟具體的硬件相關(guān)的,同一個鍵在不同鍵盤上的掃描碼有可能不同。鍵盤控制器將這個掃描碼傳給計算機,然后交給鍵盤驅(qū)動程序。鍵盤驅(qū)動程序會完成相關(guān)的工作,并把這個掃描碼轉(zhuǎn)換為鍵盤虛擬碼。鍵盤虛擬碼是針對鍵盤掃描碼的非通用性所提出。盡管出于硬件原因,同一個按鍵可能有不同的掃描碼,但是無論什么鍵盤,同一個按鍵的虛擬碼總是相同的,這樣程序就可以識別了。簡單點說,虛擬碼就是我們經(jīng)??梢钥吹降南馰K_A,VK_B這樣的常數(shù),比如鍵a的虛擬碼(字母大小寫雖然是同一個鍵,但是虛擬碼不同)是&H61(即十進制的97,一般用16進制來表示虛擬碼)。當(dāng)鍵盤驅(qū)動程序把掃描碼轉(zhuǎn)換為虛擬碼后,會把這個鍵盤操作的掃描碼和虛擬碼還有其它信息一起傳遞給操作系統(tǒng)。 操作系統(tǒng)在得到這個信息后,會對消息進行封裝,然后把這個鍵盤消息插入到消息列隊(這個過程則不需要我們理會)。最后,這個鍵盤消息最終會被送到當(dāng)前的活動窗口那里,活動窗口所在的應(yīng)用程序接收到這個消息后,就知道鍵盤上哪個鍵被按下,也就可以根據(jù)按鍵決定該作出什么響應(yīng)返回給用戶了。 明白整個過程后,然后就可以進行編程實現(xiàn)模擬鍵盤按鍵操作了。最直接的模擬方法是:直接偽造一個鍵盤消息發(fā)給目標(biāo)程序。因為鍵盤信息最終發(fā)送的目標(biāo)程序而引起目標(biāo)程序的響應(yīng)。 WINDOWS提供了消息函數(shù)(API函數(shù)(非托管函數(shù))),這里要用到的主要是: PostMessage(將一條消息投遞到指定窗口的消息隊列), SendMessage(調(diào)用一個窗口的窗口函數(shù),將一條消息發(fā)給那個窗口), PostMessage函數(shù)和SendMessage函數(shù)的聲明一樣,均能向目標(biāo)程序發(fā)送消息,所不同的返回值不同。 Declare Function PostMessage& Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) Declare Function SendMessage& Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) 可以用到的常量很多,這里要用到以下幾個: WM_KEYDOW 按下一個鍵 WM_KEYUP 釋放一個鍵 WM_SYSKEYDOWN 當(dāng)用戶按住ALT鍵同時按下其它鍵時提交此消息給擁有焦點的窗口 WM_SYSKEYUP 當(dāng)用戶釋放一個鍵同時ALT 鍵還按著時提交此消息給擁有焦點的窗口 PostMessage函數(shù)還有2個參數(shù),WParam,IParam。在一個鍵盤消息中,前一個參數(shù)的意義較為簡單,表示欲模擬的按鍵的虛擬碼(如按鍵A的的虛擬碼為VK_A,欲模擬這個按鍵時,WParam的值即為VK_A)。而后一個參數(shù)的則較為復(fù)雜,因為它包含了多個信息,一般的模擬過程中可以把它設(shè)置為0,但如果你想要模擬更真實,那么有必要對這個參數(shù)進行設(shè)置。lParam 是一個long類型的參數(shù),內(nèi)存中占4個字節(jié),二進制格式為 00000000 00000000 00000000 00000000 該參數(shù)的的0-15(從右往左的16位)位表示鍵的發(fā)送次數(shù)等擴展信息,16-23位為按鍵的掃描碼,24-31位表示是按下鍵還是釋放鍵。此參數(shù)一般寫成16進制格式,即為 &H00 00 00 00 ,第0-15位一般為&H0001,如果是按下鍵,那么24-31位為&H00,釋放鍵那么24-31位則為&HC0,16-23位的掃描碼的得到需要用到一個API函數(shù)MapVirtualKey,這個函數(shù)可以將虛擬碼轉(zhuǎn)換為掃描碼,或?qū)呙璐a轉(zhuǎn)換為虛擬碼,還可以把虛擬碼轉(zhuǎn)換為對應(yīng)字符的ASCII碼。 MapVirtualKey的聲明如下: Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long, 其中, wCode 類型為Long,欲轉(zhuǎn)換的源字符或掃描碼 wMapType 類型為Long,控制映射類型,取值為0,1,2,0表示wCode是個虛擬鍵碼。函數(shù)返回相應(yīng)的掃描碼,1表示wCode是個掃描碼。函數(shù)返回相應(yīng)的虛擬鍵碼,2表示wCode是個虛擬鍵碼。函數(shù)返回相應(yīng)的ASCII值(未加Shift組合鍵)。 函數(shù)返回值為Long型,其結(jié)果取決于wMapType參數(shù) 如下函數(shù)是利用MapVirtualKey函數(shù)得到一個虛擬按鍵的掃描碼,進而構(gòu)造IParam參數(shù),從而向記事本模擬發(fā)送一個A Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _ Function MakeIParam(Byval VirtualKey as long,Byval Flag as long) As Long 'VirtualKey表示按鍵虛擬碼,F(xiàn)lag表示按下鍵還是釋放鍵。 Dim s As String Dim FirstByte As string If Flag=WM_KEYDOWN Then FirstByte="00" '用來表示IParam的第24位至31位,先用字符串儲存,后轉(zhuǎn)換為16進制 Else FirstByte="C0" EndIf Dim ScanCode As long ScanCode=MapVirtuealKey(VIrtualKey,0) '使用API函數(shù)前需先聲明 Dim SecondByte as string SecondByte=Right("00"&Hex(ScanCode),2) '用來表示IParam的第16位至23位 s=FirstByte&SecondByte&"0001" MakeIParam=Val("&H"&s) End Function Private Sub Form_Load() If hWnd = 0 Or hEdit = 0 Then FindWindow 和FindWindowEx函數(shù)用來查找記事本編輯框的句柄。(句柄可以解釋為系統(tǒng)分配給每個資源的唯一標(biāo)識)這種方法通過局部鍵盤消息來模擬按鍵,它可以實現(xiàn)后臺按鍵,也就是說他對你的前臺操作不會有什么影響。比如,你可以用這個方法做個程序在游戲中模擬按鍵來不斷地執(zhí)行某些重復(fù)的操作,無論目標(biāo)程序是否獲得焦點都沒有影響,這就是后臺模擬按鍵的原理。 當(dāng)然模擬按鍵的方法不止一種!
|
|