VB驱动级模拟鼠标点击

看了网上关于以WinIO库模拟键盘事件的文章,便想把这种技巧用到鼠标事件上来。以前操作鼠标一直用SetCursorPos()控制光标位置,效率虽低,总算还可以忍受,但 模拟鼠标单击用的API是mouse_event(),其效率之低实在令人无法满意,而PostMessage()因为使用时需要提供句柄,又太不方便。偶得此方,仔细进行了论证和实验,发现效果十分理想。

  有一些使用DirectX接口的程序,在读取鼠标操作时绕过了Windows的消息机制,而使用DirectInput.这是因为有些游戏对实时性控制的要求比较高,要求以最快速度响应鼠标输入。由于Windows消息是队列形式的,消息在传递时会有不少延迟,达不到游戏的要求。而DirectInput则绕过了Windows消息,直接与鼠标驱动程序打交道,效率当然提高了不少。因此也就造成,对这样的程序无论用PostMessage()或者是mouse_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,得到断码,表示释放这个键

  这篇文章使用WinIO来写入硬件端口,让我想起了从前的DOS时代,在那时,读写硬件端口何其方便。然而,我转而使用VB之后,在Windows下直接操作底层端口变成了美好的回忆(当然,也因为很少有必要)。

  废话少说,查阅资料得知,要操作鼠标缓冲区,须先向0x64端口写入KBC指令0xD3,表示要写入,再向0x64写入扫描码。扫描码0x09代表鼠标左键按下,0x08代表鼠标左键放开。写代码如下:

'====================声明部分====================
Private Declare Function InitializeWinIo Lib "WinIo.dll" () As Boolean
Private Declare Function ShutdownWinIo Lib "WinIo.dll" () As Boolean
Private Declare Function GetPortVal Lib "WinIo.dll" (ByVal PortAddr As Integer, ByRef PortVal As Long, ByVal bSize As Byte) As Boolean
Private Declare Function SetPortVal Lib "WinIo.dll" (ByVal PortAddr As Integer, ByVal PortVal As Long, ByVal bSize As Byte) As Boolean
'================================================
Private Sub Form_Load()
If InitializeWinIo = False Then
     '用InitializeWinIo函数加载驱动程序,如果成功会返回true,否则返回false
    MsgBox "驱动程序加载失败!"
    Unload Me
End If
Timer1.Interval = 3000
Timer1.Enabled = True
End Sub

Private Sub Form_Unload(Cancel As Integer)
    ShutdownWinIo '程序结束时用ShutdownWinIo函数卸载驱动程序
End Sub

Private Sub Timer1_Timer()
    ClickMouse
End Sub

Private Sub ClickMouse()
    SetKey &HD3, &H9
     SetKey &HD3, &H0
     SetKey &HD3, &H0
    
     SetKey &HD3, &H8
     SetKey &HD3, &H0
     SetKey &HD3, &H0
End Sub

Private Sub SetKey(Typ, ScanCode)
    KBCWait4IBE        ‘根据KBC规范,在向键盘端口写入数据前要等待一段时间
    SetPortVal &H64, Typ, 1     '写入KBC指令
    KBCWait4IBE
    SetPortVal &H60, ScanCode, 1 '写入扫描码
End Sub

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

扫描二维码关注公众号,回复: 1160023 查看本文章

  该程序为实验所用,仅作实例,在WinXP Home Edition,VB6下测试通过。

猜你喜欢

转载自yeuego.iteye.com/blog/947439