WinIo詳細(xì)使用說(shuō)明_第1頁(yè)
WinIo詳細(xì)使用說(shuō)明_第2頁(yè)
WinIo詳細(xì)使用說(shuō)明_第3頁(yè)
WinIo詳細(xì)使用說(shuō)明_第4頁(yè)
WinIo詳細(xì)使用說(shuō)明_第5頁(yè)
已閱讀5頁(yè),還剩14頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、winio 的使用 WinIO 程序庫(kù)允許在 32 位的 Windows 應(yīng)用程序中直接對(duì) I/O 端口和物理存進(jìn)行存取操作。通過(guò)使用一種核模式的設(shè)備驅(qū)動(dòng)器和其它幾種底層編程技巧,它繞過(guò)了 Windows 系統(tǒng) 的保護(hù)機(jī)制。 WinNT/2000/XP 下, WinIO 函數(shù)庫(kù)只允許被具有管理者權(quán)限的應(yīng)用程序調(diào)用。如果使用者不是以管理者的身份進(jìn)入的,則WinIO.DLL不能夠被安裝,也不能激活 WinlO驅(qū)動(dòng)器。通過(guò)在管理者權(quán)限下安裝驅(qū)動(dòng)器軟件就可以克服這種限制。然而,在這種情況下, ShutdownWinIo 函數(shù)不能在應(yīng)用程序結(jié)束之前被調(diào)用, 因?yàn)樵摵瘮?shù)將WinIO 驅(qū)動(dòng)程序從系統(tǒng)注冊(cè)表中

2、刪除。 該函數(shù)庫(kù)提供8 個(gè)函數(shù)功能調(diào)用:bool _stdcall InitializeWinIo(); 本函數(shù)初始化WioIO 函數(shù)庫(kù)。必須在調(diào)用所有其它功能函數(shù)之前調(diào)用本函數(shù)。 如果函數(shù)調(diào)用成功,返回值為非零值。 如果調(diào)用失敗,則返回值為 0。 void _stdcall ShutdownWinIo(); 本函數(shù)在存中清除WinIO 庫(kù)本函數(shù)必須在中止應(yīng)用函數(shù)之前或者不再需要WinIO 庫(kù)時(shí)調(diào)用,bool _stdcall GetPortVal(WORD wPortAddr, PDWORD pdwPortVal, BYTE bSize); 使用此函數(shù)從一個(gè)輸入或輸出端口讀取一個(gè)字節(jié)/字/雙

3、字?jǐn)?shù)據(jù)。參數(shù): wPortAddr -輸入輸出端口地址 pdwPortVal -指向雙字變量的指針,接收從端口得到的數(shù)據(jù)。 bSize - 需要讀的字節(jié)數(shù),可以是 1 (BYTE), 2 (WORD) or 4 (DWORD). 如果調(diào)用成功,則返回非零值。 如果函數(shù)調(diào)用失敗,則函數(shù)返回值為零。bool _stdcall SetPortVal(WORD wPortAddr, DWORD dwPortVal, BYTE bSize); 使用本函數(shù)將一個(gè)字節(jié)/ 字/雙字的數(shù)據(jù)寫入輸入或輸出接口。參數(shù):wPortAddr -輸入輸出口地址 dwPortVal -要寫入口的數(shù)據(jù) bSize - 要寫的

4、數(shù)據(jù)個(gè)數(shù),可以是 1 (BYTE), 2 (WORD) or 4 (DWORD). 如果調(diào)用成功,則返回非零值。 如果函數(shù)調(diào)用失敗,則函數(shù)返回值為零。PBYTE _stdcall MapPhysToLin(PBYTE pbPhysAddr, DWORD dwPhysSize, HANDLE *pPhysicalMemoryHandle) 使用此函數(shù)將物理存的一部分映射到一個(gè)32 位應(yīng)用程序的線性地址空間。下面是一個(gè)例子: PBYTE pbLinAddr; HANDLE PhysicalMemoryHandle; pbLinAddr = MapPhysToLin(0xA0000, 65536,

5、&PhysicalMemoryHandle); 該函數(shù)將把物理地址圍為0XA0000 - 0XAFFFF的地址空間映射到與應(yīng)用程序?qū)?yīng)的線性地址空間。 返回值為一個(gè)與物理地址 0xA0000 相關(guān)的線性地址。如果出現(xiàn)錯(cuò)誤,則返回值 為 NULL。參數(shù):pbPhysAddr -指向物理地址的指針dwPhysSize -需要映射的字節(jié)數(shù)pPhysicalMemoryHandle -變量指針,如果調(diào)用成功,負(fù)責(zé)接收物理存句柄。隨后本 句柄在調(diào)用 UnmapPhysicalMemory 函數(shù)時(shí)作為其第一個(gè)參數(shù)。bool _stdcall UnmapPhysicalMemory(HANDLE P

6、hysicalMemoryHandle, PBYTE pbLinAddr) 使用本函數(shù)解除原先使用 MapPhysToLin 函數(shù)映射的一段線性物理存區(qū)域,該區(qū)域被映射到應(yīng)用程序所屬的線性地址空間。Windows 9x 應(yīng)用程序不需要調(diào)用此函數(shù)。參數(shù):PhysicalMemoryHandle -物理存區(qū)域所屬的句柄,此參數(shù)由對(duì)MapPhysToLin函數(shù)的調(diào) 用返回。pbLinAddr - MapPhysToLin函數(shù)調(diào)用返回的線性地址。bool _stdcall GetPhysLong(PBYTE pbPhysAddr, PDWORD pdwPhysVal);從指定的物理地址讀取一個(gè)雙字?jǐn)?shù)據(jù)

