青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 298  文章 - 377  trackbacks - 0
<2012年8月>
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用鏈接

留言簿(34)

隨筆分類

隨筆檔案

文章檔案

相冊(cè)

收藏夾

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

這年頭,在這個(gè)論壇里面已經(jīng)沒有什么技術(shù)貼了...呵呵~發(fā)一篇驚天地,泣鬼神的帖子.當(dāng)然這個(gè)只是模擬鍵盤的終極模擬.呵呵~
     鍵盤是我們使用計(jì)算機(jī)的一個(gè)很重要的輸入設(shè)備了,即使在鼠標(biāo)大行其道的今天,很多程序依然離不開鍵盤來操作。但是有時(shí)候,一些重復(fù)性的,很繁瑣的鍵盤操作總會(huì)讓人疲憊,于是就有了用程序來代替人們按鍵的方法,這樣可以把很多重復(fù)性的鍵盤操作交給程序來模擬,省了很多精力,按鍵精靈就是這樣的一個(gè)軟件。那么我們?cè)鯓硬拍苡肰B來寫一個(gè)程序,達(dá)到與按鍵精靈類似的功能呢?那就讓我們來先了解一下windows中響應(yīng)鍵盤事件的機(jī)制。
    當(dāng)用戶按下鍵盤上的一個(gè)鍵時(shí),鍵盤內(nèi)的芯片會(huì)檢測到這個(gè)動(dòng)作,并把這個(gè)信號(hào)傳送到計(jì)算機(jī)。如何區(qū)別是哪一個(gè)鍵被按下了呢?鍵盤上的所有按鍵都有一個(gè)編碼,稱作鍵盤掃描碼。當(dāng)你按下一個(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)一鍵盤上所有鍵的編碼,于是就提出了虛擬碼概念。無論什么鍵盤,同一個(gè)按鍵的虛擬碼總是相同的,這樣程序就可以識(shí)別了。簡單點(diǎn)說,虛擬碼就是我們經(jīng)常可以看到的像VK_A,VK_B這樣的常數(shù),比如鍵A的虛擬碼是65,寫成16進(jìn)制就是&H41,注意,人們經(jīng)常用16進(jìn)制來表示虛擬碼。當(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è)過程可以簡單的如下表示:
用戶按下按鍵-----鍵盤驅(qū)動(dòng)程序?qū)⒋耸录鬟f給操作系統(tǒng)-----操作系統(tǒng)將鍵盤事件插入消息隊(duì)列-----鍵盤消息被發(fā)送到當(dāng)前活動(dòng)窗口
明白了這個(gè)過程,我們就可以編程實(shí)現(xiàn)在其中的某個(gè)環(huán)節(jié)來模擬鍵盤操作了。在VB中,有多種方法可以實(shí)現(xiàn)鍵盤模擬,我們就介紹幾種比較典型的。

 

