Delphi下利用WinIo模拟鼠标键盘详解

http://www.cnblogs.com/rogee/archive/2010/09/14/1827248.html

本文最早在编程论坛上发表,文章地址:http://programbbs.com/bbs/view12-17207-1.htm,相关文件可以在上述地址的页面中下载。转载时请注明出处。

前言

  一日发现SendInput对某程序居然无效,无奈只好开始研究WinIo。上网查了很多资料,发现关于WinIo模拟鼠标键盘的资料很少,有的也只是支言片语讲的不是很详细,而且大部分都是关于模拟键盘的。自己写了一些程序研究一方,经历了无数次的键盘死锁、鼠标满屏乱飞、复位重启,总算小有结果。现在将研究结果写出来与大家分享。另外,本人的水平有限文中有出错的地方欢迎根贴讨论。(PS:关于SendInput的使用可以参考我写的另一篇贴子《Delphi下利用SendInput模拟鼠标键盘》http://programbbs.com/bbs/view12-17219-1.htm

  我已经将主要的模拟功能写在了一个单元文件中:MouseKeyboard.pas,调用该单元文件中的相关函数就可以实现鼠标键盘的模拟。该单元文件的下载和使用方法请参考2楼的内容。在本楼的末尾有一个中英文对译PS/2鼠标键盘协议的下载,这个协议对编写模拟鼠标键盘的程序很有帮助。另外我还提供了一个鼠标移动速度测试程序、一个使用MouseKeyboard.pas的简单示范程序的下载。

一、WinIo简介

  WinIo通过加载一个内核模式的设备驱动程序,利用几种底层编程技巧,使得Windows应用程序可以直接对I/O端口和物理内存进行存取,从而绕过了Windows系统的保护机制。WinIo包含了3个文件:WinIo.dll、WinIo.sys和WINIO.VXD,其中WINIO.VXD驱动程序用在Win95/98系统上,WinIo.sys驱动程序用在WinNT/2000/XP系统上,WinIo.dll提供了功能函数的调用。在WinIo.dll中有两个函数最重要:InitializeWinIo用来初始化WinIo的驱动程序,必须在调用所有其它功能函数之前调用该函数;ShutdownWinIo用来卸载WinIo的驱动程序,在中止应用函数之前或者不再需要使用WinIo时调用。在初始化完成之后就可以直接读写I/O端口而不会出现非法操作,本程序就是利用向鼠标键盘硬件端口写入数据来模拟鼠标键盘的操作。

  由于是底层的硬件端口读写,所以必需对硬件的相关协议有所了解,对于鼠标键盘最重要的协议就是PS/2鼠标键盘协议(以下简称PS/2协议)。我这里提供了一个pdf版的中英文对译PS/2鼠标键盘协议,在该协议的前半部主要讲硬件的电气接口协议,但是后半部分的内容对于模拟鼠标键盘非常有用。这个协议可是我在网上翻腾了好久才找到的。协议的下载见本楼末尾。

二、Intel 8042

  Intel 8042或兼容微控制器(以下简称i8042)被用作PC键盘的控制器,虽然名为键盘控制器,但是实际上鼠标也是由其控制的。i8042一般整合在芯片组中。向i8042发送指定的命令和数据就可以模拟鼠标键盘的操作。i8042包含了如下四个寄存器:

一个字节的输入缓冲区:包含从鼠标或键盘读入的字节;只读。
一个字节的输出缓冲区:包含要写到鼠标或键盘的字节;只写。
一个字节的状态寄存器:8个状态标志;只读。
一个字节的控制寄存器:7个控制标志;读写。

  其中前三个寄存器(输入、输出、状态)可以通过$60和$64端口直接存取,读写$60和$64端口所实现的功能如下:

端口 读写 功能
$60  读 读输入缓冲区
$60  写 写输出缓冲区
$64  读 读状态寄存器
$64  写 发送命令

  写$64端口不会写入到任何特定的寄存器中,但是解释为发送命令给i8042。如果命令接收一个参数,则参数被发往$60端口。同样,命令的任何返回结构可以从$60端口读出。i8042的状态标志是从$64端口读出的。它们包含了错误信息、状态信息和输入输出缓冲区里有无数据的指示。这些标志的定义如下:

 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
┌──┬──┬──┬──┬──┬──┬──┬──┐
│PERR│TO │MOBF│INH │A2 │SYS │IBF │OBF │
└──┴──┴──┴──┴──┴──┴──┴──┘

  其中标志位OBF最重要(其它标志位的意思请参考PS/2协议),它表示输出缓冲区是否已满,是否可以写入输出缓冲区。0表示输出缓冲区空,1表示输出缓冲区已满。所以在向$60端口写入数据之前要检查该标志位是否已被置0。另外在向$64端口发送命令之前也要检查该标志位,已确保上次的操作已经完成。向指定端口写入数据的程序如下,其中使用了内嵌汇编对端口进行操作。注意:鼠标键盘是慢速设备,每次操作时要有一定的延时:

procedure SetByte(Por,Cod : Byte);
begin
    Sleep(1);
    asm
        PUSH EAX
        PUSH EDX
        //等待状态寄存器标志位OBF置0
        @Loop:
        IN AL,$64
        AND AL,01b
        JNZ @Loop
        //写入数据
        MOV AL,Cod
        MOV DL,Por
        MOV DH,0
        OUT DX,AL

POP EDX
        POP EAX
    end;
end;

  发送命令给i8042就是写$64端口。在命令发送后,命令参数写到$60端口。命令中用来模拟鼠标键盘操作的有两条(其它命令请参考PS/2协议):

$D2:写键盘缓冲区,把参数写到输入缓冲区就像从键盘接收到的一样。
$D3:写鼠标缓冲区,把参数写到输入缓冲区就像从鼠标接收到的一样。

  例如:按下“A”键,利用上面的程序可以写成“SetByte($64,$D2); SetByte($60,$1E);”。注意:如果向$60端口发送的数据不只1个字节,那么发送的每个字节前都要发送一条命令,例如:按下“Insert”键的程序为“SetByte($64,$D2); SetByte($60,$E0); SetByte($64,$D2); SetByte($60,$52);”,要调用SetByte四次。知道了如何向i8042发送命令,下面就可以具体的模拟鼠标键盘。

三、键盘模拟

  键盘的处理器会扫描或监视按键矩阵。如果它发现有键被按下、释放或按住,键盘将发送“扫描码”的数据包到i8042。扫描码有两种不同的类型:“通码”和“断码”,当一个键被按下或按住就发送通码;当一个键被释放就发送断码。每个按键被分配了唯一的通码和断码,这样i8042通过查找唯一的扫描码就可以测定是哪个按键。每个键一整套的通断码组成了“扫描码集”。有三套标准的扫描码集分别是第一套、第二套和第三套。i8042缺省支持第一套扫描码。

  一部份键的断码是将通码的最高位置1,但并不是所有的键都这样,而且很多键的扫描码不只有1个字节。所以没有一个简单的公式可以计算扫描码。如果你要知道某特定按键的通码和断码,你将不得不查表获得。例如:“A”键的通码为$1E,断码为$9E,“Insert”键的通码为$E0,$52,断码为$E0,$D2,模拟按键的程序如下:

//按下并放开“A”键
SetByte($64,$D2); SetByte($60,$1E);
SetByte($64,$D2); SetByte($60,$9E);

//按下并放开“Insert”键
SetByte($64,$D2); SetByte($60,$E0);
SetByte($64,$D2); SetByte($60,$52);
SetByte($64,$D2); SetByte($60,$E0);
SetByte($64,$D2); SetByte($60,$D2);

  特别的在PS/2协议中说在第一第二套扫描码里没有“Pause/Break”键的断码。当这个键按下时发送它的通码,当它释放时,什么都没有被发送。在第一套扫描码里Pause键的通码长达6个字节:$E1,$1D,$45,$E1,$9D,$C5。但是我在实际测试中发现Pause键的通码其实是前3个字节:$E1,$1D,$45,后3个字节$E1,$9D,$C5是Pause键的断码。至少在我的键盘上是这样。

  在PS/2协议中已经把所有三套扫描码集中所有的通码和断码做成了表格,具体的内容可以查阅相关的部份。在单元文件MouseKeyboard.pas中我已经将第一套键盘扫描码定义成常量数组,其中还包括了键对应的字符。单元中有两个函数MKFindKeyCode和MKFindKeyChar,可以用来对常量数组进行查找。

四、鼠标模拟

  标准的PS/2鼠标支持下面的输入:X(左右)位移、Y(上下)位移、左键、中键和右键。鼠标以一个固定的频率读取这些输入并更新不同的计数器然后标记出反映的移动和按键状态。有很多PS/2鼠标具有额外的输入比如微软的Intellimouse,它既支持标准输入也支持滚轮和两个附加的按键。

  标准的鼠标有两个计数器保持位移的跟踪:X位移计数器和Y位移计数器。可存放9位的2进制补码并且每个计数器都有相关的溢出标志。它们的内容连同三个鼠标按钮的状态一起以三字节移动数据包的形式发送给i8042。位移计数器表示从最后一次位移数据包被送往i8042后有位移量发生。标准的PS/2鼠标发送位移和按键信息给i8042采用如下的3字节数据包格式:

     Bit7   Bit6   Bit5   Bit4   Bit3 Bit2  Bit1  Bit0
    ┌────┬────┬────┬────┬──┬───┬───┬───┐
Byte 1 │Y 溢出位│X 溢出位│Y 符号位│X 符号位│置 1│中键位│右键位│左键位│
    ├────┴────┴────┴────┴──┴───┴───┴───┤
Byte 2 │            X 左右移位值,补码             │
    ├──────────────────────────────────┤
Byte 3 │            Y 上下移位值,补码             │
    └──────────────────────────────────┘

  位移计数器是一个9位二进制补码整数。它的最高位作为符号位出现在位移数据包的第一个字节里,这些计数器在鼠标读取输入发现有位移时被更新,这些值是自从最后一次发送位移数据包给i8042后位移的累计量(即最后一次包发给i8042后位移计数器被复位),位移计数器可表示的值的范围是-255到+255。如果超过了范围,相应的溢出位就被设置,并且在复位前计数器不会增减。

  对标准的PS/2鼠标的一个流行的扩展是微软的Intellimouse。它包括支持五个鼠标按键和三个位移轴(左右、上下和滚轮)。这些附加特征要求使用4字节的位移数据包而不是标准3字节数据包。微软的Intellimouse使用4字节的位移数据包格式有两种情况分别如下:

1、三键带滚轮鼠标:

     Bit7   Bit6   Bit5   Bit4   Bit3 Bit2  Bit1  Bit0
    ┌────┬────┬────┬────┬──┬───┬───┬───┐
Byte 1 │Y 溢出位│X 溢出位│Y 符号位│X 符号位│置 1│中键位│右键位│左键位│
    ├────┴────┴────┴────┴──┴───┴───┴───┤
Byte 2 │            X 左右移位值,补码             │
    ├──────────────────────────────────┤
Byte 3 │            Y 上下移位值,补码             │
    ├──────────────────────────────────┤
Byte 4 │            Z 滚动移位值,补码             │
    └──────────────────────────────────┘

  Z位移是二进制补码表示滚轮的自上次数据报告以来的位移。有效值的范围在-8到+7,这意味着数值实际只有低四位有用,高四位仅用作符号扩展位。当Z小于0时表示向上滚动,当Z大于0时表示向下滚动。

2、五键带滚轮鼠标:

     Bit7   Bit6   Bit5   Bit4   Bit3 Bit2  Bit1  Bit0
    ┌────┬────┬────┬────┬──┬───┬───┬───┐
Byte 1 │Y 溢出位│X 溢出位│Y 符号位│X 符号位│置 1│中键位│右键位│左键位│
    ├────┴────┴────┴────┴──┴───┴───┴───┤
Byte 2 │            X 左右移位值,补码             │
    ├──────────────────────────────────┤
Byte 3 │            Y 上下移位值,补码             │
    ├────┬────┬────┬────┬──┬───┬───┬───┤
Byte 4 │ 置 0 │ 置 0 │第5键位 │第4键位 │ Z3 │ Z2 │ Z1 │ Z0 │
    └────┴────┴────┴────┴──┴───┴───┴───┘

  Z0~Z3是二进制补码用于表示从上次数据报告以来滚轮的位移量,有效范围从-8到+7;第4键位:置1表示第4键按下了,置0表示第4键没有按下;第5键位:置1表示第5键按下了,置0表示第5键没有按下。可以看出三键带滚轮鼠标和五键带滚轮鼠标这两种数据包格式是相互兼容的。一般现在使用的鼠标都是带滚轮的,所以使用的都是4字节的数据包格式。调用API函数GetSystemMetrics(SM_CMOUSEBUTTONS)可以返回当前鼠标的按键数,调用GetSystemMetrics(SM_MOUSEWHEELPRESENT)可以返回当前鼠标是否有带滚轮。通过调用这两个API函数可以判断当前使用的是3字节数据包还是4字节数据包。知道了数据包的格式就可以向i8042发送$D3命令模拟鼠标操作,模拟单击鼠标左键的程序如下:

//按下鼠标左键
SetByte($64,$D3); SetByte($60,$09);
SetByte($64,$D3); SetByte($60,$00);
SetByte($64,$D3); SetByte($60,$00);
SetByte($64,$D3); SetByte($60,$00); //三键带滚轮鼠标

//放开鼠标左键
SetByte($64,$D3); SetByte($60,$08);
SetByte($64,$D3); SetByte($60,$00);
SetByte($64,$D3); SetByte($60,$00);
SetByte($64,$D3); SetByte($60,$00); //三键带滚轮鼠标

  现在再来谈谈鼠标移动的模拟,鼠标移动的模拟其实是一个很头痛的问题。如果只是让鼠标随便动动那是很简单,但是要将鼠标在屏幕上的指针移动到指定坐标就不是那么容易的事件了。首先,鼠标位移计数器使用的是平面直角坐标系。也就说当鼠标向左移动时X小于0,当鼠标向右移动时X大于0,当鼠标向下移动时Y小于0当鼠标向上移动时Y大于0。屏幕上的指针坐标使用的是计算机屏幕坐标系,对应于平面直角坐标系在X轴上是一致的,在Y轴上相差一个正负号。

  其次,决定位移计数器增减数量的参数叫分辨率,缺省的分辨率为:4计数单位每毫米。这就意味着鼠标位移计数器的位移量(以下简称鼠标位移量)同鼠标指针在屏幕上的象素位移量(以下简称指针位移量)并不一样。鼠标位移量同指针位移量的比值与鼠标移动速度的设置有关,通过“控制面板”中“鼠标”选项卡的“调整指针移动速度”可以对该值进行设置,这个值保存在注册表HKEY_CURRENT_USER\Control Panel\Mouse\MouseSensitivity中,通过修改注册表可以将这个值设置成带有小数点的,通过调用API函数SystemParametersinfo使用参数SPI_GETMOUSESPEED就可以获取该值。另外,使用第三方鼠标驱动程序也可以设置鼠标的移动速度,一般这类驱动程度都有带鼠标加速功能,这使得鼠标位移量同指针位移量的比值根本无法确定。

  当使用Windows自带的鼠标驱动程序,在鼠标选项卡中将指针移动速度设置在中间位置,注册表中MouseSensitivity的值为10,此时鼠标位移量同指针位移量的比值还是可以计算的,当鼠标位移量小于7时:指针位移量等于鼠标位移量;当鼠标位移量大于等于7时:指针位移量等于鼠标位移量的2倍。在这种情况下的前20个比值如下,其中n为鼠标位移量,cX、cY为指针位移量:

n, cX, cY
1,   1,   1
2,   2,   2
3,   3,   3
4,   4,   4
5,   5,   5
6,   6,   6
7, 14, 14
8, 16, 16
9, 18, 18
10, 20, 20
11, 22, 22
12, 24, 24
13, 26, 26
14, 28, 28
15, 30, 30
16, 32, 32
17, 34, 34
18, 36, 36
19, 38, 38
20, 40, 40

  例如:让鼠标指针在屏幕上移动的程序如下:

//鼠标上移50象索
SetByte($64,$D3); SetByte($60,$08);
SetByte($64,$D3); SetByte($60,$00);
SetByte($64,$D3); SetByte($60,$19);
SetByte($64,$D3); SetByte($60,$00); //三键带滚轮

//鼠标左移50象索
SetByte($64,$D3); SetByte($60,$18);
SetByte($64,$D3); SetByte($60,$E7);
SetByte($64,$D3); SetByte($60,$00);
SetByte($64,$D3); SetByte($60,$00); //三键带滚轮

  另外我发现一些鼠标对于大的鼠标移动量(大于128或大于170)没有反应。可能是因为没有人可以用手把鼠标移动的这么快,所以不需要。这在模拟鼠标移动过程中是要注意的。由于没有很好的算法,在MouseKeyboard.pas中我使用了一个鼠标移动换算表来计算鼠标的移动量,这个表其实就是一个255长度的Integer数组,里面记录了每当鼠标移动多少个单位,指针就会移动多少个象素。我还写了几个函数,提供对该表导入导出到ini格式文件中的功能,这样应该可以适应大多数的鼠标配置。我写了一个测试鼠标移动速度的程序,它可以测试当前鼠标位移量同指针位移量的比值,可以根据对角移动、水平移动、垂直移动的比值计算出鼠标移动换算表并保存到ini文件中。这个程序的下载见本楼末尾。

五、使用WinIo的优缺点

  使用WinIo的优点是不言而喻的,直接的硬件端口读写使得很多程序无法对其进行屏蔽。它的缺点也很头痛,首先就是兼容性的问题,直接读写硬件端口会使程序在一些配置的计算机上正常运行,而在另一些配置的计算机上无法运行。特别是USB鼠标,我的程序是在PS/2鼠标上测试的,可能对一部份USB鼠标不兼容。其次就是数据冲突的问题,在模拟操作的时候鼠标键盘仍然在工作,如果这时操作鼠标键盘可能会出现失控的现象。特别是鼠标。原因在于:当程序进行模拟操作的时候操作鼠标键盘,程序发送给i8042的数据包可能会与鼠标键盘发送给i8042的数据包相互交错在一起(因为每次只能发送1个字节),从而造成数据包混乱引起鼠标键盘的失控。我在一次测试时就碰了一下鼠标,鼠标指针就在屏幕上乱飞。最后一点缺点就是这种模拟是全局性,在模拟操作的过程中你什么都不能做,其实SendInput也是一样。

六、MouseKeyboard.pas的使用

  在上面的压缩包中包含了4个文件:MouseKeyboard.pas、WinIo.dll、WinIo.sys和WINIO.VXD,后面3个文件是WinIo的相关文件。把这4个文件放到项目同一个文件夹下,将MouseKeyboard添加到单元的uses段中就可以调用MouseKeyboard.pas中的函数。值得注意的是当你的程序发布时WinIo.dll、WinIo.sys和WINIO.VXD这3个文件要和可执行文件放在同一个文件夹下,程序运行的时候会调用它们。当程序在一个“陌生”的计算机上运行时,最好调用一下MKTestConversion函数测试并保存当前计算机的鼠标移动换算表。使用上楼提供的鼠标移动测试程序生成ini文件也可以。

  在MouseKeyboard.pas中我定义了一个枚举类型的TMKKeyCode,里面有104个项对应不同的按键。我还定义了一个常量记录数组MKKeyData,里面定义了104键的扫描码、通码长度、断码长度以及键对应的字符。在MouseKeyboard.pas中共有21个函数,使用说明如下:

1、function MKInitMouseKey: Boolean;
  这个函数会初始化WinIo,并对一些内部变量进行初始化,同时这个函数会尝试从当前文件夹中打开一个名为“Mouse.ini”的ini文件,并从文件的“Conversion”节中加载鼠标移动换算表,如果失败这个函数会调用MKInitConversion初始化一个默认的鼠标移动换算表。一般情况下不要调用这个函数,在单元被加载的时候会自动调用这个函数来完成初始化。

2、procedure MKFreeMouseKey;
  释放驱动程序卸载WinIo,一般情况下不要调用这个函数,在单元被卸载的时候会自动调用这个函数来完成卸载释放。

3、function MKSendData(const Data : TMKPacketData; Len : Byte; Typ : TMKDataType): Boolean;
  向指定的键盘鼠标端口发送数据包,返回是否成功。数据包最长只能是4个字节,Len为数据包的字节长度。这个函数内嵌了一个上文提到的SetByte函数用以对端口的字节发送。

4、function MKFindKeyCode(Code : TMKKeyCode): Integer;
  查找指定键码在MKKeyData中的id,找到返回id,没找到返回-1。

5、function MKFindKeyChar(Char : AnsiChar): Integer;
  查找指定字符在MKKeyData中的id,找到返回id,没找到返回-1。

6、function MKKeyDown(Code : TMKKeyCode): Boolean;
  按下指定的键,返回是否成功。

7、function MKKeyUp(Code : TMKKeyCode): Boolean;
  放开指定的键,返回是否成功。

8、function MKKeyPress(Code : TMKKeyCode; Interval : Cardinal): Boolean;
  按下并放开指定的键,返回是否成功。Interval为按下和放开之间的时间间隔,单位毫秒。注意:本函数不支持重复机打,即无论Interval设的多么大都只有一次按键。

示范程序,组合键Ctrl+A如下:
MKKeyDown(kcLCtrl); //按下Ctrl
MKKeyPress(kcA);    //击键A
MKKeyUp(kcLCtrl);   //放开Ctrl

9、function MKKeyInput(const Text : String; Interval : Cardinal): Boolean;
  模拟键盘输入指定的文本,返回是否成功。文本中只能是单字节字符(#32~#126)、Tab(#9)键和回车键(#13),即可以从键盘上输入的字符,不能是汉字,其它字符会被忽略。Interval为按下和放开键之间的时间间隔,单位毫秒。

10、function MKMouseDown(Code : TMKMouseCode): Boolean;
  按下鼠标的指定键,返回是否成功。

11、function MKMouseUp(Code : TMKMouseCode): Boolean;
  放开鼠标的指定键,返回是否成功。

12、function MKMouseClick(Code : TMKMouseCode; Interval : Cardinal): Boolean;
  单击鼠标的指定键,返回是否成功。Interval为按下和放开键之间的时间间隔,单位毫秒。

13、function MKMouseWheel(cZ : Integer): Boolean;
  滚动鼠标的滚轮,返回是否成功。cZ为鼠标滚轮滚动移位值,非象素值。cZ有效值的范围在-8到+7,当cZ小于0时向上滚动,当cZ大小0向下滚动。

14、function MKMouseMove(cX,cY : Integer): Boolean;
  移动鼠标指针在屏幕上的位置,返回是否成功。cX和cY为鼠标位移计数器的值,非象素值。当cX小于0时向左移动,当cX大小0时向右移动,当cY小于0时向下移动,当cY大于0时向上移动。cX和cY的值的范围是-255到+255。

15、procedure MKInitConversion(var Con : TMKMoveConversion);
  初始化一个鼠标移动换算表,假设鼠标的移动速度设为中等,没有使用其它第三方的鼠标加速驱动程序。

16、function MKTestConversion(var Con : TMKMoveConversion; MaxMove : Integer): Boolean;
  测试并确认鼠标移动换算表,返回是否成功。MaxMove为测试时的鼠标移动的最大值,这个函数会对鼠标进行对角移动,并记录下鼠标指针的实际移动象素量,计算出鼠标移动换算表。在测试期间不能操作鼠标,否则可能会出现鼠标失控,测试不准的情况。

17、function MKConversionToIni(const Con : TMKMoveConversion; IniFile : TIniFile; const Section : String): Boolean;
  将鼠标移动换算表导出到ini文件的指定节中,返回是否成功。

18、function MKConversionFromIni(var Con : TMKMoveConversion; IniFile : TIniFile; const Section : String): Boolean;
  从ini文件的指定节中导入鼠标移动换算表,返回是否成功。

19、procedure MKLoadConversion(const Con : TMKMoveConversion);
  导入一个鼠标移动换算表作为默认换算表。

20、function MKMouseMoveTo(X,Y : Integer; const Con : TMKMoveConversion; MaxMove : Integer; Interval : Cardinal): Boolean;
21、function MKMouseMoveTo(X,Y : Integer; MaxMove : Integer; Interval : Cardinal): Boolean;
  这是重载的两个函数,函数可以将鼠标指针移动到指定位置,并返回是否成功。X和Y为象素值,X和Y的值的范围不能超出屏幕。MaxMove为移动时的cX和cY的最大值。Interval为两次移动之间的时间间隔。第1个函数使用指定的鼠标移动换算表Con进行移动坐标的计算,第2函数使用默认的鼠标移动换算表进行移动坐标的计算。如果鼠标的移动速度没有设为中等,或者使用其它第三方的鼠标加速驱动程序,可能会使该函数无法将鼠标指针移动到准确的位置,会出现一定的误差。一些程序对鼠标移动速度敏感,当鼠标移动太快时无法对鼠标做出反应,适当的设置MaxMove值可以解决这个问题。

示范程序,拖放到指定位置如下:
MKMouseDown(mcLeft);    //按下鼠标左键
MKMouseMoveTo(780,300); //移动到指定位置
MKMouseUp(mcLeft);      //放开鼠标左键

更新(2010年1月8日):

  更新MouseKeyboard.pas文件,原文件中有个小BGU。模拟键盘输入“Q”时,无论大小写,模拟按下时是输入“Q”,模拟放开时输入变成了“P”。我检查了通断码,发现“Q”的断码居然和“P”的通码一样都是“$19”。这肯定是错误的。我的通断码数据都是从PS/2协议中抄写出来的,我查看了该文件发现数据也是一样的错误。“Q”的通码是“$10”,我尝试将通码的最高位置1变成“$90”作为“Q”的断码。这样的测试在我的电脑上成功了,可以成功的模拟放开“Q”。但是我不知道这会不会是键盘兼容的问题。

  更新后的MouseKeyboard.pas文件只变动了一个很小的地方。在原文件的第150行由以下内容:

(Code : kcQ;           Char : ‘qQ‘; Length : $11; Make : ($10,$00,$00,$00); Break : ($19,$00,$00,$00)),

  修改为

(Code : kcQ;           Char : ‘qQ‘; Length : $11; Make : ($10,$00,$00,$00); Break : ($90,$00,$00,$00)), //断码$19改为$90

  也就是将“Q”的断码由“$19”更改为“$90”。你也可以利用下面的链接下载更新后的MouseKeyboard.pas文件

文件下载:http://files.cnblogs.com/rogee/SIMouseKeyboard.pas.zip

时间: 2024-10-10 23:25:39

Delphi下利用WinIo模拟鼠标键盘详解的相关文章

Linux 模拟 鼠标 键盘 事件

/************************************************************************ * Linux 模拟 鼠标 键盘 事件 * 说明: * 以前看到有些软件能够控制鼠标移动,键盘操作等功能,总想知道这些到底 * 是怎么做到的,好像是2年前也尝试去做这件事,但那时候对知识的匮乏直接导致 * 无法进行,早上突然想到这件事,于是又搜索了一下,鉴于目前经常接触Linux * 驱动,对这些东西的理解也就很容易. * * 2016-2-27 深

C#模拟鼠标键盘控制其他窗口(一)

编写程序模拟鼠标和键盘操作可以方便的实现你需要的功能,而不需要对方程序为你开放接口.比如,操作飞信定时发送短信等.我之前开发过飞信耗子,用的是对飞信协议进行抓包,然后分析协议,进而模拟协议的执行,开发出了客户端,与移动服务器进行通信,但是这有一些缺点.如果移动的服务器对接口进行变更,我所编写的客户端也要进行相应的升级.如果服务器的协议进行了更改,甚至个人编写的这种第三方客户端需要重写.而我个人也没有这个时间和精力,或者说没有足够的利益支撑我继续去重构飞信耗子.因此,这款还算优秀的软件,现在就束之

教程-Delphi中Spcomm使用属性及用法详解

Delphi中Spcomm使用属性及用法详解 Delphi是一种具有 功能强大.简便易用和代码执行速度快等优点的可视化快速应用开发工具,它在构架企业信息系统方面发挥着越来越重要的作用,许多程序员愿意选择 Delphi作为开发工具编制各种应用程序.但是,美中不足之处是 Delphi没有自带的串口通信控件,在它的帮助文档里也没有提及串口通信,这就给编制通信程序的开发人员带来许多不便. 目前,利用 Delphi实现串口通信的常用的方法有 3种:一是利用控件,如 MSCOMM控件和 SPCOMM控件:二

CentOS6.5下VNC Server远程桌面配置详解

参考文献: (总结)CentOS Linux下VNC Server远程桌面配置详解 远程桌面连接工具VNC——license Key 我的下载地址为 太平洋下载 VNC连接黑屏的问题 centos 6.5安装vncserver 并开启远程桌面 有个服务固定ip才能访问,系统是centos6.5,远程ssh只有命令,执行命令还行,但是打开网页看连接效果不行,所以需要服务器桌面共享. 一.安装相应桌面环境与vnc服务端和客户端: # yum groupinstall "X Window System

Modbus读写模拟量寄存器详解

读可读写模拟量寄存器: 发送命令(主机向从机)格式: [设备地址] [命令号03] [起始寄存器地址高8位] [低8位] [读取的寄存器数高8位] [低8位] [CRC校验的低8位] [CRC校验的高8位] 例:[11][03][00][6B][00][03][CRC低][CRC高] 意义如下: 11:设备地址,例子中的地址是11,溶氧传感器端默认的地址是1,. 03:读模拟量的命令号固定为03,这是Modbus协议规定的. 00.6B:起始地址高8位(00).低8位(6B):表示想读取的模拟量

华为网络技术大赛模拟题答案详解

华为网络技术大赛模拟题答案详解 [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article/details/51354514 一.判断题 (1)VLSM的作用是:在有类的IP地址基础上,从主机位部分划分出相应的位数做为网络位.但是在路由器上部署时,需要路由协议的支持. [解释]对,VLSM=Variable Length Subnet Mask,可变长子网掩码 (2)有效的沟通是任何组织和任何项目的基础,项目经理可以花90%或者更多的时间在沟通这方

虚拟机VMware下CentOS6.6安装教程图文详解

分享下,虚拟机VMware下CentOS6.6安装教程图文详解. CentOS 6.6 发布下载 地址: http://www.linuxidc.com/Linux/2014-10/108554.htm 1.首先要下载一个centos的iso镜像,我是用虚拟机VMware来安装的,用VMware最好创建一个空白硬盘. 2.创建完毕再设置里面挂载iso的centos系统文件. 3.进入到这个页面: 说明: 1.install or upgrade an existing system安装或升级现有

Linux下同步工具inotify+rsync使用详解

Linux下同步工具inotify+rsync使用详解 Posted on 2014-12-12 |  In Linux|  9|  Visitors 438 1. rsync 1.1 什么是rsync rsync是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件.它使用所谓的"Rsync演算法"来使本地和远程两个主机之间的文件达到同步,这个算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快.所以通常可以作为备份工具来使用. 运行Rsync serve

Linux,unix,cygwin,centeros下的tar压缩解压缩命令详解

Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置.不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的.但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的.为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面. 我们把这