7、。參數(shù):pbPhysAddr -指向物理地址的指針。pdwPhysVal -指向一個(gè)雙字變量的指針,接收從物理存中傳來(lái)的數(shù)據(jù)。如果此函數(shù)調(diào)用成功,返回一個(gè)非零值。如果函數(shù)調(diào)用失敗,則返回一個(gè)零值。bool _stdcall SetPhysLong(PBYTE pbPhysAddr, DWORD dwPhysVal);將一個(gè)雙字型數(shù)據(jù)寫入指定的物理地址。參數(shù):pbPhysAddr -指向物理地址的指針。pdwPhysVal -指定待寫入物理存地址出的雙字型數(shù)據(jù)。如果此函數(shù)調(diào)用成功,返回一個(gè)非零值。如果函數(shù)調(diào)用失敗,則返回一個(gè)零值。VB winIO 模擬 鍵盤 外掛 2007-01-12 01:0

8、5 鍵盤是我們使用計(jì)算機(jī)的一個(gè)很重要的輸入設(shè)備了, 即使在鼠標(biāo)大行其道的今天, 很多程序依然離不開(kāi)鍵盤來(lái)操作。 但是有時(shí)候,一些重復(fù)性的, 很繁瑣的鍵盤操作總會(huì)讓人疲憊, 于是就有了用程序來(lái)代替人們按鍵的方法, 這樣可以把很多重復(fù)性的鍵盤操作交給程序來(lái)模擬, 省了很多精力, 按鍵精靈就是這樣的一個(gè)軟件。那么我們?cè)鯓硬拍苡?VB 來(lái)寫一個(gè)程序,達(dá)到與按鍵精靈類似的功能呢?那就讓我們來(lái)先了解一下windows 中響應(yīng)鍵盤事件的機(jī)制。當(dāng)用戶按下鍵盤上的一個(gè)鍵時(shí), 鍵盤的芯片會(huì)檢測(cè)到這個(gè)動(dòng)作, 并把這個(gè)信號(hào)傳送到計(jì)算機(jī)。 如何區(qū)別是哪一個(gè)鍵被按下了呢?鍵盤上的所有按鍵都有一個(gè)編碼, 稱作鍵盤掃描碼。當(dāng)

9、你按下一個(gè)鍵時(shí), 這個(gè)鍵的掃描碼就被傳給系統(tǒng)。 掃描碼是跟具體的硬件相關(guān)的, 同一個(gè)鍵, 在不同鍵盤上的掃描碼有可能不同。 鍵盤控制器就是將這個(gè)掃描碼傳給計(jì)算機(jī), 然后交給鍵盤驅(qū)動(dòng)程序。 鍵盤驅(qū)動(dòng)程序會(huì)完成相關(guān)的工作, 并把這個(gè)掃描碼轉(zhuǎn)換為鍵盤虛擬碼。 什么是虛擬碼呢?因?yàn)閽呙璐a與硬件相關(guān), 不具有通用性, 為了統(tǒng)一鍵盤上所有鍵的編碼, 于是就提出了虛擬碼概念。 無(wú)論什么鍵盤, 同一個(gè)按鍵的虛擬碼總是相同的, 這樣程序就可以識(shí)別了。簡(jiǎn)單點(diǎn)說(shuō),虛擬碼就是我們經(jīng)??梢钥吹降南馰K_A,VK_B 這樣的常數(shù),比如鍵 A的虛擬碼是65,寫成 16 進(jìn)制就是 &H41 ,注意,人們經(jīng)常用 16

10、進(jìn)制來(lái)表示虛擬碼。當(dāng)鍵盤驅(qū)動(dòng)程序把掃描碼轉(zhuǎn)換為虛擬碼后, 會(huì)把這個(gè)鍵盤操作的掃描碼和虛擬碼還有其它信息一起傳遞給操作系統(tǒng)。 然后操作系統(tǒng)則會(huì)把這些信息封裝在一個(gè)消息中, 并把這個(gè)鍵盤消息插入到消息列隊(duì)。最后,要是不出意外的話,這個(gè)鍵盤消息最終會(huì)被送到當(dāng)前的活動(dòng)窗口那里,活動(dòng)窗口所在的應(yīng)用程序接收到這個(gè)消息后, 就知道鍵盤上哪個(gè)鍵被按下, 也就可以決定該作出什么響應(yīng)給用戶了。這個(gè)過(guò)程可以簡(jiǎn)單的如下表示:用戶按下按鍵 鍵盤驅(qū)動(dòng)程序?qū)⒋耸录鬟f給操作系統(tǒng)操作系統(tǒng)將鍵盤事件插入消息隊(duì)列 鍵盤消息被發(fā)送到當(dāng)前活動(dòng)窗口明白了這個(gè)過(guò)程,我們就可以編程實(shí)現(xiàn)在其中的某個(gè)環(huán)節(jié)來(lái)模擬鍵盤操作了。在VB 中,有多種方

11、法可以實(shí)現(xiàn)鍵盤模擬,我們就介紹幾種比較典型的。1. 局部級(jí)模擬從上面的流程可以看出, 鍵盤事件是最終被送到活動(dòng)窗口, 然后才引起目標(biāo)程序響應(yīng)的。那么最直接的模擬方法就是: 直接偽造一個(gè)鍵盤消息發(fā)給目標(biāo)程序。 哈哈, 這實(shí)在是很簡(jiǎn)單,windows 提供了幾個(gè)這樣的 API 函數(shù)可以實(shí)現(xiàn)直接向目標(biāo)程序發(fā)送消息的功能,常用的有SendMessage和PostMessage,它們的區(qū)別是 PostMessage函數(shù)直接把消息仍給目標(biāo)程序就不管了,而SendMessage 把消息發(fā)出去后,還要等待目標(biāo)程序返回些什么東西才好。這里要注意的是,模擬鍵盤消息一定要用PostMessage函數(shù)才好,用Send