1.局部級(jí)模擬

    從上面的流程可以看出,鍵盤事件是最終被送到活動(dòng)窗口,然后才引起目標(biāo)程序響應(yīng)的。那么最直接的模擬方法就是:直接偽造一個(gè)鍵盤消息發(fā)給目標(biāo)程序。哈哈,這實(shí)在是很簡單,windows提供了幾個(gè)這樣的API函數(shù)可以實(shí)現(xiàn)直接向目標(biāo)程序發(fā)送消息的功能,常用的有SendMessage和PostMessage,它們的區(qū)別是PostMessage函數(shù)直接把消息仍給目標(biāo)程序就不管了,而SendMessage把消息發(fā)出去后,還要等待目標(biāo)程序返回些什么東西才好。這里要注意的是,模擬鍵盤消息一定要用PostMessage函數(shù)才好,用SendMessage是不正確的(因?yàn)槟M鍵盤消息是不需要返回值的,不然目標(biāo)程序會(huì)沒反應(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ā)送什么樣的消息,最后wParam 和lParam 這兩個(gè)參數(shù)是隨消息附加的數(shù)據(jù),具體內(nè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è)鍵盤消息,那么再來看看如何確定鍵盤消息中的wParam 和lParam 這兩個(gè)參數(shù)。在一個(gè)鍵盤消息中,wParam 參數(shù)的含義較簡單,它表示你要發(fā)送的鍵盤事件的按鍵虛擬碼,比如你要對(duì)目標(biāo)程序模擬按下A鍵,那么wParam 參數(shù)的值就設(shè)為VK_A ,至于lParam 這個(gè)參數(shù)就比較復(fù)雜了,因?yàn)樗硕鄠€(gè)信息,一般可以把它設(shè)為0,但是如果你想要你的模擬更真實(shí)一些,那么建議你還是設(shè)置一下這個(gè)參數(shù)。那么我們就詳細(xì)了解一下lParam 吧。lParam 是一個(gè)long類型的參數(shù),它在內(nèi)存中占4個(gè)字節(jié),寫成二進(jìn)制就是00000000 00000000 00000000 00000000  一共是32位,我們從右向左數(shù),假設(shè)最右邊那位為第0位(注意是從0而不是從1開始計(jì)數(shù)),最左邊的就是第31位,那么該參數(shù)的的0-15位表示鍵的發(fā)送次數(shù)等擴(kuò)展信息,16-23位為按鍵的掃描碼,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 "MapVirtualKeyA" (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 "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
'參數(shù)VirtualKey表示按鍵虛擬碼,flag表示是按下鍵還是釋放鍵,用WM_KEYDOWN和WM_KEYUP這兩個(gè)常數(shù)表示
    Dim s As String
    Dim Firstbyte As String    'lparam參數(shù)的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參數(shù)的16-23位,即虛擬鍵掃描碼
    Secondbyte = Right("00" & Hex(Scancode), 2)
    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) ,很簡單吧。值得注意的是,即使你發(fā)送消息時(shí)設(shè)置了lParam參數(shù)的值,但是系統(tǒng)在傳遞消息時(shí)仍然可能會(huì)根據(jù)當(dāng)時(shí)的情況重新設(shè)置該參數(shù),那么目標(biāo)程序收到的消息中l(wèi)Param的值可能會(huì)和你發(fā)送時(shí)的有所不同。所以,如果你很懶的話,還是直接把它設(shè)為0吧,對(duì)大多數(shù)程序不會(huì)有影響的,呵呵。
    好了,做完以上的事情,現(xiàn)在我們可以向目標(biāo)程序發(fā)送鍵盤消息了。首先取得目標(biāo)程序接受這個(gè)消息的控件的句柄,比如目標(biāo)句柄是12345,那么我們來對(duì)目標(biāo)模擬按下并釋放A鍵,像這樣:(為了簡單起見,lParam這個(gè)參數(shù)就不構(gòu)造了,直接傳0)
PostMessage 12345,WM_KEYDOWN,VK_A,0&   '按下A鍵
PostMessage 12345,WM_UP,VK_A,0&        '釋放A鍵
好了,一次按鍵就完成了。現(xiàn)在你可以迫不及待的打開記事本做實(shí)驗(yàn),先用FindWindowEx這類API函數(shù)找到記事本程序的句柄,再向它發(fā)送鍵盤消息,期望記事本里能詭異的自動(dòng)出現(xiàn)字符。可是你馬上就是失望了,咦,怎么一點(diǎn)反應(yīng)也沒有?你欺騙感情啊~~~~~~~~~~55555555555555  不是的哦,接著往下看啊。
一般目標(biāo)程序都會(huì)含有多個(gè)控件,并不是每個(gè)控件都會(huì)對(duì)鍵盤消息作出反應(yīng),只有把鍵盤消息發(fā)送給接受它的控件才會(huì)得到期望的反應(yīng)。那記事本來說,它的編輯框其實(shí)是個(gè)edit類,只有這個(gè)控件才對(duì)鍵盤事件有反應(yīng),如果只是把消息發(fā)給記事本的窗體,那是沒有用的。現(xiàn)在你找出記事本那個(gè)編輯框的句柄,比如是54321,那么寫如下代碼:
PostMessage 54321,WM_KEYDOWN,VK_F1,0&   '按下F1鍵
PostMessage 54321,WM_UP,VK_F1,0&        '釋放F1鍵
怎么樣,是不是打開了記事本的“幫助”信息?這說明目標(biāo)程序已經(jīng)收到了你發(fā)的消息,還不錯(cuò)吧~~~~~~~~
可以馬上新問題就來了,你想模擬向記事本按下A這個(gè)鍵,好在記事本里自動(dòng)輸入字符,可是,沒有任何反應(yīng)!這是怎么一回事呢?
原來,如果要向目標(biāo)程序發(fā)送字符,光靠WM_KEYDOWN和WM_UP這兩個(gè)事件還不行,還需要一個(gè)事件:WM_CHAR,這個(gè)消息表示一個(gè)字符,程序需靠它看來接受輸入的字符。一般只有A,B,C等這樣的按鍵才有WM_CHAR消息,別的鍵(比如方向鍵和功能鍵)是沒有這個(gè)消息的,WM_CHAR消息一般發(fā)生在WM_KEYDOWN消息之后。WM_CHAR消息的lParam參數(shù)的含義與其它鍵盤消息一樣,而它的wParam則表示相應(yīng)字符的ASCII編碼(可以輸入中文的哦^_^),現(xiàn)在你可以寫出一個(gè)完整的向記事本里自動(dòng)寫入字符的程序了,下面是一個(gè)例子,并附有這些消息常數(shù)的具體值:
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參數(shù)的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參數(shù)的16-23位,即虛擬鍵掃描碼
    Secondbyte = Right("00" & Hex(Scancode), 2)
    s = Firstbyte & Secondbyte & "0001"  '0001為lparam參數(shù)的0-15位,即發(fā)送次數(shù)和其它擴(kuò)展信息
    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

