鍵盤是咱們使用計算機的一個很重要的輸入設備了,即便在鼠標大行其道的今天,不少程序依然離不開鍵盤來操做。可是有時候,一些重複性的,很繁瑣的鍵盤操做總會讓人疲憊,因而就有了用程序來代替人們按鍵的方法,這樣能夠把不少重複性的鍵盤操做交給程序來模擬,省了不少精力,按鍵精靈就是這樣的一個軟件。那麼咱們怎樣才能用VB來寫一個程序,達到與按鍵精靈相似的功能呢?那就讓咱們來先了解一下windows中響應鍵盤事件的機制。
當用戶按下鍵盤上的一個鍵時,鍵盤內的芯片會檢測到這個動做,並把這個信號傳送到計算機。如何區別是哪個鍵被按下了呢?鍵盤上的全部按鍵都有一個編碼,稱做鍵盤掃描碼。當你按下一個鍵時,這個鍵的掃描碼就被傳給系統。掃描碼是跟具體的硬件相關的,同一個鍵,在不一樣鍵盤上的掃描碼有可能不一樣。鍵盤控制器就是將這個掃描碼傳給計算機,而後交給鍵盤驅動程序。鍵盤驅動程序會完成相關的工做,並把這個掃描碼轉換爲鍵盤虛擬碼。什麼是虛擬碼呢?由於掃描碼與硬件相關,不具備通用性,爲了統一鍵盤上全部鍵的編碼,因而就提出了虛擬碼概念。不管什麼鍵盤,同一個按鍵的虛擬碼老是相同的,這樣程序就能夠識別了。簡單點說,虛擬碼就是咱們常常能夠看到的像VK_A,VK_B這樣的常數,好比鍵A的虛擬碼是65,寫成16進制就是&H41,注意,人們常常用16進制來表示虛擬碼。當鍵盤驅動程序把掃描碼轉換爲虛擬碼後,會把這個鍵盤操做的掃描碼和虛擬碼還有其它信息一塊兒傳遞給操做系統。而後操做系統則會把這些信息封裝在一個消息中,並把這個鍵盤消息插入到消息列隊。最後,要是不出意外的話,這個鍵盤消息最終會被送到當前的活動窗口那裏,活動窗口所在的應用程序接收到這個消息後,就知道鍵盤上哪一個鍵被按下,也就能夠決定該做出什麼響應給用戶了。這個過程能夠簡單的以下表示:
用戶按下按鍵-----鍵盤驅動程序將此事件傳遞給操做系統-----操做系統將鍵盤事件插入消息隊列-----鍵盤消息被髮送到當前活動窗口
明白了這個過程,咱們就能夠編程實如今其中的某個環節來模擬鍵盤操做了。在VB中,有多種方法能夠實現鍵盤模擬,咱們就介紹幾種比較典型的。
1.局部級模擬
從上面的流程能夠看出,鍵盤事件是最終被送到活動窗口,而後才引發目標程序響應的。那麼最直接的模擬方法就是:直接僞造一個鍵盤消息發給目標程序。哈哈,這實在是很簡單,windows提供了幾個這樣的API函數能夠實現直接向目標程序發送消息的功能,經常使用的有SendMessage和PostMessage,它們的區別是PostMessage函數直接把消息仍給目標程序就無論了,而SendMessage把消息發出去後,還要等待目標程序返回些什麼東西纔好。這裏要注意的是,模擬鍵盤消息必定要用PostMessage函數纔好,用SendMessage是不正確的(由於模擬鍵盤消息是不須要返回值的,否則目標程序會沒反應),切記切記!PostMessage函數的VB聲明以下:
Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
參數hwnd 是你要發送消息的目標程序上某個控件的句柄,參數wMsg 是消息的類型,表示你要發送什麼樣的消息,最後wParam 和lParam 這兩個參數是隨消息附加的數據,具體內容要由消息決定。
再來看看wMsg 這個參數,要模擬按鍵就靠這個了。鍵盤消息經常使用的有以下幾個:
WM_KEYDOWN 表示一個普通鍵被按下
WM_KEYUP 表示一個普通鍵被釋放
WM_SYSKEYDOWN 表示一個系統鍵被按下,好比Alt鍵
WM_SYSKEYUP 表示一個系統鍵被釋放,好比Alt鍵
若是你肯定要發送以上幾個鍵盤消息,那麼再來看看如何肯定鍵盤消息中的wParam 和lParam 這兩個參數。在一個鍵盤消息中,wParam 參數的含義較簡單,它表示你要發送的鍵盤事件的按鍵虛擬碼,好比你要對目標程序模擬按下A鍵,那麼wParam 參數的值就設爲VK_A ,至於lParam 這個參數就比較複雜了,由於它包含了多個信息,通常能夠把它設爲0,可是若是你想要你的模擬更真實一些,那麼建議你仍是設置一下這個參數。那麼咱們就詳細瞭解一下lParam 吧。lParam 是一個long類型的參數,它在內存中佔4個字節,寫成二進制就是00000000 00000000 00000000 00000000 一共是32位,咱們從右向左數,假設最右邊那位爲第0位(注意是從0而不是從1開始計數),最左邊的就是第31位,那麼該參數的的0-15位表示鍵的發送次數等擴展信息,16-23位爲按鍵的掃描碼,24-31位表示是按下鍵仍是釋放鍵。你們通常習慣寫成16進制的,那麼就應該是&H00 00 00 00 ,第0-15位通常爲&H0001,若是是按下鍵,那麼24-31位爲&H00,釋放鍵則爲&HC0,那麼16-23位的掃描碼怎麼會得呢?這須要用到一個API函數MapVirtualKey,這個函數能夠將虛擬碼轉換爲掃描碼,或將掃描碼轉換爲虛擬碼,還能夠把虛擬碼轉換爲對應字符的ASCII碼。它的VB聲明以下:
Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long
參數wCode 表示待轉換的碼,參數wMapType 表示從什麼轉換爲何,若是是虛擬碼轉掃描碼,則wMapType 設置爲0,若是是虛擬掃描碼轉虛擬碼,則wMapType 設置爲1,若是是虛擬碼轉ASCII碼,則wMapType 設置爲2.相信有了這些,咱們就能夠構造鍵盤事件的lParam參數了。下面給出一個構造lParam參數的函數:
Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long
Function MakeKeyLparam(ByVal VirtualKey As Long, ByVal flag As Long) As Long
'參數VirtualKey表示按鍵虛擬碼,flag表示是按下鍵仍是釋放鍵,用WM_KEYDOWN和WM_KEYUP這兩個常數表示
Dim s As String
Dim Firstbyte As String 'lparam參數的24-31位
If flag = WM_KEYDOWN Then '若是是按下鍵
Firstbyte = "00"
Else
Firstbyte = "C0" '若是是釋放鍵
End If
Dim Scancode As Long
'得到鍵的掃描碼
Scancode = MapVirtualKey(VirtualKey, 0)
Dim Secondbyte As String 'lparam參數的16-23位,即虛擬鍵掃描碼
Secondbyte = Right("00" & Hex(Scancode), 2)
s = Firstbyte & Secondbyte & "0001" '0001爲lparam參數的0-15位,即發送次數和其它擴展信息
MakeKeyLparam = Val("&H" & s)
End Function
這個函數像這樣調用,好比按下A鍵,那麼lParam=MakeKeyLparam(VK_A,WM_KEYDOWN) ,很簡單吧。值得注意的是,即便你發送消息時設置了lParam參數的值,可是系統在傳遞消息時仍然可能會根據當時的狀況從新設置該參數,那麼目標程序收到的消息中lParam的值可能會和你發送時的有所不一樣。因此,若是你很懶的話,仍是直接把它設爲0吧,對大多數程序不會有影響的,呵呵。
好了,作完以上的事情,如今咱們能夠向目標程序發送鍵盤消息了。首先取得目標程序接受這個消息的控件的句柄,好比目標句柄是12345,那麼咱們來對目標模擬按下並釋放A鍵,像這樣:(爲了簡單起見,lParam這個參數就不構造了,直接傳0)
PostMessage 12345,WM_KEYDOWN,VK_A,0& '按下A鍵
PostMessage 12345,WM_UP,VK_A,0& '釋放A鍵
好了,一次按鍵就完成了。如今你能夠火燒眉毛的打開記事本作實驗,先用FindWindowEx這類API函數找到記事本程序的句柄,再向它發送鍵盤消息,指望記事本里能詭異的自動出現字符。但是你立刻就是失望了,咦,怎麼一點反應也沒有?你欺騙感情啊~~~~~~~~~~55555555555555 不是的哦,接着往下看啊。
通常目標程序都會含有多個控件,並非每一個控件都會對鍵盤消息做出反應,只有把鍵盤消息發送給接受它的控件纔會獲得指望的反應。那記事原本說,它的編輯框實際上是個edit類,只有這個控件纔對鍵盤事件有反應,若是隻是把消息發給記事本的窗體,那是沒有用的。如今你找出記事本那個編輯框的句柄,好比是54321,那麼寫以下代碼:
PostMessage 54321,WM_KEYDOWN,VK_F1,0& '按下F1鍵
PostMessage 54321,WM_UP,VK_F1,0& '釋放F1鍵
怎麼樣,是否是打開了記事本的「幫助」信息?這說明目標程序已經收到了你發的消息,還不錯吧~~~~~~~~
能夠立刻新問題就來了,你想模擬向記事本按下A這個鍵,好在記事本里自動輸入字符,但是,沒有任何反應!這是怎麼一回事呢?
原來,若是要向目標程序發送字符,光靠WM_KEYDOWN和WM_UP這兩個事件還不行,還須要一個事件:WM_CHAR,這個消息表示一個字符,程序需靠它看來接受輸入的字符。通常只有A,B,C等這樣的按鍵纔有WM_CHAR消息,別的鍵(好比方向鍵和功能鍵)是沒有這個消息的,WM_CHAR消息通常發生在WM_KEYDOWN消息以後。WM_CHAR消息的lParam參數的含義與其它鍵盤消息同樣,而它的wParam則表示相應字符的ASCII編碼(能夠輸入中文的哦^_^),如今你能夠寫出一個完整的向記事本里自動寫入字符的程序了,下面是一個例子,並附有這些消息常數的具體值:
Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long
Public Const WM_KEYDOWN = &H100
Public Const WM_KEYUP = &H101
Public Const WM_CHAR = &H102
Public Const VK_A = &H41
Function MakeKeyLparam(ByVal VirtualKey As Long, ByVal flag As Long) As Long
Dim s As String
Dim Firstbyte As String 'lparam參數的24-31位
If flag = WM_KEYDOWN Then '若是是按下鍵
Firstbyte = "00"
Else
Firstbyte = "C0" '若是是釋放鍵
End If
Dim Scancode As Long
'得到鍵的掃描碼
Scancode = MapVirtualKey(VirtualKey, 0)
Dim Secondbyte As String 'lparam參數的16-23位,即虛擬鍵掃描碼
Secondbyte = Right("00" & Hex(Scancode), 2)
s = Firstbyte & Secondbyte & "0001" '0001爲lparam參數的0-15位,即發送次數和其它擴展信息
MakeKeyLparam = Val("&H" & s)
End Function
Private Sub Form_Load()
dim hwnd as long
hwnd = XXXXXX 'XXXXX表示記事本編輯框的句柄
PostMessage hwnd,WM_KEYDOWN,VK_A,MakeKeyLparam(VK_A,WM_KEYDOWN) '按下A鍵
PostMessage hwnd,WM_CHAR,ASC("A"),MakeKeyLparam(VK_A,WM_KEYDOWN) '輸入字符A
PostMessage hwnd,WM_UP,VK_A,MakeKeyLparam(VK_A,WM_UP) '釋放A鍵
End Sub
這就是經過局部鍵盤消息來模擬按鍵。這個方法有一個極大的好處,就是:它能夠實現後臺按鍵,也就是說他對你的前臺操做不會有什麼影響。好比,你能夠用這個方法作個程序在遊戲中模擬按鍵來不斷地執行某些重複的操做,而你則一邊喝茶一邊與QQ上的MM們聊得火熱,它絲絕不會影響你的前臺操做。不管目標程序是否得到焦點都沒有影響,這就是後臺模擬按鍵的原理啦~~~~
'根據你的反饋,可知有的遊戲已經屏蔽了KEYDOWN消息,因此不妨使用WM_LBUTTONDOWN,也許會成功。修改以下:
'用PostMessage,必須先知道目標的句柄hwd
Option Explicit
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Sub Command1_Click()
PostMessage Hwd, WM_LBUTTONDOWN, 0, 0 '模擬按下指定鍵
PostMessage Hwd, WM_LBUTTONUP, 0, 0
End Sub
用上面的方法模擬按鍵並非對全部程序都有效的,有的程序啊,你向它發了一大堆消息,但是它卻一點反應也沒有。這是怎麼回事呢? 這就要看具體的狀況了,有些程序(特別是一些遊戲)出於某些緣由,會禁止用戶對它使用模擬按鍵程序,這個怎麼實現呢?好比能夠在程序中檢查一下,若是發現 本身不是活動窗口,就不接受鍵盤消息。或者仔細檢查一下收到的鍵盤消息,你會發現真實的按鍵和模擬的按鍵消息老是有一些小差異,從這些小差異上,目標程序 就能判斷出:這是假的!是僞造的!!所以,若是用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是擴展標誌,通常沒有用。好比要模擬按下TAB鍵,能夠這樣: Const KEYEVENTF_KEYUP = &H2Const VK_TAB = &H9 keybd_event VK_TAB, 0, 0, 0 '按下TAB鍵 keybd_event VK_TAB, 0, KEYEVENTF_KEYUP, 0 '釋放TAB鍵 注意有時候按鍵的速度不要太快,不然會出問題,能夠用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遊戲,那麼你就須要正確使用這個參數傳入掃描碼,用了它能夠產生 正確的硬件事件消息,以被遊戲識別。這樣的話,就能夠寫成這樣: Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long '先聲明API函數mapvirtualkey,能夠將字符轉換爲鍵盤掃描碼 keybd_event VK_TAB, MapVirtualKey(VK_TAB, 0), 0, 0 '按下TAB鍵 keybd_event VK_TAB, MapVirtualKey(VK_TAB, 0), KEYEVENTF_KEYUP, 0 '釋放TAB鍵 以上就是用keybd_event函數來模擬鍵盤事件。除了這個函數,SendInput函數也能夠模擬全局鍵盤事件。另外用全局鉤子也能夠模擬鍵盤消息,不過我的以爲比較囉嗦,呵呵