12、Message是不正確的(因?yàn)槟M鍵盤消息是不需要返回值的,不然目標(biāo)程序會(huì)沒(méi)反應(yīng)),切記切記!PostMessage 函數(shù)的 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參數(shù) hwnd 是你要發(fā)送消息的目標(biāo)程序上某個(gè)控件的句柄,參數(shù) wMsg 是消息的類型,表示你要發(fā)送什么樣的消息,最后 w

13、Param 和 lParam 這兩個(gè)參數(shù)是隨消息附加的數(shù)據(jù),具體容要由消息決定。再來(lái)看看 wMsg 這個(gè)參數(shù),要模擬按鍵就靠這個(gè)了。鍵盤消息常用的有如下幾個(gè):WM_KEYDOWN 表示一個(gè)普通鍵被按下WM_KEYUP 表示一個(gè)普通鍵被釋放WM_SYSKEYDOWN 表示一個(gè)系統(tǒng)鍵被按下,比如 Alt 鍵WM_SYSKEYUP 表示一個(gè)系統(tǒng)鍵被釋放,比如 Alt 鍵如果你確定要發(fā)送以上幾個(gè)鍵盤消息,那么再來(lái)看看如何確定鍵盤消息中的 wParam 和lParam 這兩個(gè)參數(shù)。在一個(gè)鍵盤消息中, wParam 參數(shù)的含義較簡(jiǎn)單,它表示你要發(fā)送的鍵盤事件的按鍵虛擬碼,比如你要對(duì)目標(biāo)程序模擬按下A 鍵,

14、那么 wParam 參數(shù)的值就設(shè)為 VK_A ,至于lParam 這個(gè)參數(shù)就比較復(fù)雜了,因?yàn)樗硕鄠€(gè)信息,一般可以把它設(shè)為0,但是如果你想要你的模擬更真實(shí)一些,那么建議你還是設(shè)置一下這個(gè)參數(shù)。那么我們就詳細(xì)了解一下lParam 吧。 lParam 是一個(gè) long 類型的參數(shù),它在存中占 4 個(gè)字節(jié),寫成二進(jìn)制就是 00000000 00000000 00000000 00000000 一共是 32 位,我們從右向左數(shù),假設(shè)最右邊那位為第 0 位 (注意是從0 而不是從 1 開(kāi)始計(jì)數(shù) ) ,最左邊的就是第31 位,那么該參數(shù)的的 0-15 位表示鍵的發(fā)送次數(shù)等擴(kuò)展信息, 16-23 位為按

15、鍵的掃描碼, 24-31 位表示是按下鍵還是釋放鍵。大家一般習(xí)慣寫成16進(jìn)制的,那么就應(yīng)該是&H00 00 00 00 ,第 0-15位一般為&H0001,如果是按下鍵, 那么24-31位為&H00,釋放鍵則為&HC0那么16-23位的掃描碼怎么會(huì)得呢?這需要用到一個(gè)API 函數(shù) MapVirtualKey ,這個(gè)函數(shù)可以將虛擬碼轉(zhuǎn)換為掃描碼,或?qū)呙璐a轉(zhuǎn)換為虛擬碼,還可以把虛擬碼轉(zhuǎn)換為對(duì)應(yīng)字符的ASCII碼。它的VB聲明如下:Declare Function MapVirtualKey Lib "user32" Alias "Ma

16、pVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long參數(shù) wCode 表示待轉(zhuǎn)換的碼, 參數(shù) wMapType 表示從什么轉(zhuǎn)換為什么,如果是虛擬碼轉(zhuǎn)掃描碼, 則 wMapType 設(shè)置為 0, 如果是虛擬掃描碼轉(zhuǎn)虛擬碼,則 wMapType 設(shè)置為 1,如果是虛擬碼轉(zhuǎn)ASCII 碼,則 wMapType 設(shè)置為2.相信有了這些,我們就可以構(gòu)造鍵盤事件的lParam 參數(shù)了。下面給出一個(gè)構(gòu)造lParam 參數(shù)的函數(shù):Declare Function MapVirtualKey Lib "user

17、32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As LongFunction MakeKeyLparam(ByVal VirtualKey As Long, ByVal flag As Long) As Long' 參數(shù) VirtualKey 表示按鍵虛擬碼,flag 表示是按下鍵還是釋放鍵,用 WM_KEYDOWN 和WM_KEYUP這兩個(gè)常數(shù)表示Dim s As StringDim Firstbyte As String 'lparam 參數(shù)的

18、24-31 位If flag = WM_KEYDOWN Then '如果是按下鍵Firstbyte = "00"ElseFirstbyte = "C0"'如果是釋放鍵End IfDim Scancode As Long'獲得鍵的掃描碼Scancode = MapVirtualKey(VirtualKey, 0)Dim Secondbyte As String 'lparam 參數(shù)的 16-23 位,即虛擬鍵掃描碼Secondbyte = Right("00" & Hex(Scancode), 2

19、)s = Firstbyte & Secondbyte & "0001"'0001 為 lparam 參數(shù)的 0-15 位, 即發(fā)送次數(shù)和其它擴(kuò)展信息MakeKeyLparam = Val("&H" & s)End Function這個(gè)函數(shù)像這樣調(diào)用,比如按下 A鍵,那么lParam=MakeKeyLparam(VK_A,WM_KEYDOWN),很簡(jiǎn)單吧。值得注意的是,即使你發(fā)送消息時(shí)設(shè)置了 lParam 參數(shù)的值,但是系統(tǒng)在傳遞消息時(shí)仍然可能會(huì)根據(jù)當(dāng)時(shí)的情況重新設(shè)置該參數(shù),那么目標(biāo)程序收到的消息中 lParam