這就是通過局部鍵盤消息來模擬按鍵。這個(gè)方法有一個(gè)極大的好處,就是:它可以實(shí)現(xiàn)后臺(tái)按鍵,也就是說他對(duì)你的前臺(tái)操作不會(huì)有什么影響。比如,你可以用這個(gè)方法做個(gè)程序在游戲中模擬按鍵來不斷地執(zhí)行某些重復(fù)的操作,而你則一邊喝茶一邊與QQ上的MM們聊得火熱,它絲毫不會(huì)影響你的前臺(tái)操作。無論目標(biāo)程序是否獲得焦點(diǎn)都沒有影響,這就是后臺(tái)模擬按鍵的原理啦~~~~


2.全局級(jí)模擬

    你會(huì)發(fā)現(xiàn),用上面的方法模擬按鍵并不是對(duì)所有程序都有效的,有的程序啊,你向它發(fā)了一大堆消息,可是它卻一點(diǎn)反應(yīng)也沒有。這是怎么回事呢?這就要看具體的情況了,有些程序(特別是一些游戲)出于某些原因,會(huì)禁止用戶對(duì)它使用模擬按鍵程序,這個(gè)怎么實(shí)現(xiàn)呢?比如可以在程序中檢查一下,如果發(fā)現(xiàn)自己不是活動(dòng)窗口,就不接受鍵盤消息。或者仔細(xì)檢查一下收到的鍵盤消息,你會(huì)發(fā)現(xiàn)真實(shí)的按鍵和模擬的按鍵消息總是有一些小差別,從這些小差別上,目標(biāo)程序就能判斷出:這是假的!是偽造的!!因此,如果用PostMessage發(fā)送局部消息模擬按鍵不成功的話,你可以試一試全局級(jí)的鍵盤消息,看看能不能騙過目標(biāo)程序。
模擬全局鍵盤消息常見的可以有以下一些方法:
(1) 用API函數(shù)keybd_event,這個(gè)函數(shù)可以用來模擬一個(gè)鍵盤事件,它的VB聲明為:
Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
參數(shù)bVk表示要模擬的按鍵的虛擬碼,bScan表示該按鍵的掃描碼(一般可以傳0),dwFlags表示是按下鍵還是釋放鍵(按下鍵為0,釋放鍵為2),dwExtraInfo是擴(kuò)展標(biāo)志,一般沒有用。比如要模擬按下A鍵,可以這樣:
Const KEYEVENTF_KEYUP = &H2
keybd_event VK_A, 0, 0, 0   '按下A鍵
keybd_event VK_A, 0, KEYEVENTF_KEYUP, 0   '釋放A鍵
注意有時(shí)候按鍵的速度不要太快,否則會(huì)出問題,可以用API函數(shù)Sleep來進(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   '按下Ctrl鍵
keybd_event VK_C, 0, 0, 0      '按下C鍵
Sleep 500            '延時(shí)500毫秒
keybd_event VK_C, 0, KEYEVENTF_KEYUP, 0   '釋放C鍵
keybd_event VK_Ctrl, 0, KEYEVENTF_KEYUP, 0   '釋放Ctrl鍵
好了,現(xiàn)在你可以試試是不是可以騙過目標(biāo)程序了,這個(gè)函數(shù)對(duì)大部分的窗口程序都有效,可是仍然有一部分游戲?qū)λa(chǎn)生的鍵盤事件熟視無睹,這時(shí)候,你就要用上bScan這個(gè)參數(shù)了。一般的,bScan都傳0,但是如果目標(biāo)程序是一些DirectX游戲,那么你就需要正確使用這個(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_KEYUP, 0   '釋放A鍵
以上就是用keybd_event函數(shù)來模擬鍵盤事件。除了這個(gè)函數(shù),SendInput函數(shù)也可以模擬全局鍵盤事件。SendInput可以直接把一條消息插入到消息隊(duì)列中,算是比較底層的了。它的VB聲明如下:
Declare Function 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)用GetlastError函數(shù)。
備注:Sendlnput函數(shù)將INPUT結(jié)構(gòu)中的事件順序地插入鍵盤或鼠標(biāo)的輸入流中。這些事件與用戶插入的(用鼠標(biāo)或鍵盤)或調(diào)用keybd_event,mouse_event,或另外的Sendlnput插人的鍵盤或鼠標(biāo)的輸入流不兼容。
嗯,這個(gè)函數(shù)用起來蠻復(fù)雜的,因?yàn)樗膮?shù)都是指針一類的東西。要用它來模擬鍵盤輸入,先要構(gòu)造一組數(shù)據(jù)結(jié)構(gòu),把你要模擬的鍵盤消息裝進(jìn)去,然后傳給它。為了方便起見,把它做在一個(gè)過程里面,要用的時(shí)候直接調(diào)用好了,代碼如下:
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)
'參數(shù)bkey傳入要模擬按鍵的虛擬碼即可模擬按下指定鍵
Dim GInput(0 To 1) As GENERALINPUT
Dim KInput As KEYBDINPUT
 KInput.wVk = bkey  '你要模擬的按鍵
 KInput.dwFlags = 0 '按下鍵標(biāo)志
 GInput(0).dwType = INPUT_KEYBOARD
 CopyMemory GInput(0).xi(0), KInput, Len(KInput) '這個(gè)函數(shù)用來把內(nèi)存中KInput的數(shù)據(jù)復(fù)制到GInput
 KInput.wVk = bkey  
 KInput.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, GInput(0), Len(GInput(0))    '把GInput中存放的消息插入到消息列隊(duì)
