Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2496894
  • 博文数量: 319
  • 博客积分: 9650
  • 博客等级: 中将
  • 技术积分: 3881
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-27 21:05
文章分类

全部博文(319)

文章存档

2017年(5)

2016年(10)

2015年(3)

2014年(3)

2013年(10)

2012年(26)

2011年(67)

2010年(186)

2009年(9)

分类: 嵌入式

2011-12-10 10:45:12

模拟键盘%B7%BF%B6%F7%BA%EA/blog/item/621c35c45b7a2fae8226ac2a.html

 

 

待解决问题
vb WM_KEYDOWN 参数设置????
  (离问题结束还有0天0小时)
id="question_content">Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
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
Dim cqhwnd As Long
Private Const VK_F1 = &H70
Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Private Const WM_CLOSE = &H10

Private Sub Command2_Click()
PostMessage cqhwnd, WM_KEYDOWN, VK_A, 0&
PostMessage cqhwnd, WM_KEYUP, VK_A, 0&
End Sub

模拟按下按键。
=================
Private Const VK_F1 = &H70
Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Private Const WM_CLOSE = &H10
这些什么意思?貌似赋值?
如果我想模拟按Q键,应该赋值为什么?
为什么有些会输出2次?
提问者:  提问时间:09-06-17 14:39 
 
 
'函数申明
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal Scan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Const KEYEVENTF_KEYUP = &H2 '释放按键常数

'回车键例子
Private Sub Command1_Click()
Text1.SetFocus
Call keybd_event(13, 0, 0, 0) '按下
Call keybd_event(13, 0, KEYEVENTF_KEYUP, 0) '释放
End Sub


键码
常数 值 描述
vbKeyLButton 1 鼠标左键
vbKeyRButton 2 鼠标右键
vbKeyCancel 3 CANCEL 键
vbKeyMButton 4 鼠标中键
vbKeyBack 8 BACKSPACE 键
vbKeyTab 9 TAB 键
vbKeyClear 12 CLEAR 键
vbKeyReturn 13 ENTER 键
vbKeyShift 16 SHIFT 键
vbKeyControl 17 CTRL 键
vbKeyMenu 18 菜单键
vbKeyPause 19 PAUSE 键
vbKeyCapital 20 CAPS LOCK 键
vbKeyEscape 27 ESC 键
vbKeySpace 32 SPACEBAR 键
vbKeyPageUp 33 PAGEUP 键
vbKeyPageDown 34 PAGEDOWN 键
vbKeyEnd 35 END 键
vbKeyHome 36 HOME 键
vbKeyLeft 37 LEFT ARROW 键
vbKeyUp 38 UP ARROW 键
vbKeyRight 39 RIGHT ARROW 键
vbKeyDown 40 DOWN ARROW 键
vbKeySelect 41 SELECT 键
vbKeyPrint 42 PRINT SCREEN 键
vbKeyExecute 43 EXECUTE 键
vbKeySnapshot 44 SNAP SHOT 键
vbKeyInser 45 INS 键
vbKeyDelete 46 DEL 键
vbKeyHelp 47 HELP 键
vbKeyNumlock 144 NUM LOCK 键

vbKeyA 65 A 键
vbKeyB 66 B 键
vbKeyC 67 C 键
vbKeyD 68 D 键
vbKeyE 69 E 键
vbKeyF 70 F 键
vbKeyG 71 G 键
vbKeyH 72 H 键
vbKeyI 73 I 键
vbKeyJ 74 J 键
vbKeyK 75 K 键
vbKeyL 76 L 键
vbKeyM 77 M 键
vbKeyN 78 N 键
vbKeyO 79 O 键
vbKeyP 80 P 键
vbKeyQ 81 Q 键
vbKeyR 82 R 键
vbKeyS 83 S 键
vbKeyT 84 T 键
vbKeyU 85 U 键
vbKeyV 86 V 键
vbKeyW 87 W 键
vbKeyX 88 X 键
vbKeyY 89 Y 键
vbKeyZ 90 Z 键
vbKey0 48 0 键
vbKey1 49 1 键
vbKey2 50 2 键
vbKey3 51 3 键
vbKey4 52 4 键
vbKey5 53 5 键
vbKey6 54 6 键
vbKey7 55 7 键
vbKey8 56 8 键
vbKey9 57 9 键
vbKeyF1 112 F1 键
vbKeyF2 113 F2 键
vbKeyF3 114 F3 键
vbKeyF4 115 F4 键
vbKeyF5 116 F5 键
vbKeyF6 117 F6 键
vbKeyF7 118 F7 键
vbKeyF8 119 F8 键
vbKeyF9 120 F9 键
vbKeyF10 121 F10 键
vbKeyF11 122 F11 键
vbKeyF12 123 F12 键
vbKeyF13 124 F13 键
vbKeyF14 125 F14 键
vbKeyF15 126 F15 键
vbKeyF16 127 F16 键

 

 