20、的值可能會(huì)和你發(fā)送時(shí)的有所不同。 所以,如果你很懶的話, 還是直接把它設(shè)為 0 吧,對(duì)大多數(shù)程序不會(huì)有影響的,呵呵。好了, 做完以上的事情, 現(xiàn)在我們可以向目標(biāo)程序發(fā)送鍵盤消息了。 首先取得目標(biāo)程序接受這個(gè)消息的控件的句柄,比如目標(biāo)句柄是12345 ,那么我們來(lái)對(duì)目標(biāo)模擬按下并釋放A鍵,像這樣: (為了簡(jiǎn)單起見(jiàn), lParam 這個(gè)參數(shù)就不構(gòu)造了,直接傳0)PostMessage 12345, WM_KEYDOWN, VK_A, 0&'按下 A 鍵PostMessage 12345, WM_UP , VK_A, 0&'釋放 A 鍵好了,一次按鍵就完成了?,F(xiàn)在你可

21、以迫不及待的打開(kāi)記事本做實(shí)驗(yàn),先用 FindWindowEx這類 API 函數(shù)找到記事本程序的句柄, 再向它發(fā)送鍵盤消息, 期望記事本里能詭異的自動(dòng)出現(xiàn)字符??墒悄泷R上就是失望了,咦,怎么一點(diǎn)反應(yīng)也沒(méi)有?你欺騙感情啊555不是的哦,接著往下看啊。一般目標(biāo)程序都會(huì)含有多個(gè)控件, 并不是每個(gè)控件都會(huì)對(duì)鍵盤消息作出反應(yīng), 只有把鍵盤消息發(fā)送給接受它的控件才會(huì)得到期望的反應(yīng)。那記事本來(lái)說(shuō),它的編輯框其實(shí)是個(gè)edit 類,只有這個(gè)控件才對(duì)鍵盤事件有反應(yīng), 如果只是把消息發(fā)給記事本的窗體, 那是沒(méi)有用的。 現(xiàn)在你找出記事本那個(gè)編輯框的句柄,比如是54321 ,那么寫如下代碼:PostMessage 543

22、21, WM_KEYDOWN, VK_F1, 0&'按下F1 鍵PostMessage 54321, WM_UP , VK_F1, 0&'釋放 F1 鍵怎么樣,是不是打開(kāi)了記事本的 “幫助” 信息?這說(shuō)明目標(biāo)程序已經(jīng)收到了你發(fā)的消息,還不錯(cuò)吧 可以馬上新問(wèn)題就來(lái)了,你想模擬向記事本按下A 這個(gè)鍵,好在記事本里自動(dòng)輸入字符,可是,沒(méi)有任何反應(yīng)!這是怎么一回事呢?原來(lái), 如果要向目標(biāo)程序發(fā)送字符, 光靠 WM_KEYDOWN 和 WM_UP 這兩個(gè)事件還不行, 還需要一個(gè)事件: WM_CHAR,這個(gè)消息表示一個(gè)字符,程序需靠它看來(lái)接受輸入的字符。一 般只有A, B,

23、 C等這樣的按鍵才有 WM_CHAR消息,另U的鍵(比如方向鍵和功能鍵)是沒(méi)有這 個(gè)消息的,WM_CHAR消息一般發(fā)生在 WM_KEYDOWN消息之后。WM_CHAR消息的IParam 參數(shù)的含義與其它鍵盤消息一樣,而它的 wParam則表示相應(yīng)字符的 ASCII編碼(可以輸入中 文的哦A_A),現(xiàn)在你可以寫出一個(gè)完整的向記事本里自動(dòng)寫入字符的程序了,下面是一個(gè)例子,并附有這些消息常數(shù)的具體值:Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Lon

24、g, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As LongDeclare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As LongPublic Const WM_KEYDOWN = &H100Public Const WM_KEYUP = &H101Public Const WM_CHAR = &a

25、mp;H102Public Const VK_A = &H41Function MakeKeyLparam(ByVal VirtualKey As Long, ByVal flag As Long) As LongDim s As StringDim Firstbyte As String 'lparam 參數(shù)的 24-31 位If flag = WM_KEYDOWN Then '如果是按下鍵Firstbyte = "00"ElseFirstbyte = "C0"'如果是釋放鍵End IfDim Scancode As L

26、ong'獲得鍵的掃描碼Scancode = MapVirtualKey(VirtualKey, 0)Dim Secondbyte As String 'lparam 參數(shù)的 16-23 位,即虛擬鍵掃描碼Secondbyte = Right("00" & Hex(Scancode), 2)s = Firstbyte & Secondbyte & "0001"'0001 為 lparam 參數(shù)的 0-15 位, 即發(fā)送次數(shù)和其它擴(kuò)展信息MakeKeyLparam = Val("&H"

27、; & s)End FunctionPrivate Sub Form_Load()dim hwnd as longhwnd = 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) '輸入字 符APostMessage hwnd,WM_UP , VK_A, MakeKeyLp

28、aram(VK_A,WM_UP) '釋放 A 鍵End Sub這就是通過(guò)局部鍵盤消息來(lái)模擬按鍵。 這個(gè)方法有一個(gè)極大的好處, 就是: 它可以實(shí)現(xiàn)后臺(tái)按鍵, 也就是說(shuō)他對(duì)你的前臺(tái)操作不會(huì)有什么影響。 比如, 你可以用這個(gè)方法做個(gè)程序在游戲中模擬按鍵來(lái)不斷地執(zhí)行某些重復(fù)的操作,而你則一邊喝茶一邊與QQ 上的 MM 們聊得火熱, 它絲毫不會(huì)影響你的前臺(tái)操作。 無(wú)論目標(biāo)程序是否獲得焦點(diǎn)都沒(méi)有影響, 這就是后臺(tái)模擬按鍵的原理啦2.全局級(jí)模擬你會(huì)發(fā)現(xiàn), 用上面的方法模擬按鍵并不是對(duì)所有程序都有效的, 有的程序啊, 你向它發(fā)了一大堆消息, 可是它卻一點(diǎn)反應(yīng)也沒(méi)有。 這是怎么回事呢?這就要看具體的情況