End Sub

    除了以上這些,用全局鉤子也可以模擬鍵盤消息。如果你對(duì)windows中消息鉤子的用法已經(jīng)有所了解,那么你可以通過設(shè)置一個(gè)全局HOOK來模擬鍵盤消息,比如,你可以用WH_JOURNALPLAYBACK這個(gè)鉤子來模擬按鍵。WH_JOURNALPLAYBACK是一個(gè)系統(tǒng)級(jí)的全局鉤子,它和WH_JOURNALRECORD的功能是相對(duì)的,常用它們來記錄并回放鍵盤鼠標(biāo)操作。WH_JOURNALRECORD鉤子用來將鍵盤鼠標(biāo)的操作忠實(shí)地記錄下來,記錄下來的信息可以保存到文件中,而WH_JOURNALPLAYBACK則可以重現(xiàn)這些操作。當(dāng)然亦可以單獨(dú)使用WH_JOURNALPLAYBACK來模擬鍵盤操作。你需要首先聲明SetWindowsHookEx函數(shù),它可以用來安裝消息鉤子:
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這個(gè)鉤子,然后你需要自己寫一個(gè)鉤子函數(shù),在系統(tǒng)調(diào)用它時(shí),把你要模擬的事件傳遞給鉤子參數(shù)lParam所指向的EVENTMSG區(qū)域,就可以達(dá)到模擬按鍵的效果。不過用這個(gè)鉤子模擬鍵盤事件有一個(gè)副作用,就是它會(huì)鎖定真實(shí)的鼠標(biāo)鍵盤,不過如果你就是想在模擬的時(shí)候不會(huì)受真實(shí)鍵盤操作的干擾,那么用用它倒是個(gè)不錯(cuò)的主意。
3.驅(qū)動(dòng)級(jí)模擬

    如果上面的方法你都試過了,可是你發(fā)現(xiàn)目標(biāo)程序卻仍然頑固的不接受你模擬的消息,寒~~~~~~~~~還好,我還剩下最后一招,這就是驅(qū)動(dòng)級(jí)模擬:直接讀寫鍵盤的硬件端口!
    有一些使用DirectX接口的游戲程序,它們?cè)谧x取鍵盤操作時(shí)繞過了windows的消息機(jī)制,而使用DirectInput.這是因?yàn)橛行┯螒驅(qū)?shí)時(shí)性控制的要求比較高,比如賽車游戲,要求以最快速度響應(yīng)鍵盤輸入。而windows消息由于是隊(duì)列形式的,消息在傳遞時(shí)會(huì)有不少延遲,有時(shí)1秒鐘也就傳遞十幾條消息,這個(gè)速度達(dá)不到游戲的要求。而DirectInput則繞過了windows消息,直接與鍵盤驅(qū)動(dòng)程序打交道,效率當(dāng)然提高了不少。因此也就造成,對(duì)這樣的程序無論用PostMessage或者是keybd_event都不會(huì)有反應(yīng),因?yàn)檫@些函數(shù)都在較高層。對(duì)于這樣的程序,只好用直接讀寫鍵盤端口的方法來模擬硬件事件了。要用這個(gè)方法來模擬鍵盤,需要先了解一下鍵盤編程的相關(guān)知識(shí)。
    在DOS時(shí)代,當(dāng)用戶按下或者放開一個(gè)鍵時(shí),就會(huì)產(chǎn)生一個(gè)鍵盤中斷(如果鍵盤中斷是允許的),這樣程序會(huì)跳轉(zhuǎn)到BIOS中的鍵盤中斷處理程序去執(zhí)行。打開windows的設(shè)備管理器,可以查看到鍵盤控制器由兩個(gè)端口控制。其中&H60是數(shù)據(jù)端口,可以讀出鍵盤數(shù)據(jù),而&H64是控制端口,用來發(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ù)就可以模擬按鍵了!用過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 &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)在的問題就是在VB中如何向端口寫入數(shù)據(jù)了。因?yàn)樵趙indows中,普通應(yīng)用程序是無權(quán)操作端口的,于是我們就需要一個(gè)驅(qū)動(dòng)程序來幫助我們實(shí)現(xiàn)。在這里我們可以使用一個(gè)組件WINIO來完成讀寫端口操作。什么是WINIO?WINIO是一個(gè)全免費(fèi)的、無需注冊(cè)的、含源程序的WINDOWS2000端口操作驅(qū)動(dòng)程序組件(可以到http://www.internals.com/上去下載)。它不僅可以操作端口,還可以操作內(nèi)存;不僅能在VB下用,還可以在DELPHI、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)鏈接庫),我們只需要調(diào)用WinIo.dll中的函數(shù),然后WinIo.dll就會(huì)安裝并調(diào)用驅(qū)動(dòng)程序來完成相應(yīng)的功能。值得一提的是這個(gè)組件完全是綠色的,無需安裝,你只需要把這3個(gè)文件復(fù)制到與你的程序相同的文件夾下就可以使用了。用法很簡單,先用里面的InitializeWinIo函數(shù)安裝驅(qū)動(dòng)程序,然后就可以用GetPortVal來讀取端口或者用SetPortVal來寫入端口了。好,讓我們來做一個(gè)驅(qū)動(dòng)級(jí)的鍵盤模擬吧。先把winio的3個(gè)文件拷貝到你的程序的文件夾下,然后在VB中新建一個(gè)工程,添加一個(gè)模塊,在模塊中加入下面的winio函數(shù)聲明:

Declare Function MapPhysToLin Lib "WinIo.dll" (ByVal PhysAddr As Long, ByVal PhysSize As Long, ByRef PhysMemHandle) As Long
Declare Function UnmapPhysicalMemory Lib "WinIo.dll" (ByVal PhysMemHandle, ByVal LinAddr) As Boolean
Declare Function GetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As Long, ByRef PhysVal As Long) As Boolean
Declare Function SetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As Long, ByVal PhysVal As Long) As Boolean
Declare Function GetPortVal Lib "WinIo.dll" (ByVal PortAddr As Integer, ByRef PortVal As Long, ByVal bSize As Byte) As Boolean
Declare Function SetPortVal Lib "WinIo.dll" (ByVal PortAddr As Integer, ByVal PortVal As Long, ByVal bSize As Byte) As Boolean
Declare Function InitializeWinIo Lib "WinIo.dll" () As Boolean
Declare Function ShutdownWinIo Lib "WinIo.dll" () As Boolean
Declare Function InstallWinIoDriver Lib "WinIo.dll" (ByVal DriverPath As String, ByVal Mode As Integer) As Boolean
Declare 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è)過程:
Sub KBCWait4IBE()   '等待鍵盤緩沖區(qū)為空
Dim dwVal As Long
  Do
  GetPortVal &H64, dwVal, 1
