全局級模擬
你會發現,用上面的方法類比按鍵並不是對所有程式都有效的,有的程式啊,你向它發了一大堆消息,可是它卻一點反應也沒有。
這是怎麼回事呢?這就要看具體的情況了,有些程式(特別是一些遊戲)出於某些原因,會禁止用戶對它使用類比按鍵程式,這個怎麼實現呢?
比如可以在程式中檢查一下,如果發現自己不是活動視窗,就不接受鍵盤消息。
或者仔細檢查一下收到的鍵盤消息,你會發現真實的按鍵和類比的按鍵消息總是有一些小差別,從這些小差別上,目的程式就能判斷出:這是假的!是偽造的!!因此,如果用PostMessage發送局部消息類比按鍵不成功的話,你可以試一試全局級的鍵盤消息,看看能不能騙過目的程式。
類比全局鍵盤消息常見的可以有以下一些方法:
(1) 用API函數keybd_event,這個函數可以用來類比一個鍵盤事件,它的VB聲明為:
Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
參數bVk表示要類比的按鍵的虛擬碼,bScan表示該按鍵的掃描碼(一般可以傳0),dwFlags表示是按下鍵還是釋放鍵(按下鍵為0,釋放鍵為2),dwExtraInfo是擴展標誌,一般沒有用。
比如要模擬按下A鍵,可以這樣:
Const KEYEVENTF_KEYUP = &H2
keybd_event VK_A, 0, 0, 0
'按下A鍵
keybd_event VK_A, 0, KEYEVENTF_KEYUP, 0
'釋放A鍵
注意有時候按鍵的速度不要太快,否則會出問題,可以用API函數Sleep來進行延時,聲明如下:
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
參數dwMilliseconds表示延時的時間,以毫秒為單位。
那麼如果要模擬按下功能鍵怎麼做呢?比如要按下Ctrl+C實現拷貝這個功能,可以這樣:
keybd_event VK_Ctrl, 0, 0, 0
'按下Ctrl鍵
keybd_event VK_C, 0, 0, 0
'按下C鍵
Sleep 500
'延時500毫秒
keybd_event VK_C, 0, KEYEVENTF_KEYUP, 0
'釋放C鍵
keybd_event VK_Ctrl, 0, KEYEVENTF_KEYUP, 0
'釋放Ctrl鍵
好了,現在你可以試試是不是可以騙過目的程式了,這個函數對大部分的視窗程式都有效,可是仍然有一部分遊戲對它產生的鍵盤事件熟視無睹,
這時候,你就要用上bScan這個參數了。一般的,bScan都傳0,但是如果目的程式是一些DirectX遊戲,那麼你就需要正確使用這個參數傳入掃描碼,
用了它可以產生正確的硬體事件消息,以被遊戲識別。這樣的話,就可以寫成這樣:
keybd_event VK_A, MapVirtualKey(VK_A, 0), 0, 0
'按下A鍵
keybd_event VK_A, MapVirtualKey(VK_A, 0), KEYEVENTF_KEYUP, 0
'釋放A鍵
以上就是用keybd_event函數來類比鍵盤事件。除了這個函數,SendInput函數也可以類比全局鍵盤事件。
SendInput可以直接把一條消息插入到消息佇列中,算是比較底層的了。
它的VB聲明如下:
Declare Function SendInput Lib "user32.dll" (ByVal nInputs As Long, pInputs As GENERALINPUT, ByVal cbSize As Long) As Long
參數:
nlnprts:定義plnputs指向的結構的數目。
plnputs:指向INPUT結構陣列的指標。每個結構代表插人到鍵盤或滑鼠輸入流中的一個事件。
cbSize:定義INPUT結構的大小。若cbSize不是INPUT結構的大小,則函數調用失敗。
返回值:函數返回被成功地插人鍵盤或滑鼠輸入流中的事件的數目。若要獲得更多的錯誤資訊,可以調用GetlastError函數。
備註:Sendlnput函數將INPUT結構中的事件順序地插入鍵盤或滑鼠的輸入流中。這些事件與用戶插入的(用滑鼠或鍵盤)或調用keybd_event,mouse_event,或另外的Sendlnput插人的鍵盤或滑鼠的輸入流不相容。
嗯,這個函數用起來蠻複雜的,因為它的參數都是指標一類的東西。要用它來類比鍵盤輸入,先要構造一組資料結構,把你要類比的鍵盤消息裝進去,然後傳給它。為了方便起見,把它做在一個過程裏面,要用的時候直接調用好了,代碼如下:
Declare Function SendInput Lib "user32.dll" (ByVal nInputs As Long, pInputs As GENERALINPUT, ByVal cbSize As Long) As Long
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByTELen As Long)
Type GENERALINPUT
dwType As Long
xi(0 To 23) As Byte
End Type
Type KEYBDINPUT
wVk As Integer
wScan As Integer
dwFlags As Long
time As Long
dwExtraInfo As Long
End Type
Const INPUT_KEYBOARD = 1
Sub MySendKey(bkey As Long)
'參數bkey傳入要類比按鍵的虛擬碼即可類比按下指定鍵
Dim GInput(0 To 1) As GENERALINPUT
Dim KInput As KEYBDINPUT
KInput.wVk = bkey
'你要類比的按鍵
KInput.dwFlags = 0 '按下鍵標誌
GInput(0).dwType = INPUT_KEYBOARD
CopyMemory GInput(0).xi(0), KInput, Len(KInput) '這個函數用來把記憶體中KInput的資料複製到GInput
KInput.wVk = bkey
KInput.dwFlags = KEYEVENTF_KEYUP
' 釋放按鍵
GInput(1).dwType = INPUT_KEYBOARD ' 表示該消息為鍵盤消息
CopyMemory GInput(1).xi(0), KInput, Len(KInput)
'以上工作把按下鍵和釋放鍵共2條鍵盤消息加入到GInput資料結構中
SendInput 2, GInput(0), Len(GInput(0))
'把GInput中存放的消息插入到消息列隊
End Sub
除了以上這些,用全局鉤子也可以類比鍵盤消息。如果你對windows中消息鉤子的用法已經有所瞭解,
那麼你可以通過設置一個全局HOOK來類比鍵盤消息,比如,你可以用WH_JOURNALPLAYBACK這個鉤子來類比按鍵。
WH_JOURNALPLAYBACK是一個系統級的全局鉤子,它和WH_JOURNALRECORD的功能是相對的,常用它們來記錄並重播鍵盤滑鼠操作。WH_JOURNALRECORD鉤子用來將鍵盤滑鼠的操作忠實地記錄下來,記錄下來的資訊可以保存到檔中,而WH_JOURNALPLAYBACK則可以重現這些操作。
當然亦可以單獨使用WH_JOURNALPLAYBACK來類比鍵盤操作。你需要首先聲明SetWindowsHookEx函數,它可以用來安裝消息鉤子:
Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long,ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
先安裝WH_JOURNALPLAYBACK這個鉤子,然後你需要自己寫一個鉤子函數,在系統調用它時,把你要模擬的事件傳遞給鉤子參數lParam所指向的EVENTMSG區域,就可以達到類比按鍵的效果。不過用這個鉤子類比鍵盤事件有一個副作用,就是它會鎖定真實的滑鼠鍵盤,不過如果你就是想在模擬的時候不會受真實鍵盤操作的干擾,那麼用用它倒是個不錯的主意。
3.驅動級模擬
如果上面的方法你都試過了,可是你發現目的程式卻仍然頑固的不接受你類比的消息,寒~~~~~~~~~還好,我還剩下最後一招,這就是驅動級模擬:直接讀寫鍵盤的硬體埠!
有一些使用DirectX介面的遊戲程式,它們在讀取鍵盤操作時繞過了windows的消息機制,而使用DirectInput.這是因為有些遊戲對即時性控制的要求比較高,
比如賽車遊戲,要求以最快速度回應鍵盤輸入。而windows消息由於是佇列形式的,消息在傳遞時會有不少延遲,有時1秒鐘也就傳遞十幾條消息,
這個速度達不到遊戲的要求。而DirectInput則繞過了windows消息,直接與鍵盤驅動程式打交道,效率當然提高了不少。
因此也就造成,對這樣的程式無論用PostMessage或者是keybd_event都不會有反應,因為這些函數都在較高層。
對於這樣的程式,只好用直接讀寫鍵盤埠的方法來類比硬體事件了。要用這個方法來類比鍵盤,需要先瞭解一下鍵盤編程的相關知識