29、了, 有些程序 (特別是一些游戲) 出于某些原因,會(huì)禁止用戶對(duì)它使用模擬按鍵程序,這個(gè)怎么實(shí)現(xiàn)呢?比如可以在程序中檢查一下, 如果發(fā)現(xiàn)自己不是活動(dòng)窗口, 就不接受鍵盤消息。 或者仔細(xì)檢查一下收到的鍵盤消息, 你會(huì)發(fā)現(xiàn)真實(shí)的按鍵和模擬的按鍵消息總是有一些小差別, 從這些小差別上,目標(biāo)程序就能判斷出:這是假的!是偽造的! !因此,如果用 PostMessage發(fā)送局部消息模擬按鍵不成功的話, 你可以試一試全局級(jí)的鍵盤消息, 看看能不能騙過(guò)目標(biāo)程序。模擬全局鍵盤消息常見(jiàn)的可以有以下一些方法:(1) 用 API 函數(shù)keybd_event ,這個(gè)函數(shù)可以用來(lái)模擬一個(gè)鍵盤事件,它的VB 聲明為:Decl

30、are Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags AsLong, ByVal dwExtraInfo As Long)參數(shù) bVk 表示要模擬的按鍵的虛擬碼, bScan 表示該按鍵的掃描碼(一般可以傳0), dwFlags表示是按下鍵還是釋放鍵(按下鍵為0 ,釋放鍵為2) , dwExtraInfo 是擴(kuò)展標(biāo)志,一般沒(méi)有用。比如要模擬按下A 鍵,可以這樣:Const KEYEVENTF_KEYUP = &H2keybd_event VK_A

31、, 0, 0, 0'按下 A 鍵keybd_event VK_A, 0, KEYEVENTF_KEYU,0P'釋放A 鍵注意有時(shí)候按鍵的速度不要太快,否則會(huì)出問(wèn)題,可以用 API 函數(shù) Sleep 來(lái)進(jìn)行延時(shí),聲明如下:Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)參數(shù) dwMilliseconds 表示延時(shí)的時(shí)間,以毫秒為單位。那么如果要模擬按下功能鍵怎么做呢?比如要按下Ctrl+C 實(shí)現(xiàn)拷貝這個(gè)功能,可以這樣:keybd_event VK_Ctrl, 0, 0, 0'

32、按下 Ctrl 鍵keybd_event VK_C, 0, 0, 0'按下 C鍵Sleep 500'延時(shí)500 毫秒keybd_event VK_C, 0, KEYEVENTF_KEYUP'釋放 C鍵keybd_event VK_Ctrl, 0, KEYEVENTF_KEYU,0P '釋放 Ctrl 鍵好了,現(xiàn)在你可以試試是不是可以騙過(guò)目標(biāo)程序了,這個(gè)函數(shù)對(duì)大部分的窗口程序都有效,可是仍然有一部分游戲?qū)λa(chǎn)生的鍵盤事件熟視無(wú)睹, 這時(shí)候, 你就要用上bScan 這個(gè)參數(shù)了。一般的,bScan都傳0,但是如果目標(biāo)程序是一些DirectX游戲,那么你就需要正確使用這

33、個(gè)參數(shù)傳入掃描碼,用了它可以產(chǎn)生正確的硬件事件消息, 以被游戲識(shí)別。這樣的話,就 可以寫成這樣:keybd_event VK_A, MapVirtualKey(VK_A, 0), 0, 0'按下A 鍵keybd_event VK_A, MapVirtualKey(VK_A, 0), KEYEVENTF_KEYU,P0'釋放A 鍵以上就是用 keybd_event 函數(shù)來(lái)模擬鍵盤事件。除了這個(gè)函數(shù), SendInput 函數(shù)也可以模擬全局鍵盤事件。 SendInput 可以直接把一條消息插入到消息隊(duì)列中,算是比較底層的了。它 的 VB 聲明如下:Declare Function

34、SendInput Lib "user32.dll" (ByVal nInputs As Long, pInputs As GENERALINPUT, ByVal cbSize As Long) As Long 參數(shù): nlnprts :定義 plnputs 指向的結(jié)構(gòu)的數(shù)目。plnputs :指向 INPUT 結(jié)構(gòu)數(shù)組的指針。每個(gè)結(jié)構(gòu)代表插人到鍵盤或鼠標(biāo)輸入流中的一個(gè)事 件。cbSize:定義INPUT結(jié)構(gòu)的大小。若 cbSize不是INPUT結(jié)構(gòu)的大小,則函數(shù)調(diào)用失敗。返回值: 函數(shù)返回被成功地插人鍵盤或鼠標(biāo)輸入流中的事件的數(shù)目。 若要獲得更多的錯(cuò)誤信息,可以調(diào)用 Ge

35、tlastError 函數(shù)。備注:Sendlnput函數(shù)將INPUT結(jié)構(gòu)中的事件順序地插入鍵盤或鼠標(biāo)的輸入流中。這些事件與用戶插入的(用鼠標(biāo)或鍵盤)或調(diào)用 keybd_event , mouse_event ,或另外的 Sendlnput 插 人的鍵盤或鼠標(biāo)的輸入流不兼容。嗯, 這個(gè)函數(shù)用起來(lái)蠻復(fù)雜的, 因?yàn)樗膮?shù)都是指針一類的東西。 要用它來(lái)模擬鍵盤輸入,先要構(gòu)造一組數(shù)據(jù)結(jié)構(gòu), 把你要模擬的鍵盤消息裝進(jìn)去, 然后傳給它。 為了方便起見(jiàn), 把它做在一個(gè)過(guò)程里面,要用的時(shí)候直接調(diào)用好了,代碼如下:Declare Function SendInput Lib "user32.dll&q

36、uot; (ByVal nInputs As Long, pInputs As GENERALINPUT, ByVal cbSize As Long) As LongDeclare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)Type GENERALINPUTdwType As Longxi(0 To 23) As ByteEnd TypeType KEYBDINPUTwVk As Integerw