'這句表示從&H64端口讀取一個(gè)字節(jié)并把讀出的數(shù)據(jù)放到變量dwVal中
'GetPortVal函數(shù)的用法是GetPortVal 端口號(hào),存放讀出數(shù)據(jù)的變量,讀入的長度
  Loop While (dwVal And &H2)
End Sub
上面的是一個(gè)根據(jù)KBC規(guī)范寫的過程,它的作用是在向鍵盤端口寫入數(shù)據(jù)前等待一段時(shí)間,后面將會(huì)用到。
然后再添加如下過程,這2個(gè)過程用來模擬按鍵:

Public Const KBC_KEY_CMD = &H64    '鍵盤命令端口
Public Const KBC_KEY_DATA = &H60   '鍵盤數(shù)據(jù)端口

Sub MyKeyDown(ByVal vKeyCoad As Long)   
'這個(gè)用來模擬按下鍵,參數(shù)vKeyCoad傳入按鍵的虛擬碼
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)
  
    KBCWait4IBE   '發(fā)送數(shù)據(jù)前應(yīng)該先等待鍵盤緩沖區(qū)為空
    SetPortVal KBC_KEY_CMD, &HD2, 1     '發(fā)送鍵盤寫入命令
'SetPortVal函數(shù)用于向端口寫入數(shù)據(jù),它的用法是SetPortVal 端口號(hào),欲寫入的數(shù)據(jù),寫入數(shù)據(jù)的長度
    KBCWait4IBE
    SetPortVal KBC_KEY_DATA, btScancode, 1  '寫入按鍵信息,按下鍵
    