这年头,在这个论坛里面已经没有什么技术贴了...呵呵~发一篇惊天地,泣鬼神的帖子.当然这个只是模拟键盘的终极模拟.呵呵~
      键盘是我们使用计算机的一个很重要的输入设备了,即使在鼠标大行其道的今天,很多程序依然离不开键盘来操作。但是有时候,一些重复性的,很繁琐的键盘操作 总会让人疲惫,于是就有了用程序来代替人们按键的方法,这样可以把很多重复性的键盘操作交给程序来模拟,省了很多精力,按键精灵就是这样的一个软件。那么 我们怎样才能用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参数的函数:

  1. Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As LongByVal wMapType As LongAs Long  
  2.   
  3. Function MakeKeyLparam(ByVal VirtualKey As LongByVal flag As LongAs Long  
  4. '参数VirtualKey表示按键虚拟码,flag表示是按下键还是释放键,用WM_KEYDOWN和WM_KEYUP这两个常数表示  
  5.      Dim s As String  
  6.      Dim Firstbyte As String     'lparam参数的24-31位  
  7.      If flag = WM_KEYDOWN   Then '如果是按下键  
  8.          Firstbyte = "00"  
  9.      Else  
  10.          Firstbyte = "C0"        '如果是释放键  
  11.      End If  
  12.      Dim Scancode As Long  
  13.      '获得键的扫描码  
  14.      Scancode = MapVirtualKey(VirtualKey, 0)  
  15.      Dim Secondbyte As String    'lparam参数的16-23位,即虚拟键扫描码  
  16.      Secondbyte = Right("00" & Hex(Scancode), 2)  
  17.      s = Firstbyte & Secondbyte & "0001"   '0001为lparam参数的0-15位,即发送次数和其它扩展信息  
  18.      MakeKeyLparam = Val("&H" & s)  
  19. 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 编码(可以输入中文的哦^_^),现在你可以写出一个完整的向记事本里自动写入字符的程序了,下面是一个例子,并附有这些消息常数的具体值:

  1. Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As LongByVal wMsg As LongByVal wParam As Long, lParam As Any) As Long  
  2. Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As LongByVal wMapType As LongAs Long  
  3.   
  4. Public Const WM_KEYDOWN = &H100  
  5. Public Const WM_KEYUP = &H101  
  6. Public Const WM_CHAR = &H102  
  7. Public Const VK_A = &H41   
  8.   
  9. Function MakeKeyLparam(ByVal VirtualKey As LongByVal flag As LongAs Long  
  10.      Dim s As String  
  11.      Dim Firstbyte As String     'lparam参数的24-31位  
  12.      If flag = WM_KEYDOWN   Then '如果是按下键  
  13.          Firstbyte = "00"  
  14.      Else  
  15.          Firstbyte = "C0"        '如果是释放键  
  16.      End If  
  17.      Dim Scancode As Long  
  18.      '获得键的扫描码  
  19.      Scancode = MapVirtualKey(VirtualKey, 0)  
  20.      Dim Secondbyte As String    'lparam参数的16-23位,即虚拟键扫描码  
  21.      Secondbyte = Right("00" & Hex(Scancode), 2)  
  22.      s = Firstbyte & Secondbyte & "0001"   '0001为lparam参数的0-15位,即发送次数和其它扩展信息  
  23.      MakeKeyLparam = Val("&H" & s)  
  24. End Function  
  25.   
  26. Private Sub Form_Load()  
  27.      dim hwnd as long  
  28.      hwnd = XXXXXX   'XXXXX表示记事本编辑框的句柄  
  29.      PostMessage hwnd,WM_KEYDOWN,VK_A,MakeKeyLparam(VK_A,WM_KEYDOWN)   '按下A键  
  30.      PostMessage hwnd,WM_CHAR,ASC("A"),MakeKeyLparam(VK_A,WM_KEYDOWN)   '输入字符A  
  31.      PostMessage hwnd,WM_UP,VK_A,MakeKeyLparam(VK_A,WM_UP)        '释放A键  
  32. End Sub  

这 就是通过局部键盘消息来模拟按键。这个方法有一个极大的好处,就是:它可以实现后台按键,也就是说他对你的前台操作不会有什么影响。比如,你可以用这个方 法做个程序在游戏中模拟按键来不断地执行某些重复的操作,而你则一边喝茶一边与QQ上的MM们聊得火热,它丝毫不会影响你的前台操作。无论目标程序是否获 得焦点都没有影响,这就是后台模拟按键的原理啦~~~~


2.全局级模拟

     你会发现,用上面的方法模拟按键并不 是对所有程序都有效的,有的程序啊,你向它发了一大堆消息,可是它却一点反应也没有。这是怎么回事呢?这就要看具体的情况了,有些程序(特别是一些游戏) 出于某些原因,会禁止用户对它使用模拟按键程序,这个怎么实现呢?比如可以在程序中检查一下,如果发现自己不是活动窗口,就不接受键盘消息。或者仔细检查 一下收到的键盘消息,你会发现真实的按键和模拟的按键消息总是有一些小差别,从这些小差别上,目标程序就能判断出:这是假的!是伪造的!!因此,如果用 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插人的键盘或鼠标的输入流不兼容。
嗯,这个函数用起来蛮复杂的,因为它的参数都是指针一类的东西。要用它来模拟键盘输入,先要构造一组数据结构,把你要模拟的键盘消息装进去,然后传给它。为了方便起见,把它做在一个过程里面,要用的时候直接调用好了,代码如下:

  1. Declare Function SendInput Lib "user32.dll" (ByVal nInputs As Long, pInputs As GENERALINPUT, ByVal cbSize As LongAs Long  
  2. Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)  
  3. Type GENERALINPUT  
  4.     dwType As Long  
  5.     xi(0 To 23) As Byte  
  6. End Type  
  7.   
  8. Type KEYBDINPUT  
  9.    wVk As Integer  
  10.    wScan As Integer  
  11.    dwFlags As Long  
  12.    time As Long  
  13.    dwExtraInfo As Long  
  14. End Type  
  15.   
  16. Const INPUT_KEYBOARD = 1  
  17.   
  18. Sub MySendKey(bkey As Long)  
  19. '参数bkey传入要模拟按键的虚拟码即可模拟按下指定键  
  20. Dim GInput(0 To 1) As GENERALINPUT  
  21. Dim KInput As KEYBDINPUT  
  22. KInput.wVk = bkey   '你要模拟的按键  
  23. KInput.dwFlags = 0 '按下键标志  
  24. GInput(0).dwType = INPUT_KEYBOARD  
  25. CopyMemory GInput(0).xi(0), KInput, Len(KInput) '这个函数用来把内存中KInput的数据复制到GInput  
  26. KInput.wVk = bkey    
  27. KInput.dwFlags = KEYEVENTF_KEYUP   ' 释放按键  
  28. GInput(1).dwType = INPUT_KEYBOARD ' 表示该消息为键盘消息  
  29. CopyMemory GInput(1).xi(0), KInput, Len(KInput)  
  30. '以上工作把按下键和释放键共2条键盘消息加入到GInput数据结构中  
  31. SendInput 2, GInput(0), Len(GInput(0))     '把GInput中存放的消息插入到消息列队  
  32. 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都不会有反应,因为这些函数都在较高层。对于这样的程序,只好用直接读写键 盘端口的方法来模拟硬件事件了。要用这个方法来模拟键盘,需要先了解一下键盘编程的相关知识。
     在DOS时代,当用户按下或者放开一个键 时,就会产生一个键盘中断(如果键盘中断是允许的),这样程序会跳转到BIOS中的键盘中断处理程序去执行。打开windows的设备管理器,可以查看到 键盘控制器由两个端口控制。其中&H60是数据端口,可以读出键盘数据,而&H64是控制端口,用来发出控制信号。也就是,从& H60号端口可以读此键盘的按键信息,当从这个端口读取一个字节,该字节的低7位就是按键的扫描码,而高1位则表示是按下键还是释放键。当按下键时,最高 位为0,称为通码,当释放键时,最高位为1,称为断码。既然从这个端口读数据可以获得按键信息,那么向这个端口写入数据就可以模拟按键了!用过 QbASIC4.5的朋友可能知道,QB中有个OUT命令可以向指定端口写入数据,而INP函数可以读取指定端口的数据。那我们先看看如果用QB该怎么写 代码:
假如你想模拟按下一个键,这个键的扫描码为&H50,那就这样
OUT &H64,&HD2    '把数据&HD2发送到&H64端口。这是一个KBC指令,表示将要向键盘写入数据
OUT &H60,&H50    '把扫描码&H50发送到&H60端口,表示模拟按下扫描码为&H50的这个键
那么要释放这个键呢?像这样,发送该键的断码:
OUT &H64,&HD2    '把数据&HD2发送到&H64端口。这是一个KBC指令,表示将要向键盘写入数据
OUT &H60,(&H50 OR &H80)    '把扫描码&H50与数据&H80进行或运算,可以把它的高位置1,得到断码,表示释放这个键
     好了,现在的问题就是在VB中如何向端口写入数据了。因为在windows中,普通应用程序是无权操作端口的,于是我们就需要一个驱动程序来帮助我们实 现。在这里我们可以使用一个组件WINIO来完成读写端口操作。什么是WINIO?WINIO是一个全免费的、无需注册的、含源程序的 WINDOWS2000端口操作驱动程序组件(可以到上 去下载)。它不仅可以操作端口,还可以操作内存;不仅能在VB下用,还可以在DELPHI、VC等其它环境下使用,性能特别优异。下载该组件,解压缩后可 以看到几个文件夹,其中Release文件夹下的3个文件就是我们需要的,这3个文件是WinIo.sys(用于win xp下的驱动程序), WINIO.VXD(用于win 98下的驱动程序),WinIo.dll(封装函数的动态链接库),我们只需要调用WinIo.dll中的函数,然后 WinIo.dll就会安装并调用驱动程序来完成相应的功能。值得一提的是这个组件完全是绿色的,无需安装,你只需要把这3个文件复制到与你的程序相同的 文件夹下就可以使用了。用法很简单,先用里面的InitializeWinIo函数安装驱动程序,然后就可以用GetPortVal来读取端口或者用 SetPortVal来写入端口了。好,让我们来做一个驱动级的键盘模拟吧。先把winio的3个文件拷贝到你的程序的文件夹下,然后在VB中新建一个工 程,添加一个模块,在模块中加入下面的winio函数声明:

  1. Declare Function MapPhysToLin Lib "WinIo.dll" (ByVal PhysAddr As LongByVal PhysSize As LongByRef PhysMemHandle) As Long  
  2. Declare Function UnmapPhysicalMemory Lib "WinIo.dll" (ByVal PhysMemHandle, ByVal LinAddr) As Boolean  
  3. Declare Function GetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As LongByRef PhysVal As LongAs Boolean  
  4. Declare Function SetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As LongByVal PhysVal As LongAs Boolean  
  5. Declare Function GetPortVal Lib "WinIo.dll" (ByVal PortAddr As IntegerByRef PortVal As LongByVal bSize As ByteAs Boolean  
  6. Declare Function SetPortVal Lib "WinIo.dll" (ByVal PortAddr As IntegerByVal PortVal As LongByVal bSize As ByteAs Boolean  
  7. Declare Function InitializeWinIo Lib "WinIo.dll" () As Boolean  
  8. Declare Function ShutdownWinIo Lib "WinIo.dll" () As Boolean  
  9. Declare Function InstallWinIoDriver Lib "WinIo.dll" (ByVal DriverPath As StringByVal Mode As IntegerAs Boolean  
  10. Declare Function RemoveWinIoDriver Lib "WinIo.dll" () As Boolean  
  11.   
  12. ' ------------------------------------以上是WINIO函数声明-------------------------------------------  
  13.   
  14. Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As LongByVal wMapType As LongAs Long  
  15.   
  16. '-----------------------------------以上是WIN32 API函数声明-----------------------------------------  

再添加下面这个过程:

  1. Sub KBCWait4IBE()    '等待键盘缓冲区为空  
  2. Dim dwVal As Long  
  3.    Do  
  4.    GetPortVal &H64, dwVal, 1  
  5. '这句表示从&H64端口读取一个字节并把读出的数据放到变量dwVal中  
  6. 'GetPortVal函数的用法是GetPortVal 端口号,存放读出数据的变量,读入的长度  
  7.    Loop While (dwVal And &H2)  
  8. End Sub  

上面的是一个根据KBC规范写的过程,它的作用是在向键盘端口写入数据前等待一段时间,后面将会用到。
然后再添加如下过程,这2个过程用来模拟按键:

  1. Public Const KBC_KEY_CMD = &H64     '键盘命令端口  
  2. Public Const KBC_KEY_DATA = &H60    '键盘数据端口  
  3.   
  4. Sub MyKeyDown(ByVal vKeyCoad As Long)     
  5. '这个用来模拟按下键,参数vKeyCoad传入按键的虚拟码  
  6. Dim btScancode As Long  
  7. btScancode = MapVirtualKey(vKeyCoad, 0)  
  8.     
  9.      KBCWait4IBE    '发送数据前应该先等待键盘缓冲区为空  
  10.      SetPortVal KBC_KEY_CMD, &HD2, 1      '发送键盘写入命令  
  11. 'SetPortVal函数用于向端口写入数据,它的用法是SetPortVal 端口号,欲写入的数据,写入数据的长度  
  12.      KBCWait4IBE  
  13.      SetPortVal KBC_KEY_DATA, btScancode, 1   '写入按键信息,按下键  
  14.       
  15. End Sub  
  16.   
  17. Sub MyKeyUp(ByVal vKeyCoad As Long)     
  18. '这个用来模拟释放键,参数vKeyCoad传入按键的虚拟码  
  19. Dim btScancode As Long  
  20. btScancode = MapVirtualKey(vKeyCoad, 0)  
  21.     
  22.      KBCWait4IBE    '等待键盘缓冲区为空  
  23.      SetPortVal KBC_KEY_CMD, &HD2, 1   '发送键盘写入命令  
  24.      KBCWait4IBE  
  25.      SetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1   '写入按键信息,释放键  
  26.   
  27. End Sub  


定义了上面的过程后,就可以用它来模拟键盘输入了。在窗体模块中添加一个定时器控件,然后加入以下代码:


  1. Private Sub Form_Load()   
  2. If InitializeWinIo = False Then     
  3.    '用InitializeWinIo函数加载驱动程序,如果成功会返回true,否则返回false  
  4.      MsgBox "驱动程序加载失败!"  
  5.      Unload Me  
  6. End If  
  7. Timer1.Interval=3000  
  8. Timer1.Enabled=True  
  9. End Sub  
  10.   
  11. Private Sub Form_Unload(Cancel As Integer)  
  12. ShutdownWinIo '程序结束时记得用ShutdownWinIo函数卸载驱动程序  
  13. End Sub  
  14.   
  15. Private Sub Timer1_Timer()  
  16. Dim VK_A as Long = &H41   
  17. MyKeyDown VK_A      
  18. MyKeyUp VK_A     '模拟按下并释放A键  
  19. End Sub  

[/quote]
运行上面的程序,就会每隔3秒钟模拟按下一次A键,试试看,怎么样,是不是对所有程序都有效果了?
需要注意的问题:
要在VB的调试模式下使用WINIO,需要把那3个文件拷贝到VB的安装目录中。
键盘上有些键属于扩展键(比如键盘上的方向键就是扩展键),对于扩展键不应该用上面的MyKeyDown和MyKeyUp过程来模拟,可以使用下面的2个过程来准确模拟扩展键:
[quote]

 

  1. Sub MyKeyDownEx(ByVal vKeyCoad As Long)    '模拟扩展键按下,参数vKeyCoad是扩展键的虚拟码  
  2. Dim btScancode As Long  
  3. btScancode = MapVirtualKey(vKeyCoad, 0)  
  4.   
  5.      KBCWait4IBE    '等待键盘缓冲区为空  
  6.      SetPortVal KBC_KEY_CMD, &HD2, 1      '发送键盘写入命令  
  7.      KBCWait4IBE  
  8.      SetPortVal KBC_KEY_DATA, &HE0, 1   '写入扩展键标志信息  
  9.       
  10.       
  11.      KBCWait4IBE    '等待键盘缓冲区为空  
  12.      SetPortVal KBC_KEY_CMD, &HD2, 1      '发送键盘写入命令  
  13.      KBCWait4IBE  
  14.      SetPortVal KBC_KEY_DATA, btScancode, 1   '写入按键信息,按下键  
  15.       
  16.       
  17. End Sub  
  18.   
  19.   
  20. Sub MyKeyUpEx(ByVal vKeyCoad As Long)    '模拟扩展键弹起  
  21. Dim btScancode As Long  
  22. btScancode = MapVirtualKey(vKeyCoad, 0)  
  23.   
  24.      KBCWait4IBE    '等待键盘缓冲区为空  
  25.      SetPortVal KBC_KEY_CMD, &HD2, 1      '发送键盘写入命令  
  26.      KBCWait4IBE  
  27.      SetPortVal KBC_KEY_DATA, &HE0, 1   '写入扩展键标志信息  
  28.       
  29.       
  30.      KBCWait4IBE    '等待键盘缓冲区为空  
  31.      SetPortVal KBC_KEY_CMD, &HD2, 1      '发送键盘写入命令  
  32.      KBCWait4IBE  
  33.      SetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1   '写入按键信息,释放键  
  34.       
  35. End Sub  


[/quote]
还 应该注意的是,如果要从扩展键转换到普通键,那么普通键的KeyDown事件应该发送两次。也就是说,如果我想模拟先按下一个扩展键,再按下一个普通键, 那么就应该向端口发送两次该普通键被按下的信息。比如,我想模拟先按下左方向键,再按下空格键这个事件,由于左方向键是扩展键,空格键是普通键,那么流程 就应该是这样的:
[quote]
 

  1. MyKeyDownEx VK_LEFT    '按下左方向键  
  2. Sleep 200              '延时200毫秒  
  3. MyKeyUpEx VK_LEFT      '释放左方向键  
  4.   
  5. Sleep 500  
  6. MyKeyDown VK_SPACE    '按下空格键,注意要发送两次  
  7. MyKeyDown VK_SPACE  
  8. Sleep 200  
  9. MyKeyUp VK_SPACE      '释放空格键  


好了,相信到这里,你的模拟按键程序也就差不多了,测试一下,是不是很有效呢,嘿嘿~~~~
WINIO组件的下载地址:
4.骨灰级模拟
     方法3算是很底层的模拟了,我现在还没有发现有它模拟无效的程序。但是如果你用尽上面所有的方法,仍然无效的话,那么还有最后一个方法,绝对对任何程序都会有效,那就是:把键盘拿出来,老老实实地按下去吧。~~~~

我用WINIO在VB下模拟鼠标左键点击,具体代码如下:

  1. Private Sub XR()  
  2.    Dim Result As Boolean  
  3.     
  4.    Result = SetPortVal(Val("&H64"), Val("&HD3"), 1)  
  5.     
  6.    If (Result = FalseThen  
  7.      MsgBox "Whoops ! There is a problem with SetPortByte.", vbOKOnly + vbCritical, "VBDumpPort32"  
  8.      Unload FrmVBDumpPort32  
  9.    End If  
  10.      Sleep 100  
  11.    Result = SetPortVal(Val("&H64"), Val("&Hf4"), 1)  
  12.     
  13.    If (Result = FalseThen  
  14.      MsgBox "Whoops ! There is a problem with SetPortByte.", vbOKOnly + vbCritical, "VBDumpPort32"  
  15.      Unload FrmVBDumpPort32  
  16.    End If  
  17.     
  18.    Result = SetPortVal(Val("&H60"), Val("&H09"), 1)  
  19.     
  20.    If (Result = FalseThen  
  21.      MsgBox "Whoops ! There is a problem with SetPortByte.", vbOKOnly + vbCritical, "VBDumpPort32"  
  22.      Unload FrmVBDumpPort32  
  23.    End If  
  24.       
  25.    Result = SetPortVal(Val("&H60"), Val("&H00"), 1)  
  26.     
  27.    If (Result = FalseThen  
  28.      MsgBox "Whoops ! There is a problem with SetPortByte.", vbOKOnly + vbCritical, "VBDumpPort32"  
  29.      Unload FrmVBDumpPort32  
  30.    End If  
  31.       
  32.    Result = SetPortVal(Val("&H60"), Val("&H00"), 1)  
  33.     
  34.    If (Result = FalseThen  
  35.      MsgBox "Whoops ! There is a problem with SetPortByte.", vbOKOnly + vbCritical, "VBDumpPort32"  
  36.      Unload FrmVBDumpPort32  
  37.    End If  
  38.       
  39.    Result = SetPortVal(Val("&H60"), Val("&H08"), 1)  
  40.     
  41.    If (Result = FalseThen  
  42.      MsgBox "Whoops ! There is a problem with SetPortByte.", vbOKOnly + vbCritical, "VBDumpPort32"  
  43.      Unload FrmVBDumpPort32  
  44.    End If  
  45.       
  46.    Result = SetPortVal(Val("&H60"), Val("&H00"), 1)  
  47.     
  48.    If (Result = FalseThen  
  49.      MsgBox "Whoops ! There is a problem with SetPortByte.", vbOKOnly + vbCritical, "VBDumpPort32"  
  50.      Unload FrmVBDumpPort32  
  51.    End If  
  52.       
  53.    Result = SetPortVal(Val("&H60"), Val("&H00"), 1)  
  54.     
  55.    If (Result = FalseThen  
  56.      MsgBox "Whoops ! There is a problem with SetPortByte.", vbOKOnly + vbCritical, "VBDumpPort32"  
  57.      Unload FrmVBDumpPort32  
  58.    End If  
  59. End Sub   

 

 

 

 

winio 模拟方向键:http://laomaspeak.blog.sohu.com/94919159.html

 

  1. '模块  
  2.   
  3. Public Declare Function MapPhysToLin Lib "WinIo.dll" (ByVal PhysAddr As LongByVal PhysSize As LongByRef PhysMemHandle) As Long  
  4. Public Declare Function UnmapPhysicalMemory Lib "WinIo.dll" (ByVal PhysMemHandle, ByVal LinAddr) As Boolean  
  5. Public Declare Function GetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As LongByRef PhysVal As LongAs Boolean  
  6. Public Declare Function SetPhysLong Lib "WinIo.dll" (ByVal PhysAddr As LongByVal PhysVal As LongAs Boolean  
  7. Public Declare Function GetPortVal Lib "WinIo.dll" (ByVal PortAddr As IntegerByRef PortVal As LongByVal bSize As ByteAs Boolean  
  8. Public Declare Function SetPortVal Lib "WinIo.dll" (ByVal PortAddr As IntegerByVal PortVal As LongByVal bSize As ByteAs Boolean  
  9. Public Declare Function InitializeWinIo Lib "WinIo.dll" () As Boolean  
  10. Public Declare Function ShutdownWinIo Lib "WinIo.dll" () As Boolean  
  11. Public Declare Function InstallWinIoDriver Lib "WinIo.dll" (ByVal DriverPath As StringByVal Mode As IntegerAs Boolean  
  12. Public Declare Function RemoveWinIoDriver Lib "WinIo.dll" () As Boolean  
  13.   
  14. ' ------------------------------------以上是WINIO函数声明-------------------------------------------  
  15. Public Const KBC_KEY_CMD = &H64    '键盘命令端口  
  16. Public Const KBC_KEY_DATA = &H60   '键盘数据端口  
  17. Public Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As LongByVal wMapType As LongAs Long  
  18. Public VK_A As Long  
  19.   
  20.   
  21. Public Const VK_LBUTTON = &H1  
  22. Public Const VK_RBUTTON = &H2  
  23. Public Const VK_CANCEL = &H3  
  24. Public Const VK_MBUTTON = &H4  
  25. Public Const VK_BACK = &H8  
  26. Public Const VK_TAB = &H9  
  27. Public Const VK_CLEAR = &HC  
  28. Public Const VK_RETURN = &HD  
  29. Public Const VK_SHIFT = &H10  
  30. Public Const VK_CONTROL = &H11  
  31. Public Const VK_MENU = &H12  
  32. Public Const VK_PAUSE = &H13  
  33. Public Const VK_CAPITAL = &H14  
  34. Public Const VK_ESCAPE = &H1B  
  35. Public Const VK_SPACE = &H20  
  36. Public Const VK_PRIOR = &H21  
  37. Public Const VK_NEXT = &H22  
  38. Public Const VK_END = &H23  
  39. Public Const VK_HOME = &H24  
  40. Public Const VK_LEFT = &H25  
  41. Public Const VK_UP = &H26  
  42. Public Const VK_RIGHT = &H27  
  43. Public Const VK_DOWN = &H28  
  44. Public Const VK_Select = &H29  
  45. Public Const VK_PRINT = &H2A  
  46. Public Const VK_EXECUTE = &H2B  
  47. Public Const VK_SNAPSHOT = &H2C  
  48. Public Const VK_Insert = &H2D  
  49. Public Const VK_Delete = &H2E  
  50. Public Const VK_HELP = &H2F  
  51. Public Const VK_0 = &H30  
  52. Public Const VK_1 = &H31  
  53. Public Const VK_2 = &H32  
  54. Public Const VK_3 = &H33  
  55. Public Const VK_4 = &H34  
  56. Public Const VK_5 = &H35  
  57. Public Const VK_6 = &H36  
  58. Public Const VK_7 = &H37  
  59. Public Const VK_8 = &H38  
  60. Public Const VK_9 = &H39  
  61. Public Const VK_B = &H42  
  62. Public Const VK_C = &H43  
  63. Public Const VK_D = &H44  
  64. Public Const VK_E = &H45  
  65. Public Const VK_F = &H46  
  66. Public Const VK_G = &H47  
  67. Public Const VK_H = &H48  
  68. Public Const VK_I = &H49  
  69. Public Const VK_J = &H4A  
  70. Public Const VK_K = &H4B  
  71. Public Const VK_L = &H4C  
  72. Public Const VK_M = &H4D  
  73. Public Const VK_N = &H4E  
  74. Public Const VK_O = &H4F  
  75. Public Const VK_P = &H50  
  76. Public Const VK_Q = &H51  
  77. Public Const VK_R = &H52  
  78. Public Const VK_S = &H53  
  79. Public Const VK_T = &H54  
  80. Public Const VK_U = &H55  
  81. Public Const VK_V = &H56  
  82. Public Const VK_W = &H57  
  83. Public Const VK_X = &H58  
  84. Public Const VK_Y = &H59  
  85. Public Const VK_Z = &H5A  
  86. Public Const VK_STARTKEY = &H5B  
  87. Public Const VK_CONTEXTKEY = &H5D  
  88. Public Const VK_NUMPAD0 = &H60  
  89. Public Const VK_NUMPAD1 = &H61  
  90. Public Const VK_NUMPAD2 = &H62  
  91. Public Const VK_NUMPAD3 = &H63  
  92. Public Const VK_NUMPAD4 = &H64  
  93. Public Const VK_NUMPAD5 = &H65  
  94. Public Const VK_NUMPAD6 = &H66  
  95. Public Const VK_NUMPAD7 = &H67  
  96. Public Const VK_NUMPAD8 = &H68  
  97. Public Const VK_NUMPAD9 = &H69  
  98. Public Const VK_MULTIPLY = &H6A  
  99. Public Const VK_ADD = &H6B  
  100. Public Const VK_SEPARATOR = &H6C  
  101. Public Const VK_SUBTRACT = &H6D  
  102. Public Const VK_DECIMAL = &H6E  
  103. Public Const VK_DIVIDE = &H6F  
  104. Public Const VK_F1 = &H70  
  105. Public Const VK_F2 = &H71  
  106. Public Const VK_F3 = &H72  
  107. Public Const VK_F4 = &H73  
  108. Public Const VK_F5 = &H74  
  109. Public Const VK_F6 = &H75  
  110. Public Const VK_F7 = &H76  
  111. Public Const VK_F8 = &H77  
  112. Public Const VK_F9 = &H78  
  113. Public Const VK_F10 = &H79  
  114. Public Const VK_F11 = &H7A  
  115. Public Const VK_F12 = &H7B  
  116. Public Const VK_F13 = &H7C  
  117. Public Const VK_F14 = &H7D  
  118. Public Const VK_F15 = &H7E  
  119. Public Const VK_F16 = &H7F  
  120. Public Const VK_F17 = &H80  
  121. Public Const VK_F18 = &H81  
  122. Public Const VK_F19 = &H82  
  123. Public Const VK_F20 = &H83  
  124. Public Const VK_F21 = &H84  
  125. Public Const VK_F22 = &H85  
  126. Public Const VK_F23 = &H86  
  127. Public Const VK_F24 = &H87  
  128. Public Const VK_NUMLOCK = &H90  
  129. Public Const VK_OEM_SCROLL = &H91  
  130. Public Const VK_OEM_1 = &HBA  
  131. Public Const VK_OEM_PLUS = &HBB  
  132. Public Const VK_OEM_COMMA = &HBC  
  133. Public Const VK_OEM_MINUS = &HBD  
  134. Public Const VK_OEM_PERIOD = &HBE  
  135. Public Const VK_OEM_2 = &HBF  
  136. Public Const VK_OEM_3 = &HC0  
  137. Public Const VK_OEM_4 = &HDB  
  138. Public Const VK_OEM_5 = &HDC  
  139. Public Const VK_OEM_6 = &HDD  
  140. Public Const VK_OEM_7 = &HDE  
  141. Public Const VK_OEM_8 = &HDF  
  142. Public Const VK_ICO_F17 = &HE0  
  143. Public Const VK_ICO_F18 = &HE1  
  144. Public Const VK_OEM102 = &HE2  
  145. Public Const VK_ICO_HELP = &HE3  
  146. Public Const VK_ICO_00 = &HE4  
  147. Public Const VK_ICO_CLEAR = &HE6  
  148. Public Const VK_OEM_RESET = &HE9  
  149. Public Const VK_OEM_JUMP = &HEA  
  150. Public Const VK_OEM_PA1 = &HEB  
  151. Public Const VK_OEM_PA2 = &HEC  
  152. Public Const VK_OEM_PA3 = &HED  
  153. Public Const VK_OEM_WSCTRL = &HEE  
  154. Public Const VK_OEM_CUSEL = &HEF  
  155. Public Const VK_OEM_ATTN = &HF0  
  156. Public Const VK_OEM_FINNISH = &HF1  
  157. Public Const VK_OEM_COPY = &HF2  
  158. Public Const VK_OEM_AUTO = &HF3  
  159. Public Const VK_OEM_ENLW = &HF4  
  160. Public Const VK_OEM_BACKTAB = &HF5  
  161. Public Const VK_CRSEL = &HF7  
  162. Public Const VK_EXSEL = &HF8  
  163. Public Const VK_EREOF = &HF9  
  164. Public Const VK_PLAY = &HFA  
  165. Public Const VK_ZOOM = &HFB  
  166. Public Const VK_NONAME = &HFC  
  167. Public Const VK_PA1 = &HFD  
  168. Public Const VK_OEM_CLEAR = &HFE  
  169. 'Public Button As ComboBox  
  170.   
  171. '-----------------------------------以上是WIN32 API函数声明-----------------------------------------  
  172. Sub KBCWait4IBE()   '等待键盘缓冲区为空  
  173. Dim dwVal As Long  
  174.   Do  
  175.   GetPortVal &H64, dwVal, 1  
  176. '这句表示从&H64端口读取一个字节并把读出的数据放到变量dwVal中  
  177. 'GetPortVal函数的用法是GetPortVal 端口号,存放读出数据的变量,读入的长度  
  178.   Loop While (dwVal And &H2)  
  179. End Sub  
  180.   
  181.   
  182. '窗体  
  183.   
  184. Private Declare Sub keybd_event Lib "user32" (ByVal bVk As ByteByVal Scan As ByteByVal dwFlags As LongByVal dwExtraInfo As Long)  
  185.   
  186. Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As LongAs Integer  
  187.   
  188. Private Const strEnabled = "0123456789"  
  189.   
  190.   
  191. Private Sub Command1_Click()  
  192. Timer1.Interval = Val(Text2.Text) * 1000  
  193. Timer1.Enabled = True  
  194. Command2.Enabled = True  
  195. Command1.Enabled = False  
  196. Form1.WindowState = 1  
  197. Text1.Locked = True  
  198. End Sub  
  199.   
  200. Private Sub Command2_Click()  
  201. Timer1.Interval = 0  
  202. Command1.Enabled = True  
  203. Command2.Enabled = False  
  204. Form1.WindowState = 0  
  205. Text1.Locked = False  
  206. End Sub  
  207.   
  208. Private Sub Form_Load()  
  209. Text1.Text = "这里输入你要说的话"  
  210. Text2.Text = "1"  
  211. Command1.Caption = "开始"  
  212. Command2.Caption = "停止"  
  213. Timer2.Interval = 10  
  214. Timer2.Enabled = True  
  215. Timer1.Enabled = Flash  
  216. Command2.Enabled = False  
  217. If InitializeWinIo = False Then  
  218.   '用InitializeWinIo函数加载驱动程序,如果成功会返回true,否则返回false  
  219.     MsgBox "驱动程序加载失败!"  
  220.     Unload Me  
  221.  End If  
  222. End Sub  
  223. Sub MyKeyDown(ByVal vKeyCoad As Long)  
  224. '这个用来模拟按下键,参数vKeyCoad传入按键的虚拟码  
  225. Dim btScancode As Long  
  226. btScancode = MapVirtualKey(vKeyCoad, 0)  
  227.     
  228.     KBCWait4IBE   '发送数据前应该先等待键盘缓冲区为空  
  229.     SetPortVal KBC_KEY_CMD, &HD2, 1     '发送键盘写入命令  
  230. 'SetPortVal函数用于向端口写入数据,它的用法是SetPortVal 端口号,欲写入的数据,写入数据的长度  
  231.     KBCWait4IBE  
  232.     SetPortVal KBC_KEY_DATA, btScancode, 1  '写入按键信息,按下键  
  233.       
  234. End Sub  
  235.   
  236.  Sub MyKeyUp(ByVal vKeyCoad As Long)  
  237. '这个用来模拟释放键,参数vKeyCoad传入按键的虚拟码  
  238. Dim btScancode As Long  
  239. btScancode = MapVirtualKey(vKeyCoad, 0)  
  240.     
  241.     KBCWait4IBE   '等待键盘缓冲区为空  
  242.     SetPortVal KBC_KEY_CMD, &HD2, 1  '发送键盘写入命令  
  243.     KBCWait4IBE  
  244.     SetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1  '写入按键信息,释放键  
  245.   
  246. End Sub  
  247.   
  248. Sub MyKeyDownEx(ByVal vKeyCoad As Long)   '模拟扩展键按下,参数vKeyCoad是扩展键的虚拟码  
  249. Dim btScancode As Long  
  250. btScancode = MapVirtualKey(vKeyCoad, 0)  
  251.     KBCWait4IBE   '等待键盘缓冲区为空  
  252.     SetPortVal KBC_KEY_CMD, &HD2, 1     '发送键盘写入命令  
  253.     KBCWait4IBE  
  254.     SetPortVal KBC_KEY_DATA, &HE0, 1  '写入扩展键标志信息  
  255.       
  256.       
  257.     KBCWait4IBE   '等待键盘缓冲区为空  
  258.     SetPortVal KBC_KEY_CMD, &HD2, 1     '发送键盘写入命令  
  259.     KBCWait4IBE  
  260.     SetPortVal KBC_KEY_DATA, btScancode, 1  '写入按键信息,按下键  
  261.       
  262.       
  263. End Sub  
  264.   
  265. Sub MyKeyUpEx(ByVal vKeyCoad As Long)   '模拟扩展键弹起  
  266. Dim btScancode As Long  
  267. btScancode = MapVirtualKey(vKeyCoad, 0)  
  268.     KBCWait4IBE   '等待键盘缓冲区为空  
  269.     SetPortVal KBC_KEY_CMD, &HD2, 1     '发送键盘写入命令  
  270.     KBCWait4IBE  
  271.     SetPortVal KBC_KEY_DATA, &HE0, 1  '写入扩展键标志信息  
  272.       
  273.       
  274.     KBCWait4IBE   '等待键盘缓冲区为空  
  275.     SetPortVal KBC_KEY_CMD, &HD2, 1     '发送键盘写入命令  
  276.     KBCWait4IBE  
  277.     SetPortVal KBC_KEY_DATA, (btScancode Or &H80), 1  '写入按键信息,释放键  
  278.       
  279. End Sub  
  280.   
  281.   
  282. Private Sub Form_Unload(Cancel As Integer)  
  283.  ShutdownWinIo '程序结束时记得用ShutdownWinIo函数卸载驱动程序  
  284. End Sub  
  285. Private Sub Timer1_Timer()  
  286. MyKeyDown VK_A  
  287. MyKeyUp VK_A  
  288. Call keybd_event(13, 0, 0, 0) '回车  
  289. 'Call keybd_event(40, 0, 0, 0) '向下  
  290. SendKeys Text1  
  291. End Sub  
  292.   
  293.   
  294. Private Sub Timer3_Timer()  
  295. MyKeyDownEx VK_DOWN   '按下向下方向键  
  296. End Sub  
  297.   
  298. Private Sub Timer2_Timer()  
  299. If GetAsyncKeyState(vbKeyF6) Then '按下F6开始说话  
  300. Command1_Click  
  301. End If  
  302. If GetAsyncKeyState(vbKeyF8) Then '按下F8停止说话  
  303. Command2_Click  
  304. End If  
  305. End Sub  
  306. Private Sub Text2_KeyPress(KeyAscii As Integer)  
  307. If InStr(1, strEnabled, Chr$(KeyAscii)) = 0 Then  
  308.  KeyAscii = 0  
  309. End If  
  310. End Sub  

 

VB:如何发送WM_KEYDOWN和WM_KEYUP消息

29277723/blog/item/918bfe1b5fdec81d8718bf4a.html

 

其实没什么说的,只是最近一段时间问的人比较多,所以写上几句
简单的说,有两个需要注意的地方,一是要用postmessage发送消息,二是这两个消息lparam参数比较复杂,发送消息的时候要构造好lparam参数,下面给出示例代码:

 

  1. Option Explicit  
  2. Private Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long  
  3. Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long  
  4. Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long  
  5. Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long  
  6. Private Const WM_KEYDOWN = &H100  
  7. Private Const WM_KEYUP = &H101  
  8. 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  
  9.   
  10. Private Sub Command1_Click()  
  11. Dim jsb As Long  
  12. jsb = FindWindow("notepad", vbNullString)  
  13. Dim mhwnd As Long  
  14. mhwnd = FindWindowEx(jsb, 0, "edit", vbNullString)  
  15. Dim lParam As Long  
  16. lParam = makelparam(vbKey5, False)  
  17. PostMessage mhwnd, WM_KEYDOWN, vbKey5, lParam  
  18. lParam = makelparam(vbKey5, True)  
  19. PostMessage mhwnd, WM_KEYUP, vbKey5, lParam  
  20. End Sub  
  21.   
  22. Private Function makelparam(ByVal VirtualKey As Long, ByVal flag As Boolean) As Long  
  23. Dim s As String  
  24. Dim Firstbyte As String 'lparam参数的24-31位  
  25. If flag = False Then 'keydown  
  26.        Firstbyte = "00"  
  27. Else  
  28.        Firstbyte = "C0" 'keyup  
  29. End If  
  30. Dim Scancode As Long  
  31. '获得虚拟键扫描码  
  32. Scancode = MapVirtualKey(VirtualKey, 0)  
  33. Dim Secondbyte As String 'lparam参数的16-23位,即虚拟键扫描码  
  34. Secondbyte = Right("00" & Hex(Scancode), 2)  
  35. s = Firstbyte & Secondbyte & "0001" '0001为lparam参数的0-15位,即发送次数  
  36. makelparam = Val("&H" & s)  
  37. End Function  

 

阅读(6102) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~