37、Scan As IntegerdwFlags As Longtime As LongdwExtraInfo As LongEnd TypeConst INPUT_KEYBOARD = 1Sub MySendKey(bkey As Long)'參數(shù) bkey 傳入要模擬按鍵的虛擬碼即可模擬按下指定鍵Dim GInput(0 To 1) As GENERALINPUTDim KInput As KEYBDINPUTKInput.wVk = bkey '你要模擬的按鍵KInput.dwFlags = 0 '按下鍵標(biāo)志GInput(0).dwType = INPUT_KEYBO

38、ARDCopyMemory GInput(0).xi(0), KInput, Len(KInput) ' 這個(gè)函數(shù)用來(lái)把存中 KInput 的數(shù)據(jù)復(fù)制到GInputKInput.wVk = bkeyKInput.dwFlags = KEYEVENTF_KEYUP' 釋放按鍵GInput(1).dwType = INPUT_KEYBOARD '表示該消息為鍵盤消息CopyMemory GInput(1).xi(0), KInput, Len(KInput)' 以上工作把按下鍵和釋放鍵共 2 條鍵盤消息加入到 GInput 數(shù)據(jù)結(jié)構(gòu)中SendInput 2, GIn

39、put(0), Len(GInput(0) '把 GInput 中存放的消息插入到消息列隊(duì)End Sub除了以上這些,用全局鉤子也可以模擬鍵盤消息。如果你對(duì) windows 中消息鉤子的用法已經(jīng)有所了解,那么你可以通過(guò)設(shè)置一個(gè)全局 HOOK 來(lái)模擬鍵盤消息,比如,你可以用WH_JOURNALPLAYBAC這個(gè)鉤子來(lái)模擬按鍵。WH_JOURNALPLAYBAC是一個(gè)系統(tǒng)級(jí)的全局鉤子,它和 WHJOURNALRECORDJ功能是相對(duì)的,常用它們來(lái)記錄并回放鍵盤鼠標(biāo)操作。WH_JOURNALRECORDg子用來(lái)將鍵盤鼠標(biāo)的操作忠實(shí)地記錄下來(lái),記錄下來(lái)的信息可以保 存到文件中,而WH_JOUR

40、NALPLAYBAC則可以重現(xiàn)這些操作。當(dāng)然亦可以單獨(dú)使用WH_JOURNALPLAYBACK模擬鍵盤操作。你需要首先聲明 SetWindowsHookEx函數(shù),它可以 用來(lái)安裝消息鉤子: 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_JOURNALPL

41、AYBAC毆個(gè)鉤子,然后你需要自己寫一個(gè)鉤子函數(shù),在系統(tǒng)調(diào)用它 時(shí),把你要模擬的事件傳遞給鉤子參數(shù)IParam所指向的EVENTMSG區(qū)域,就可以達(dá)到模擬按鍵的效果。不過(guò)用這個(gè)鉤子模擬鍵盤事件有一個(gè)副作用,就是它會(huì)鎖定真實(shí)的鼠標(biāo)鍵盤, 不過(guò)如果你就是想在模擬的時(shí)候不會(huì)受真實(shí)鍵盤操作的干擾,那么用用它倒是個(gè)不錯(cuò)的主 意。3.驅(qū)動(dòng)級(jí)模擬如果上面的方法你都試過(guò)了,可是你發(fā)現(xiàn)目標(biāo)程序卻仍然頑固的不接受你模擬的消息,寒還好,我還剩下最后一招,這就是驅(qū)動(dòng)級(jí)模擬:直接讀寫鍵盤的硬件端口!有一些使用 DirectX 接口的游戲程序,它們?cè)谧x取鍵盤操作時(shí)繞過(guò)了windows 的消息機(jī)制,而使用 DirectIn

42、put. 這是因?yàn)橛行┯螒驅(qū)?shí)時(shí)性控制的要求比較高,比如賽車游戲,要求以最快速度響應(yīng)鍵盤輸入。而windows 消息由于是隊(duì)列形式的,消息在傳遞時(shí)會(huì)有不少延遲,有時(shí)1 秒鐘也就傳遞十幾條消息,這個(gè)速度達(dá)不到游戲的要求。而 DirectInput 則繞過(guò)了 windows 消息,直接與鍵盤驅(qū)動(dòng)程序打交道,效率當(dāng)然提高了不少。因此也就造成,對(duì)這樣的程序無(wú)論用PostMessage或者是keybd_event都不會(huì)有反應(yīng),因?yàn)檫@些函數(shù)都在較高層。 對(duì)于這樣的程序, 只好用直接讀寫鍵盤端口的方法來(lái)模擬硬件事件了。 要用這個(gè)方法 來(lái)模擬鍵盤,需要先了解一下鍵盤編程的相關(guān)知識(shí)。在 DOS 時(shí)代,當(dāng)用戶按下

43、或者放開(kāi)一個(gè)鍵時(shí),就會(huì)產(chǎn)生一個(gè)鍵盤中斷(如果鍵盤中斷是允許的),這樣程序會(huì)跳轉(zhuǎn)到BIOS中的鍵盤中斷處理程序去執(zhí)行。打開(kāi)windows的設(shè)備管理器,可以查看到鍵盤控制器由兩個(gè)端口控制。其中 &H60 是數(shù)據(jù)端口,可以讀出鍵盤數(shù)據(jù), 而&H64是控制端口,用來(lái)發(fā)出控制信號(hào)。也就是,從 &H60號(hào)端口可以讀此鍵盤的按鍵信 息, 當(dāng)從這個(gè)端口讀取一個(gè)字節(jié), 該字節(jié)的低7 位就是按鍵的掃描碼, 而高 1 位則表示是按下鍵還是釋放鍵。當(dāng)按下鍵時(shí),最高位為0,稱為通碼,當(dāng)釋放鍵時(shí),最高位為1,稱為斷碼。 既然從這個(gè)端口讀數(shù)據(jù)可以獲得按鍵信息, 那么向這個(gè)端口寫入數(shù)據(jù)就可以模擬按鍵了