End Sub

 Sub MyKeyUp(ByVal vKeyCoad As Long)   
'這個(gè)用來模擬釋放鍵,參數(shù)vKeyCoad傳入按鍵的虛擬碼
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)
  
    KBCWait4IBE   '等待鍵盤緩沖區(qū)為空
    SetPortVal KBC_KEY_CMD, &HD2, 1  '發(fā)送鍵盤寫入命令
    KBCWait4IBE
    SetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1  '寫入按鍵信息,釋放鍵

End Sub


定義了上面的過程后,就可以用它來模擬鍵盤輸入了。在窗體模塊中添加一個(gè)定時(shí)器控件,然后加入以下代碼:


Private Sub Form_Load()

 

 If InitializeWinIo = False Then   
  '用InitializeWinIo函數(shù)加載驅(qū)動(dòng)程序,如果成功會(huì)返回true,否則返回false
    MsgBox "驅(qū)動(dòng)程序加載失敗!"
    Unload Me
 End If
Timer1.Interval=3000
Timer1.Enabled=True
End Sub

Private Sub Form_Unload(Cancel As Integer)
 ShutdownWinIo '程序結(jié)束時(shí)記得用ShutdownWinIo函數(shù)卸載驅(qū)動(dòng)程序
End Sub

Private Sub Timer1_Timer()
Dim VK_A as Long = &H41 
MyKeyDown VK_A    
MyKeyUp VK_A    '模擬按下并釋放A鍵
End Sub
[/quote]
運(yùn)行上面的程序,就會(huì)每隔3秒鐘模擬按下一次A鍵,試試看,怎么樣,是不是對(duì)所有程序都有效果了?
需要注意的問題:
要在VB的調(diào)試模式下使用WINIO,需要把那3個(gè)文件拷貝到VB的安裝目錄中。
鍵盤上有些鍵屬于擴(kuò)展鍵(比如鍵盤上的方向鍵就是擴(kuò)展鍵),對(duì)于擴(kuò)展鍵不應(yīng)該用上面的MyKeyDown和MyKeyUp過程來模擬,可以使用下面的2個(gè)過程來準(zhǔn)確模擬擴(kuò)展鍵:
[quote]Sub MyKeyDownEx(ByVal vKeyCoad As Long)   '模擬擴(kuò)展鍵按下,參數(shù)vKeyCoad是擴(kuò)展鍵的虛擬碼
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

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


Sub MyKeyUpEx(ByVal vKeyCoad As Long)   '模擬擴(kuò)展鍵彈起
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

    KBCWait4IBE   '等待鍵盤緩沖區(qū)為空
    SetPortVal KBC_KEY_CMD, &HD2, 1     '發(fā)送鍵盤寫入命令
    KBCWait4IBE
    SetPortVal KBC_KEY_DATA, &HE0, 1  '寫入擴(kuò)展鍵標(biāo)志信息
    
    
    KBCWait4IBE   '等待鍵盤緩沖區(qū)為空
    SetPortVal KBC_KEY_CMD, &HD2, 1     '發(fā)送鍵盤寫入命令
    KBCWait4IBE
    SetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1  '寫入按鍵信息,釋放鍵
    
End Sub
[/quote]
還應(yīng)該注意的是,如果要從擴(kuò)展鍵轉(zhuǎn)換到普通鍵,那么普通鍵的KeyDown事件應(yīng)該發(fā)送兩次。也就是說,如果我想模擬先按下一個(gè)擴(kuò)展鍵,再按下一個(gè)普通鍵,那么就應(yīng)該向端口發(fā)送兩次該普通鍵被按下的信息。比如,我想模擬先按下左方向鍵,再按下空格鍵這個(gè)事件,由于左方向鍵是擴(kuò)展鍵,空格鍵是普通鍵,那么流程就應(yīng)該是這樣的:
[quote]MyKeyDownEx VK_LEFT   '按下左方向鍵
Sleep 200             '延時(shí)200毫秒
MyKeyUpEx VK_LEFT     '釋放左方向鍵

Sleep 500
MyKeyDown VK_SPACE   '按下空格鍵,注意要發(fā)送兩次
MyKeyDown VK_SPACE
Sleep 200
MyKeyUp VK_SPACE     '釋放空格鍵



好了,相信到這里,你的模擬按鍵程序也就差不多了,測試一下,是不是很有效呢,嘿嘿~~~~
WINIO組件的下載地址:http://www.114vip.com.cn/download/winio.zip
4.骨灰級(jí)模擬
    方法3算是很底層的模擬了,我現(xiàn)在還沒有發(fā)現(xiàn)有它模擬無效的程序。但是如果你用盡上面所有的方法,仍然無效的話,那么還有最后一個(gè)方法,絕對(duì)對(duì)任何程序都會(huì)有效,那就是:把鍵盤拿出來,老老實(shí)實(shí)地按下去吧。~~~~

