轉寫的夠大了
不相干的人也別來了
學技術的進來看看好了
這個文章寫的就是VB鍵盤類比終極方法
無論什麼遊戲都可以上
提示:堶惘陪茪p彩蛋,找到了,E堛WINIO你可以自己寫!
==============我是無恥的分割線========================================================
鍵盤是我們使用電腦的一個很重要的輸入設備了,即使在滑鼠大行其道的今天,很多程式依然離不開鍵盤來操作。但是有時候,一些重複性的,很繁瑣的鍵盤操作總會讓人疲憊,於是就有了用程式來代替人們按鍵的方法,這樣可以把很多重複性的鍵盤操作交給程式來類比,省了很多精力,按鍵精靈就是這樣的一個軟體。那麼我們怎樣才能用VB來寫一個程式,達到與按鍵精靈類似的功能呢?那就讓我們來先瞭解一下windows中回應鍵盤事件的機制。
當用戶按下鍵盤上的一個鍵時,鍵盤內的晶片會檢測到這個動作,並把這個信號傳送到電腦。如何區別是哪一個鍵被按下了呢?鍵盤上的所有按鍵都有一個編碼,稱作鍵盤掃描碼。當你按下一個鍵時,這個鍵的掃描碼就被傳給系統。掃描碼是跟具體的硬體相關的,同一個鍵,在不同鍵盤上的掃描碼有可能不同。鍵盤控制器就是將這個掃描碼傳給電腦,然後交給鍵盤驅動程式。鍵盤驅動程式會完成相關的工作,並把這個掃描碼轉換為鍵盤虛擬碼。什麼是虛擬碼呢?因為掃描碼與硬體相關,不具有通用性,為了統一鍵盤上所有鍵的編碼,於是就提出了虛擬碼概念。無論什麼鍵盤,同一個按鍵的虛擬碼總是相同的,這樣程式就可以識別了。簡單點說,虛擬碼就是我們經常可以看到的像VK_A,VK_B這樣的常數,比如鍵A的虛擬碼是65,寫成16進制就是&H41,注意,人們經常用16進制來表示虛擬碼。當鍵盤驅動程式把掃描碼轉換為虛擬碼後,會把這個鍵盤操作的掃描碼和虛擬碼還有其他資訊一起傳遞給作業系統。然後作業系統則會把這些資訊封裝在一個消息中,並把這個鍵盤消息插入到消息列隊。最後,要是不出意外的話,這個鍵盤消息最終會被送到當前的活動視窗那堙A活動視窗所在的應用程式接收到這個消息後,就知道鍵盤上哪個鍵被按下,也就可以決定該作出什麼回應給用戶了。這個過程可以簡單的如下表示:
用戶按下按鍵-----鍵盤驅動程式將此事件傳遞給作業系統-----作業系統將鍵盤事件插入消息佇列-----鍵盤消息被發送到當前活動窗口
明白了這個過程,我們就可以編程實現在其中的某個環節來類比鍵盤操作了。在VB中,有多種方法可以實現鍵盤類比,我們就介紹幾種比較典型的。
1.局部級類比
從上面的流程可以看出,鍵盤事件是最終被送到活動視窗,然後才引起目的程式響應的。那麼最直接的模擬方法就是:直接偽造一個鍵盤消息發給目的程式。哈哈,這實在是很簡單,windows提供了幾個這樣的API函數可以實現直接向目的程式發送消息的功能,常用的有SendMessage和PostMessage,它們的區別是PostMessage函數直接把消息仍給目的程式就不管了,而SendMessage把消息發出去後,還要等待目的程式返回些什麼東西才好。這堶n注意的是,類比鍵盤消息一定要用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函數找到記事本程式的控制碼,再向它發送鍵盤消息,期望記事本堹鉊瑊妒漲菾吤X現字元。可是你馬上就是失望了,咦,怎麼一點反應也沒有?你欺騙感情啊~~~~~~~~~~55555555555555
不是的哦,接著往下看啊。
一般目的程式都會含有多個控制項,並不是每個控制項都會對鍵盤消息作出反應,只有把鍵盤消息發送給接受它的控制項才會得到期望的反應。那記事本來說,它的編輯框其實是個edit類,只有這個控制項才對鍵盤事件有反應,如果只是把消息發給記事本的表單,那是沒有用的。現在你找出記事本那個編輯框的控制碼,比如是54321,那麼寫如下代碼:
PostMessage 54321,WM_KEYDOWN,VK_F1,0&
'按下F1鍵
PostMessage 54321,WM_UP,VK_F1,0&
'釋放F1鍵
怎麼樣,是不是打開了記事本的“幫助”資訊?這說明目的程式已經收到了你發的消息,還不錯吧~~~~~~~~
可以馬上新問題就來了,你想模擬向記事本按下A這個鍵,好在記事本埵菾尪擗J字元,可是,沒有任何反應!這是怎麼一回事呢?
原來,如果要向目的程式發送字元,光靠WM_KEYDOWN和WM_UP這兩個事件還不行,還需要一個事件:WM_CHAR,這個消息表示一個字元,程式需靠它看來接受輸入的字元。一般只有A,B,C等這樣的按鍵才有WM_CHAR消息,別的鍵(比如方向鍵和功能鍵)是沒有這個消息的,WM_CHAR消息一般發生在WM_KEYDOWN消息之後。WM_CHAR消息的lParam參數的含義與其他鍵盤消息一樣,而它的wParam則表示相應字元的ASCII編碼(可以輸入中文的哦^_^),現在你可以寫出一個完整的向記事本埵菾宎g入字元的程式了,下面是一個例子,並附有這些消息常數的具體值:
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們聊得火熱,它絲毫不會影響你的前臺操作。無論目的程式是否獲得焦點都沒有影響,這就是後臺類比按鍵的原理啦~~~~
[ 本帖最後由 呆信 於 2009-3-20 14:52 編輯 ] |