44、! 用過(guò)QbASIC4.5的朋友可能知道,QB中有個(gè)OUT命令可以向指定端口寫入數(shù)據(jù),而 INP函 數(shù)可以讀取指定端口的數(shù)據(jù)。那我們先看看如果用 QB 該怎么寫代碼: 假如你想模擬按下一個(gè)鍵,這個(gè)鍵的掃描碼為 &H50 ,那就這樣 OUT &H64,&HD2把數(shù)據(jù)&HD2發(fā)送到&H64端口。這是一個(gè) KBC指令,表示將要向鍵盤寫入數(shù)據(jù) OUT &H60,&H50'把掃描碼 &H50 發(fā)送到 &H60 端口,表示模擬按下掃描碼為 &H50 的這個(gè)鍵 那么要釋放這個(gè)鍵呢?像這樣,發(fā)送該鍵的斷碼: OUT &am

45、p;H64,&HD2把數(shù)據(jù)&HD2發(fā)送到&H64端口。這是一個(gè) KBC指令,表示將要向鍵盤寫入數(shù)據(jù) OUT &H60,(&H50 OR &H80)'把掃描碼 &H50 與數(shù)據(jù) &H80 進(jìn)行或運(yùn)算,可以把它的高位置1 ,得到斷碼,表示釋放這個(gè)鍵好了,現(xiàn)在的問(wèn)題就是在 VB 中如何向端口寫入數(shù)據(jù)了。因?yàn)樵趙indows 中,普通應(yīng)用程序是無(wú)權(quán)操作端口的, 于是我們就需要一個(gè)驅(qū)動(dòng)程序來(lái)幫助我們實(shí)現(xiàn)。 在這里我們可以使 用一個(gè)組件WINIO 來(lái)完成讀寫端口操作。什么是WINIO? WINIO 是一個(gè)全免費(fèi)的、無(wú)需注冊(cè)的、含源程序

46、的WINDOWS2000端口操作驅(qū)動(dòng)程序組件何以到ernals./上去下載)。 它不僅可以操作端口,還可以操作存;不僅能在VB 下用,還可以在DELPH、I VC 等其它環(huán)境下使用,性能特別優(yōu)異。下載該組件,解壓縮后可以看到幾個(gè)文件夾,其中Release文件夾下的3個(gè)文件就是我們需要的,這 3個(gè)文件是 WinIo.sys(用于win xp下的驅(qū)動(dòng)程序), WINIO.VXD(用于 win 98下的驅(qū)動(dòng)程序),WinIo.dll(封裝函數(shù)的動(dòng)態(tài)庫(kù) ),我們只需要調(diào)用 WinIo.dll 中的函數(shù),然后WinIo.dll 就會(huì)安裝并調(diào)用驅(qū)動(dòng)程序來(lái)完成相應(yīng)的功能。值得一提的是這個(gè)組件完

47、全是綠色的, 無(wú)需安裝, 你只需要把這3 個(gè)文件復(fù)制到與你的程序相同的文件夾下就可以使用了。用法很簡(jiǎn)單,先用里面的 InitializeWinIo 函數(shù)安裝驅(qū)動(dòng)程序,然后就 可以用 GetPortVal 來(lái)讀取端口或者用 SetPortVal 來(lái)寫入端口了。好,讓我們來(lái)做一個(gè)驅(qū)動(dòng)級(jí) 的鍵盤模擬吧。先把winio 的 3 個(gè)文件拷貝到你的程序的文件夾下,然后在VB 中新建一個(gè)工程,添加一個(gè)模塊,在模塊中加入下面的 winio 函數(shù)聲明 :Declare Function MapPhysToLin Lib "WinIo.dll" (ByVal PhysAddr As Long,

48、 ByVal PhysSize As Long, ByRef PhysMemHandle) As LongDeclare Function UnmapPhysicalMemory Lib "WinIo.dll" (ByVal PhysMemHandle, ByVal LinAddr)As BooleanDeclare Function GetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As Long, ByRef PhysVal As Long) As BooleanDeclare Function SetPhysL

49、ong Lib "WinIo.dll" (ByVal PhysAddr As Long, ByVal PhysVal As Long) AsBooleanDeclare Function GetPortVal Lib "WinIo.dll" (ByVal PortAddr As Integer, ByRef PortVal As Long, ByVal bSize As Byte) As BooleanDeclare Function SetPortVal Lib "WinIo.dll" (ByVal PortAddr As Inte

50、ger, ByVal PortVal As Long, ByVal bSize As Byte) As BooleanDeclare Function InitializeWinIo Lib "WinIo.dll" () As BooleanDeclare Function ShutdownWinIo Lib "WinIo.dll" () As BooleanDeclare Function InstallWinIoDriver Lib "WinIo.dll" (ByVal DriverPath As String, ByVal Mo

51、de As Integer) As BooleanDeclare Function RemoveWinIoDriver Lib "WinIo.dll" () As Boolean' 以上是 WINIO 函數(shù)聲明 Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long' 以上是 WIN32 API 函數(shù)聲明 再添加下面這個(gè)過(guò)程:Sub K

52、BCWait4IBE() '等待鍵盤緩沖區(qū)為空Dim dwVal As LongDoGetPortVal &H64, dwVal, 1'這句表示從 &H64 端口讀取一個(gè)字節(jié)并把讀出的數(shù)據(jù)放到變量dwVal 中'GetPortVal 函數(shù)的用法是GetPortVal 端口號(hào),存放讀出數(shù)據(jù)的變量,讀入的長(zhǎng)度Loop While (dwVal And &H2)End Sub上面的是一個(gè)根據(jù)KBC 規(guī)寫的過(guò)程,它的作用是在向鍵盤端口寫入數(shù)據(jù)前等待一段時(shí)間,后面將會(huì)用到。然后再添加如下過(guò)程,這2 個(gè)過(guò)程用來(lái)模擬按鍵:Public Const KBC_KE