posted on 2007-07-20 15:50 聶文龍 閱讀(3463) 評(píng)論(3)  編輯 收藏 引用 所屬分類: c++

FeedBack:
# re: 讓VB菜鳥最快寫出自己的外掛.通殺所有游戲 2009-09-24 14:51 
你好:
請(qǐng)問

Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

最後是AS ANY,我用VB照打上去結(jié)果出現(xiàn)錯(cuò)誤訊息。
PostMessage hwnd,WM_UP,VK_A,MakeKeyLparam(VK_A,WM_UP)
WM_UP是不是應(yīng)該是WM_KEYUP ?

Secondbyte = Right("00" & Hex(Scancode), 2)
這一行說錯(cuò)誤
'Public ReadOnly Property Right() As Integer' 沒有引數(shù),而且無法對(duì)其傳回型別進(jìn)行索引。 D:\VB\Test4mapvirtualkey\Test4mapvirtualkey\Form1.vb 22 22 Test4mapvirtualkey

這個(gè)該修改成什麼呢?
另外再請(qǐng)問hwnd這個(gè)是不是要再另外declare 參數(shù)。
能不能再詳細(xì)教一下hwnd的用法。
我目前有遇到的問題是:
可以用hwnd 來find window ,
可以找到記事本的window ,
但找不到telnet的window。
請(qǐng)問該怎麼解決呢。
感謝回應(yīng)。
  回復(fù)  更多評(píng)論
  
# re: 讓VB菜鳥最快寫出自己的外掛.通殺所有游戲 2009-09-24 14:51 
你好:
請(qǐng)問

Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

最後是AS ANY,我用VB照打上去結(jié)果出現(xiàn)錯(cuò)誤訊息。
PostMessage hwnd,WM_UP,VK_A,MakeKeyLparam(VK_A,WM_UP)
WM_UP是不是應(yīng)該是WM_KEYUP ?

Secondbyte = Right("00" & Hex(Scancode), 2)
這一行說錯(cuò)誤
'Public ReadOnly Property Right() As Integer' 沒有引數(shù),而且無法對(duì)其傳回型別進(jìn)行索引。 D:\VB\Test4mapvirtualkey\Test4mapvirtualkey\Form1.vb 22 22 Test4mapvirtualkey

這個(gè)該修改成什麼呢?
另外再請(qǐng)問hwnd這個(gè)是不是要再另外declare 參數(shù)。
能不能再詳細(xì)教一下hwnd的用法。
我目前有遇到的問題是:
可以用hwnd 來find window ,
可以找到記事本的window ,
但找不到telnet的window。
請(qǐng)問該怎麼解決呢。
感謝回應(yīng)。
  回復(fù)  更多評(píng)論
  
# re: 讓VB菜鳥最快寫出自己的外掛.通殺所有游戲 2012-06-11 03:47 Peter Wang
骨灰級(jí)! 我喜歡 wo....  回復(fù)  更多評(píng)論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              久久久噜噜噜| 久久成人一区二区| 国产精品女人久久久久久| 欧美黄色网络| 欧美日韩国产123| 欧美日韩免费观看一区=区三区 | 亚洲国产网站| 欧美11—12娇小xxxx| 欧美顶级艳妇交换群宴| 91久久精品国产91久久性色tv| 最新国产乱人伦偷精品免费网站 | 亚洲高清自拍| 亚洲美女性视频| 性色av一区二区三区红粉影视| 久久国产主播| 欧美激情2020午夜免费观看| 国产精品久久久久久久久久ktv| 国产精品亚洲一区二区三区在线| 国产亚洲一区二区三区在线观看 | 久久久7777| 亚洲国产小视频在线观看| 亚洲免费中文| 欧美激情精品久久久久| 国产欧美不卡| 一区二区日韩免费看| 久久免费视频在线观看| 一本到12不卡视频在线dvd| 久久国产66| 国产精品久久久久久久久久免费看 | 国产精品久久久| 亚洲电影av在线| 亚洲欧美视频在线| 亚洲国产天堂久久综合| 欧美淫片网站| 国产精品福利在线观看| 亚洲乱码视频| 欧美国产日本在线| 欧美在线看片a免费观看| 欧美三级视频在线| 午夜一级在线看亚洲| 亚洲免费一在线| 亚洲国产老妈| 久久久久久久一区二区三区| 国产精品每日更新| 日韩一二在线观看| 欧美国产精品| 久久人人超碰| 国产日韩欧美视频| 午夜精品福利电影| 一区二区三区四区五区视频| 麻豆91精品91久久久的内涵| 国产日韩精品一区二区| 午夜激情一区| 亚洲小说欧美另类婷婷| 欧美激情亚洲自拍| 亚洲精品综合精品自拍| 亚洲电影网站| 欧美激情中文字幕一区二区 | 在线欧美日韩精品| 久久久国产精品一区| 亚洲欧美日韩第一区| 国产精品麻豆欧美日韩ww| 亚洲天堂久久| 亚洲一区二区在线播放| 国产精品永久| 久久精品日产第一区二区| 先锋影音网一区二区| 狠狠色丁香婷婷综合影院| 美女精品在线观看| 老司机午夜免费精品视频| 亚洲国产视频一区二区| 欧美大片在线观看一区| 欧美成人精品一区| 中国成人亚色综合网站| 一区二区三区国产盗摄| 国产欧美一区二区三区在线老狼 | 亚洲国产精品999| 欧美日韩国产二区| 欧美一区二区三区精品电影| 午夜精品一区二区三区在线视| 国产精品视频一| 麻豆精品在线视频| 欧美va亚洲va国产综合| 在线视频日韩精品| 亚洲专区一二三| 国语自产精品视频在线看一大j8 | 亚洲欧美日韩视频一区| 国内精品嫩模av私拍在线观看| 久久免费观看视频| 欧美99久久| 亚洲一区二区综合| 欧美福利视频| av72成人在线| 国产欧美日韩三级| 欧美va天堂在线| 欧美区国产区| 久久国产精品99久久久久久老狼| 久久久久久成人| 亚洲综合国产| 欧美成人免费观看| 久久av一区二区三区漫画| 免费看黄裸体一级大秀欧美| 亚洲一区二区黄色| 牛牛精品成人免费视频| 欧美在线视频二区| 欧美日韩精品免费观看视一区二区 | 国产精品扒开腿爽爽爽视频| 久久久久久九九九九| 欧美精品一区二区三区在线播放| 久久国产一区二区| 国产精品成人免费视频 | 亚洲欧美乱综合| 亚洲人线精品午夜| 久久精选视频| 亚洲综合大片69999| 麻豆freexxxx性91精品| 久久aⅴ国产欧美74aaa| a4yy欧美一区二区三区| 午夜视频在线观看一区二区三区| 亚洲人成网站在线观看播放| 亚洲女同同性videoxma| 夜夜躁日日躁狠狠久久88av| 久久久久久9| 性欧美8khd高清极品| 欧美激情视频一区二区三区免费 | 国精品一区二区| 亚洲欧美另类在线观看| 亚洲一区二区欧美日韩| 欧美国产精品日韩| 欧美成人一区二区在线| 国产一区二区三区高清在线观看| 夜夜躁日日躁狠狠久久88av| 99亚洲一区二区| 欧美成人黄色小视频| 欧美激情一区二区三级高清视频| 国际精品欧美精品| 久久久精品视频成人| 另类av导航| 91久久精品美女| 欧美激情综合五月色丁香| 日韩亚洲国产欧美| 亚洲网站视频| 国产精品美女久久久| 一区二区三区精品| 欧美一级大片在线观看| 国产日本欧美在线观看| 欧美淫片网站| 亚洲高清一二三区| 99av国产精品欲麻豆| 久久天天躁狠狠躁夜夜av| 亚洲第一在线视频| 玖玖在线精品| 国产精品一页| 亚洲视频一二| 国产精品99久久久久久www| 欧美不卡福利| 亚洲每日在线| 欧美亚洲免费在线| 国户精品久久久久久久久久久不卡 | 激情欧美日韩一区| 乱码第一页成人| 91久久精品一区二区三区| 亚洲全黄一级网站| 欧美日韩伦理在线免费| 亚洲在线成人| 免费亚洲电影在线观看| 一区二区三区免费观看| 国产人久久人人人人爽| 毛片一区二区| 亚洲男同1069视频| 亚洲国产裸拍裸体视频在线观看乱了 | 亚洲香蕉伊综合在人在线视看| 久久精品视频网| 亚洲国产欧美一区二区三区久久| 欧美日韩成人激情| 欧美在线999| 日韩视频一区二区三区在线播放免费观看 | 亚洲一区二区成人| 欧美日韩在线高清| 午夜精品久久久久久久久久久久 | 亚洲欧洲精品一区二区三区| 狂野欧美激情性xxxx欧美| 亚洲激情精品| 国产精品国产三级国产专播精品人| 欧美一级理论片| 亚洲人体影院| 久久久久99| 亚洲女人小视频在线观看| 亚洲人精品午夜| 国产一区二区欧美日韩| 欧美日韩精品一本二本三本| 久久精品国产第一区二区三区| 亚洲精品五月天| 欧美激情女人20p| 久久精品亚洲热| 欧美一区二区成人| 久久aⅴ国产欧美74aaa| 在线观看亚洲| 国产精品一区三区| 亚洲视频播放|