53、Y_CMD = &H64'鍵盤命令端口Public Const KBC_KEY_DATA = &H60 '鍵盤數(shù)據(jù)端口Sub MyKeyDown(ByVal vKeyCoad As Long)這個(gè)用來(lái)模擬按下鍵,參數(shù)vKeyCoad傳入按鍵的虛擬碼Dim btScancode As LongbtScancode = MapVirtualKey(vKeyCoad, 0)KBCWait4IBE '發(fā)送數(shù)據(jù)前應(yīng)該先等待鍵盤緩沖區(qū)為空SetPortVal KBC_KEY_CMD, &HD2, 1'發(fā)送鍵盤寫入命令'SetPortVal 函

54、數(shù)用于向端口寫入數(shù)據(jù), 它的用法是SetPortVal 端口號(hào),欲寫入的數(shù)據(jù),寫入數(shù)據(jù)的長(zhǎng)度KBCWait4IBESetPortVal KBC_KEY_DATA, btScancode, 1 ' 寫入按鍵信息,按下鍵End SubSub MyKeyUp(ByVal vKeyCoad As Long)這個(gè)用來(lái)模擬釋放鍵,參數(shù)vKeyCoad傳入按鍵的虛擬碼Dim btScancode As LongbtScancode = MapVirtualKey(vKeyCoad, 0)KBCWait4IBE '等待鍵盤緩沖區(qū)為空SetPortVal KBC_KEY_CMD, &HD

55、2, 1 '發(fā)送鍵盤寫入命令KBCWait4IBESetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1 ' 寫入按鍵信息,釋放鍵End Sub定義了上面的過(guò)程后,就可以用它來(lái)模擬鍵盤輸入了。在窗體模塊中添加一個(gè)定時(shí)器控件,然后加入以下代碼:Private Sub Form_Load()If InitializeWinIo = False Then'用 InitializeWinIo 函數(shù)加載驅(qū)動(dòng)程序,如果成功會(huì)返回 true ,否則返回 falseMsgBox "驅(qū)動(dòng)程序加載失敗!"Unload M

56、eEnd IfTimer1.Interval=3000Timer1.Enabled=TrueEnd SubPrivate Sub Form_Unload(Cancel As Integer)ShutdownWinIo ' 程序結(jié)束時(shí)記得用 ShutdownWinIo 函數(shù)卸載驅(qū)動(dòng)程序End SubPrivate Sub Timer1_Timer()Dim VK_A as Long = &H41MyKeyDown VK_AMyKeyUp VK_A'模擬按下并釋放A 鍵End Sub運(yùn)行上面的程序,就會(huì)每隔 3 秒鐘模擬按下一次A 鍵,試試看,怎么樣,是不是對(duì)所有程序都有效

57、果了?需要注意的問(wèn)題:要在VB的調(diào)試模式下使用 WINIO,需要把那3個(gè)文件拷貝到 VB的安裝目錄中。鍵盤上有些鍵屬于擴(kuò)展鍵 ( 比如鍵盤上的方向鍵就是擴(kuò)展鍵),對(duì)于擴(kuò)展鍵不應(yīng)該用上面的MyKeyDown 和 MyKeyUp 過(guò)程來(lái)模擬,可以使用下面的 2 個(gè)過(guò)程來(lái)準(zhǔn)確模擬擴(kuò)展鍵:Sub MyKeyDownEx(ByVal vKeyCoad As Long)'模擬擴(kuò)展鍵按下,參數(shù)vKeyCoad 是擴(kuò)展鍵的虛擬碼Dim btScancode As LongbtScancode = MapVirtualKey(vKeyCoad, 0)KBCWait4IBE '等待鍵盤緩沖區(qū)為空S

58、etPortVal KBC_KEY_CMD, &HD2, 1'發(fā)送鍵盤寫入命令KBCWait4IBESetPortVal KBC_KEY_DATA, &HE0, 1 ' 寫入擴(kuò)展鍵標(biāo)志信息KBCWait4IBE '等待鍵盤緩沖區(qū)為空SetPortVal KBC_KEY_CMD, &HD2, 1'發(fā)送鍵盤寫入命令KBCWait4IBESetPortVal KBC_KEY_DATA, btScancode, 1 ' 寫入按鍵信息,按下鍵End SubSub MyKeyUpEx(ByVal vKeyCoad As Long) '

59、模擬擴(kuò)展鍵彈起 Dim btScancode As LongbtScancode = MapVirtualKey(vKeyCoad, 0)KBCWait4IBE '等待鍵盤緩沖區(qū)為空SetPortVal KBC_KEY_CMD, &HD2, 1'發(fā)送鍵盤寫入命令KBCWait4IBESetPortVal KBC_KEY_DATA, &HE0, 1 ' 寫入擴(kuò)展鍵標(biāo)志信息KBCWait4IBE '等待鍵盤緩沖區(qū)為空SetPortVal KBC_KEY_CMD, &HD2, 1'發(fā)送鍵盤寫入命令KBCWait4IBESetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1 ' 寫入按鍵信息,釋放鍵End Sub還應(yīng)該注意的是,如果要從擴(kuò)展鍵轉(zhuǎn)換到普通鍵,那么普通鍵的 KeyDown 事件應(yīng)該發(fā)送兩次。也就是說(shuō), 如果我想模擬先按下一個(gè)擴(kuò)展鍵, 再按下一個(gè)普通鍵, 那么就應(yīng)該向端口發(fā)送兩次該普通鍵被按下的信

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論