键盘鼠标模拟全知道(Delphi)北极星NorthStar

一、基于windows消息机制的鼠标键盘模拟(一)、针对指定程序(窗口)模拟(PostMessage)——局部模拟(可后台)1、模拟键盘按键。2、模拟鼠标3、例子4、附录:获取窗口句柄handle1)WindowFromPoint2)FindWindow/FindWindowEx或者用GetWindow来遍历查找,3)通过进程的文件名来查找窗口句柄。(二)全局模拟1、用API函数keybd_event和mouse_event。1)函数keybd_event模拟键盘2)mouse_event模拟按下鼠标2、SendInput函数也可以模拟全局键盘鼠标事件。1)键盘模拟2)鼠标模拟3)附录:SIMouseKeyboard.pas的使用——利用SendInput模拟鼠标键盘的输入4)SendInput模拟键盘输入应注意的问题5)SendInput与WInIo的对比3、用全局钩子也可以模拟键盘消息。二、驱动级模拟(绕过windows消息)——WINIO模拟按键

*******************************************************************************************

我们怎样才能用Delphi来写一个程序,用来代替人们按键的方法呢?那就让我们来先了解一下windows中响应键盘事件的机制。

这个过程可以简单的如下表示:

用户按下键盘上的一个键>>>>>键盘控制器就把这个键的扫描码传给计算机,然后交给键盘驱动程序>>>>>键盘驱动程序会把这个扫描码转换为键盘虚拟码(VK_A,VK_B这样的常数,比如键A的虚拟码是65,写成16进制就是&H41)传给操作系统>>>>>操作系统则会把这些信息封装在一个消息中,并把这个键盘消息插入到消息列队>>>>>键盘消息被发送到当前活动窗口>>>>>活动窗口所在的应用程序接收到这个消息后,就知道键盘上哪个键被按下,也就可以决定该作出什么响应给用户了。

明白了这个过程,我们就可以编程实现在其中的某个环节来模拟键盘操作了。在Delphi中,有多种方法可以实现键盘模拟,我们就介绍几种比较典型的。

windows提供了几个这样的API函数可以实现直接向目标程序发送消息的功能,常用的有SendMessage和PostMessage,它们的区别是PostMessage函数直接把消息仍给目标程序就不管了,而SendMessage把消息发出去后,还要等待目标程序返回些什么东西才好。这里要注意的是,模拟键盘消息一定要用PostMessage函数才好,用SendMessage是不正确的(因为模拟键盘消息是不需要返回值的,不然目标程序会没反应),切记切记!

PostMessage(

hWnd:HWND;{目标程序上某个控件的句柄}

Msg:UINT;{消息的类型}

wParam:WPARAM;{32位指定附加的消息特定的信息}

lParam:LPARAM{32位指定附加的消息特定的信息}

):BOOL;

参数hwnd是你要发送消息的目标程序上某个控件的句柄,

参数Msg是消息的类型,表示你要发送什么样的消息,

参数wParam和lParam这两个参数是随消息附加的数据,具体内容要由消息决定。

参数Msg这个,要模拟按键就靠这个了。

键盘消息参数Msg常用的有如下几个:

WM_KEYDOWN表示一个普通键被按下

WM_KEYUP表示一个普通键被释放

WM_SYSKEYDOWN表示一个系统键被按下,比如Alt键

WM_SYSKEYUP表示一个系统键被释放,比如Alt键

鼠标消息参数Msg:

WM_LBUTTONDOWN//左键按下

WM_RBUTTONDOWN//右键按下

WM_MBUTTONDOWN//中间键按下

WM_LButtonUp//左键放开

WM_RbuttonUp//右键放开

WM_MButtonUp//中键放开

WM_LBUTTONDBLCLK//左键双击

WM_RBUTTONDBLCLK//左键双击

WM_MBUTTONDBLCLK//中键双击

应用程序从Windows接收的关于键盘事件的消息可分为击键消息和字符消息两种。这与你看待键盘的两种方式是一致的。首先,你可以认为键盘是键的集合。键盘上仅有一个键表示为“A”。按下此键是一次击键,释放此键也认为是一次击键。同时键盘也是能产生可显示字符或者控制字符的输入设备。有些键不产生字母,如Shift键、功能键、光标移动键和特殊字符键(如Insert键和Delete键)。对于这些键,Windows只产生击键消息。

一般有以下四个击键消息:

WM_KEYDOWN、WM_SYSKEYDOWN、WM_KEYUP、WM_SYSKEYUP。这里分为系统击键消息和非系统击键消息。而非系统击键消息是我们常用的。WM_KEYDOWN和WM_KEYUP消息通常是在按下或释放不带Alt键的键时产生;WM_SYSKEYDOWN和WM_SYSKEYUP消息通常由与Alt组合的击键产生,这些键激活程序菜单或系统菜单上的选项,或切换活动窗口,也可以用作系统菜单加速键。由于Windows处理所有Alt键的功能,应用程序无需捕获这些消息。

例如:

单个按健:PostMessage(h,WM_KEYDOWN,VK_F9,0);

ALT+按键:PostMessage(h,WM_SYSKEYDOWN,70,$20000000);

格式::PostMessage(MyHwnd,WM_KEYDOWN,wParam,lParam)

如果你确定要发送键盘消息,那么再来看看如何确定键盘消息中的wParam和lParam这两个参数。

wParam参数的含义较简单,它表示你要发送的键盘事件的按键虚拟码,比如你要对目标程序模拟按下A键,那么wParam参数的值就设为VK_A,

lParam这个参数就比较复杂了,因为它包含了多个信息,一般可以把它设为0。即PostMessage(Hwnd,WM_KEYDOWN,key,0);

但是如果你想要你的模拟更真实一些,或者你向记事本的文本框发送字符消息。那么你还需要设置一下这个参数。那么我们就详细了解一下lParam吧。

lParam这个参数就比较复杂了,因为它包含了多个信息。如果按住一个键不放,会使得自动重复功能生效,那么该键最后被释放时,Windows会给窗口过程发送一系列的WM_KEYDOWN(或WM_SYSKEYDWON)消息和一个WM_KEYUP(或WM_SYSKEYUP)消息。如果是发送“击键”,不需要产生字符,一般可以把它设为0。即PostMessage(Hwnd,WM_KEYDOWN,key,0);而对于需要产生“字符”的击键消息,比如如向记事本发送字符“A”,要设置参数lParam,否则会产生一系列A。那么我们就详细了解一下lParam吧。

lParam是一个32bit的参数,它在内存中占4个字节,写成二进制就是

00000000000000000000000000000000

大家一般习惯写成16进制的,那么就应该是

&H00000000

一共是32位,我们从右向左数,假设最右边那位为第0位(注意是从0而不是从1开始计数),最左边的就是第31位。那么该参数的

0-15位表示键的发送次数等扩展信息//一般为&H0001如果不设置发送次数,因为按下键盘键,会自动触发重复发送消息。比如你向记事本的文本框发送字符消息消息时会出现多个字符。

16-23位为按键的扫描码

24-31位表示是按下键还是释放键。按下键&H00,释放键则为&HC0,

MapVirtualKey(

uCode:UINT;{键值、扫描码或虚拟码keycode,scancodeorvirtualkey}

uMapType:UINT{flagsfortranslationmode}

):UINT;{returnstranslatedkeycode}

参数uCode表示待转换的码,参数uMapType表示从什么转换为什么,如果是虚拟码转扫描码,则uMapType设置为0,如果是扫描码转虚拟码,则wMapType设置为1,如果是虚拟码转ASCII码,则uMapType设置为2。相信有了这些,我们就可以构造键盘事件的lParam参数了。

下面给出一个构造lParam参数的函数:

functionVKB_param(VirtualKey:Integer;flag:Integer):Integer;//函数名

var

s,Firstbyte,Secondbyte:String;

S_code:Integer;

Begin

ifflag=1then//按下键

begin

Firstbyte:='00'

end

else//弹起键

Firstbyte:='C0'

end;

S_code:=MapVirtualKey(VirtualKey,0);

Secondbyte:='00'+inttostr(s_code);

Secondbyte:=copy(Secondbyte,Length(Secondbyte)-1,2);

s:='$'+Firstbyte+Secondbyte+'0001';

Result:=strtoint(s);

End;

使用按键的方法:

说明:key为键值,如2键的值是$32,flag传递的是按键状态,1是按下,0是弹起。

lparam:=VKB_param(key,1);{按下键}

PostMessage(Hwnd,WM_KEYDOWN,key,lParam);

lParam:=VKB_param(key,0);{松开键}

PostMessage(Hwnd,WM_KEYUP,key,lParam);

hwnd,lparam:Cardinal;

//hwnd:=FindWindow(nil,'无标题-记事本');//这个函数不能在记事本输入。因为记事本里接收字符的是子窗口edit。

hwnd:=FindWindowEx(FindWindow(nil,'无标题-记事本'),0,'edit',nil);//获取记事本窗口句柄

lparam:=VKB_param(97,1);//按下键

//PostMessage(hwnd,WM_KEYDOWN,vk_F3,lparam);//按下F3键

PostMessage(hwnd,WM_CHAR,97,lparam);//输入字符(edit控件接收字符)

lParam:=VKB_param(97,0);//松开键

//PostMessage(hwnd,WM_KEYUP,vk_F3,lparam);//释放F3键

////////////////////////////////////

FindWindow(

lpClassName,{窗口的类名}

lpWindowName:PChar{窗口的标题}

):HWND;{返回窗口的句柄;失败返回0}

//FindWindowEx比FindWindow多出两个句柄参数:

FindWindowEx(

Parent:HWND;{要查找子窗口的父窗口句柄}

Child:HWND;{子窗口句柄}

ClassName:PChar;{窗口的类名}

WindowName:PChar{窗口的标题}

):HWND;

///////////////////////////////

Msg模拟鼠标点击消息参数:

/////////

wparam模拟鼠标点击设置为0

Lparam参数为鼠标坐标。

对lparam的描述是:

lParam:低16位存放X坐标,高16位存放Y坐标

那么0到15位就是低16位,16到31就是高16位。

比如你有个坐标p.x=290p.y=48。那么换成2进制

p.x=00000000000000000000000100100010

p.y=00000000000000000000000000110000

因为刚才说了lParam高16位是要存放y坐标的值,低16位存放X坐标的值。这样才能组成一个正确的lParam.

所以就要把p.y的值放到高16位去。怎么放呢?就是把p.y的低16的值全部左移16位移到高16位去。运算:p.xshl16;移动之后就变成

p.y=00000000001100000000000000000000

然后把p.y和p.x的值组合成一个新的整数。lparam:=p1.X+p1.Yshl16;

lParam=00000000001100000000000100100010

前面的是p.y的二进制数表示,后面是p.x的二进制表示。

----------------------------------------------------------------------------------------------------------------------

{补充小知识:}

shl左移位shr右移位

shl是按位左移,右边补零shr是按位右移,左边补零

左移一位等于乘2,右移一位等于除2。

左移2位等于乘2的2次方,右移一位等于除2的2次方。

左移n位等于乘2的n次方,右移n位等于除2的n次方。

所以lparam:=p1.X+p1.Yshl16也可以用:lparam:=p1.X+p1.Y*power(2,16)(即lparam:=p1.X+p1.Y*65536)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Var

P1:Tpoint;

Lparam:integer;

GetCursorPos(P1);//获取屏幕坐标

P1.X:=P1.X+100;

P1.Y:=P1.Y+200;

lparam:=p1.X+p1.Yshl16;

sendmessage(h,messages.WM_LBUTTONDOWN,0,lparam);//按下鼠标左键

sendmessage(h,messages.WM_LBUTTONUP,0,lparam);//抬起鼠标左键

{-------------------------------------------}

{模拟键盘鼠标过程}

unitUAnalogKeyboardAndMouseDM;

interface

uses

Windows,Messages;

type

(*定义鼠标入键盘事件常量*)

TClickType=(leftDown,rightDown,midDown,leftUp,rightUp,midUp,leftDB,rightDB,midDB,vkeyDown,vkeyUp,vKeyClick,pageUp,PageDown);

procedureSendMouseClick(constWinHandle:HWND;constPosX,PosY:integer;constClickFlag:TClickType);

procedureSendKey(constWinHandle:HWND;constVkey:word;

constKeyClickFlag:TClickType);

implementation

{-------------------}

{*模拟鼠标*}

caseClickFlagof

leftDown:

PostMessage(WinHandle,WM_LButtonDown,0,PosX+PosY*65536);//左键按下

leftUp:

PostMessage(WinHandle,WM_LButtonUp,0,PosX+PosY*65536);//左键放开

//-----

rightDown:

PostMessage(WinHandle,WM_RButtonDown,0,PosX+PosY*65536);//右键按下

rightUp:

PostMessage(WinHandle,WM_RButtonUp,0,PosX+PosY*65536);//右键放开

midDown:

PostMessage(WinHandle,WM_MBUTTONDOWN,0,PosX+PosY*65536);//中间键按下

midUp:

PostMessage(WinHandle,WM_MButtonUp,0,PosX+PosY*65536);//中键放开

leftDB:

PostMessage(WinHandle,WM_LBUTTONDBLCLK,0,PosX+PosY*65536);//左键双击

rightDB:

PostMessage(WinHandle,WM_RBUTTONDBLCLK,0,PosX+PosY*65536);//右键双击

midDB:

PostMessage(WinHandle,WM_MBUTTONDBLCLK,0,PosX+PosY*65536);//中键双击

{*模拟键盘*}

caseKeyClickFlagof

vkeyDown:postMessage(WinHandle,WM_KEYDOWN,vkey,MapVirtualKey(Vkey,0));

vkeyUp:postMessage(WinHandle,WM_KEYUP,vkey,MapVirtualKey(Vkey,0));

vkeyClick:

postMessage(WinHandle,WM_KEYDOWN,vkey,MapVirtualKey(Vkey,0));

postMessage(WinHandle,WM_KEYUP,vkey,MapVirtualKey(Vkey,0));

end.

利用PostMessage模拟键盘鼠标的关键是获取窗口句柄handle。那么如何获取窗口(或子窗口)句柄呢

获取handle(窗口句柄)的方法通常有:

这个函数能够找出鼠标当前位置所对应的窗口句柄。

如:

handle:=FindWindow(nil,PChar('窗口的标题'));

或者:

procedureTForm1.Button1Click(Sender:TObject);

hCurrentWindow:HWnd;

WndText:String;

hCurrentWindow:=GetWindow(Handle,GW_HWNDFIRST);

whilehCurrentWindow<>0do

WndText:=GetWndText(hCurrentWindow);

ifUpperCase(WndText)='窗口的标题'thenbegin

...

hCurrentWindow:=GetWindow(hCurrentWindow,GW_HWNDNEXT);

//==========================================================

ClassName:PChar;{}

WindowName:PChar{}

{

如果Parent是0,则函数以桌面窗口为父窗口,查找桌面窗口的所有子窗口;

如果Parent是HWND_MESSAGE,函数仅查找所有消息窗口;

Child子窗口必须是Parent窗口的直接子窗口;

如果Child是0,查找从Parent的第一个子窗口开始;

如果Parent和Child同时是0,则函数查找所有的顶层窗口及消息窗口.

}

//测试1:试着找找新建程序主窗口的句柄

h:HWND;

{现在我们知道窗口的标题是:Form1、窗口的类名是:TForm1}

h:=FindWindow('TForm1','Form1');

ShowMessage(IntToStr(h));{656180;这是随机,每次启动窗口肯定不一样}

{假如不知道类名}

h:=FindWindow(nil,'Form1');

ShowMessage(IntToStr(h));{656180}

{假如不知道标题名}

h:=FindWindow('TForm1',nil);

{其实这个窗口的句柄不就是Self.Handle吗}

ShowMessage(IntToStr(Handle));{656180}

//测试2:找计算器窗口的句柄(先打开计算器)

{如果不是简体中文系统,这样可能不灵}

h:=FindWindow(nil,'计算器');

ShowMessage(IntToStr(h));{1508334}

{最好这样,但你得提前知道计算器窗口的类名是:SciCalc}

h:=FindWindow('SciCalc',nil);

首先通过进程快照得到要查找的进程ID(ProcessId),

其次,再跟据ProcessId获取进程的窗口句柄。以下为代码:

usesTLHelp32;//注意加上这个模块

ProcessName:string;//进程名

FSnapshotHandle:THandle;//进程快照句柄

FProcessEntry32:TProcessEntry32;//进程入口的结构体信息

ContinueLoop:BOOL;

MyHwnd:THandle;

FSnapshotHandle:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);//创建一个进程快照

FProcessEntry32.dwSize:=Sizeof(FProcessEntry32);

ContinueLoop:=Process32First(FSnapshotHandle,FProcessEntry32);//得到系统中第一个进程

//循环例举

whileContinueLoopdo

ProcessName:=FProcessEntry32.szExeFile;

if(ProcessName='要找的应用程序名.exe')thenbegin

MyHwnd:=GetHWndByPID(FProcessEntry32.th32ProcessID);

ContinueLoop:=Process32Next(FSnapshotHandle,FProcessEntry32);

CloseHandle(FSnapshotHandle);//释放快照句柄

//跟据ProcessId获取进程的窗口句柄

functionTForm1.GetHWndByPID(consthPID:THandle):THandle;

PEnumInfo=^TEnumInfo;

TEnumInfo=record

ProcessID:DWORD;

HWND:THandle;

functionEnumWindowsProc(Wnd:DWORD;varEI:TEnumInfo):Bool;stdcall;

PID:DWORD;

GetWindowThreadProcessID(Wnd,@PID);

Result:=(PID<>EI.ProcessID)or

(notIsWindowVisible(WND))or

(notIsWindowEnabled(WND));

ifnotResultthenEI.HWND:=WND;

functionFindMainWindow(PID:DWORD):DWORD;

EI:TEnumInfo;

EI.ProcessID:=PID;

EI.HWND:=0;

EnumWindows(@EnumWindowsProc,Integer(@EI));

Result:=EI.HWND;

ifhPID<>0then

Result:=FindMainWindow(hPID)

else

Result:=0;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

即所有窗口都可以接收到键盘模拟。所以在利用全局模拟时有必要先把接收模拟键盘鼠标的窗口置顶。

模拟全局键盘和鼠标消息常见的可以有以下一些方法:

keybd_event(

bVk:Byte;{虚拟键值}

bScan:Byte;{扫描码}

dwFlags:DWORD;{标志选项}

dwExtraInfo:DWORD{关于键的附加选项}

);{这个过程没有返回值}

keybd_event(VK_A,0,0,0);//按下A键

Sleep(200);

keybd_event(VK_A,0,KEYEVENTF_KEYUP,0);//释放A键

Sleep(

dwMilliseconds:DWORD{指定要暂停的毫秒数}

);

那么如果要模拟按下功能键怎么做呢?比如要按下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函数来模拟键盘事件。

mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//鼠标左键按下

mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);//鼠标左键弹起

{===========================================

模拟多次点击鼠标左键。默认1次。

============================================}

procedureSeriesLeftClick(i:Byte=1);

f:Byte;

forf:=1toido

Mouse_Event(MouseEventf_leftDown,0,0,0,0);

Mouse_Event(MouseEventf_leftUp,0,0,0,0);

SendInput可以直接把一条消息插入到消息队列中,算是比较底层的了。

functionSendInput(

cInputs:UINT;//pInputs中记录数组的元素数目

varpInputs:TInput;//TInput类型记录数组的第1个元素

cbSize:Integer//定义TInput的大小,一般为SizeOf(TInput)

):UINT;stdcall;

cInputs:定义pInputs中记录数组的元素数目。

pInputs:TInput类型记录数组的第1个元素。每个元素代表插人到系统消息队列的键盘或鼠标事件。

cbSize:定义TInput的大小,一般为SizeOf(TInput)。函数返回成功插入系统消息队列中事件的数目,失败返回0。

tagINPUT=packedrecord

Itype:DWORD;

caseIntegerof

0:(mi:TMouseInput);

1:(ki:TKeybdInput);

2:(hi:THardwareInput);

TInput=tagINPUT;

其中mi、ki、hi是3个共用型的记录结构,Itype指出记录结构中所使用的类型,它有3个值。

INPUT_MOUSE:表示使用mi记录结构,忽略ki和hi;

INPUT_KEYBOARD:表示使用ki记录结构,忽略mi和hi。

tagKEYBDINPUT=packedrecord

wVk:WORD;//是将要操作的按键的虚键码

wScan:WORD;//是安全码,一般不用

dwFlags:DWORD;//指定键盘所进行的操作,为0时表示按下某键,KEYEVENTF_KEYUP表示放开某键

time:DWORD;

dwExtraInfo:DWORD;//是扩展信息,可以使用API函数GetMessageExtraInfo的返回值

TKeybdInput=tagKEYBDINPUT;

procedureKeyPressA;

Inputs:array[0..1]ofTInput;

Inputs[0].Itype:=INPUT_KEYBOARD;

withInputs[0].kido

wVk:=VK_A;

wScan:=0;

dwFlags:=0;

time:=GetTickCount;

dwExtraInfo:=GetMessageExtraInfo;

Inputs[1].Itype:=INPUT_KEYBOARD;

withInputs[1].kido

dwFlags:=KEYEVENTF_KEYUP;

SendInput(2,Inputs[0],SizeOf(TInput));

tagMOUSEINPUT=packedrecord

dx:Longint;

dy:Longint;

mouseData:DWORD;

dwFlags:DWORD;

dwExtraInfo:DWORD;

TMouseInput=tagMOUSEINPUT;

其中dx、dy是鼠标移动时的坐标差(不是象素单位),在鼠标移动时有效。

mouseData是鼠标滚轮滚动值,在滚动鼠标滚轮时有效。当mouseData小于0时向下滚动,当mouseData大于0时向上滚动,mouseData的绝对值一般设为120。

dwFlags指定鼠标所进行的操作,例如,MOUSEEVENTF_MOVE表示移动鼠标,MOUSEEVENTF_LEFTDOWN表示按下鼠标左键,MOUSEEVENTF_LEFTUP表示放开鼠标左键。

dwExtraInfo是扩展信息,可以使用API函数GetMessageExtraInfo的返回值。例如单击鼠标左键的程序如下:

procedureMouseClick;

Inputs[0].Itype:=INPUT_MOUSE;

withInputs[0].mido

dx:=0;

dy:=0;

mouseData:=0;

dwFlags:=MOUSEEVENTF_LEFTDOWN;

Inputs[1].Itype:=INPUT_MOUSE;

withInputs[1].mido

dwFlags:=MOUSEEVENTF_LEFTUP;

鼠标的移动总是很麻烦,上面的dx、dy不是以象素为单位的,而是以鼠标设备移动量为单位的,它们之间的比值受鼠标移动速度设置的影响。具体的解决方法可参考《Delphi下利用WinIo模拟鼠标键盘详解》,这里不再重复。

dwFlags可以设置一个MOUSEEVENTF_ABSOLUTE标志,这使得可以用另外一种方法移动鼠标。当dwFlags设置了MOUSEEVENTF_ABSOLUTE标志,dx、dy为屏幕坐标值,表示将鼠标移动到dx,dy的位置。但是这个坐标值也不是以象素为单位的。这个值的范围是0到65535($FFFF),当dx等于0、dy等于0时表示屏幕的最左上角,当dx等于65535、dy等于65535时表示屏幕的最右下角,相当于将屏幕的宽和高分别65536等分。API函数GetSystemMetrics(SM_CXSCREEN)可以返回屏幕的宽度,

函数GetSystemMetrics(SM_CYSCREEN)可以返回屏幕的高度,利用屏幕的宽度和高度就可以将象素坐标换算成相应的dx、dy。注意:这种换算最多会出现1象素的误差。

例如:将鼠标指针移动到屏幕150,120坐标处的程序如下:

procedureMouseMove;

Input:TInput;

Input.Itype:=INPUT_MOUSE;

withInput.mido

dx:=($FFFFdiv(GetSystemMetrics(SM_CXSCREEN)-1))*150;

dy:=($FFFFdiv(GetSystemMetrics(SM_CYSCREEN)-1))*120;

dwFlags:=MOUSEEVENTF_MOVEorMOUSEEVENTF_ABSOLUTE;

SendInput(1,Input,SizeOf(TInput));

1、procedureSIKeyDown(Key:WORD);

按下指定的键。Key为虚键码。

2、procedureSIKeyUp(Key:WORD);

放开指定的键。Key为虚键码。

3、procedureSIKeyPress(Key:WORD;Interval:Cardinal);

4、procedureSIKeyInput(constText:String;Interval:Cardinal);

示范程序,组合键Ctrl+A如下:

SIKeyDown(VK_CONTROL);//按下Ctrl

SIKeyPress(VK_A);//击键A

SIKeyUp(VK_CONTROL);//放开Ctrl

5、procedureSIMouseDown(Key:WORD);

按下鼠标的指定键。Key为虚键码,鼠标左键为VK_LBUTTON,右键为VK_RBUTTON,中键为VK_MBUTTON。

6、procedureSIMouseUp(Key:WORD);

放开鼠标的指定键。Key为虚键码,鼠标左键为VK_LBUTTON,右键为VK_RBUTTON,中键为VK_MBUTTON。

7、procedureSIMouseClick(Key:WORD;Interval:Cardinal);

8、procedureSIMouseWheel(dZ:Integer);

滚动鼠标的滚轮。当dZ小于0时向下滚动,当dZ大于0时向上滚动,dZ的绝对值一般设为120。

9、procedureSIMouseMoveTo(X,Y:Integer;MaxMove:Integer;Interval:Cardinal);

示范程序,拖放到指定位置如下:

SIMouseDown(VK_LBUTTON);//按下鼠标左键

SIMouseMoveTo(780,300);//移动到指定位置

SIMouseUp(VK_LBUTTON);//放开鼠标左键

//作者:yeye552010年1月31日

//

//如果你转载了本文件中的代码,请注明代码出处和代码作者;

//如果你修改了本文件中的代码,请注明修改位置和修改作者。

unitSIMouseKeyboard;

Windows;

const

//虚键码定义

VK_LBUTTON=$01;

VK_RBUTTON=$02;

VK_CANCEL=$03;

VK_MBUTTON=$04;//*NOTcontiguouswithL&RBUTTON*/

VK_BACK=$08;

VK_TAB=$09;

VK_CLEAR=$0C;

VK_RETURN=$0D;

VK_SHIFT=$10;

VK_CONTROL=$11;

VK_MENU=$12;

VK_PAUSE=$13;

VK_CAPITAL=$14;

VK_KANA=$15;

VK_HANGEUL=$15;//*oldname-shouldbehereforcompatibility*/

VK_HANGUL=$15;

VK_JUNJA=$17;

VK_FINAL=$18;

VK_HANJA=$19;

VK_KANJI=$19;

VK_ESCAPE=$1B;

VK_CONVERT=$1C;

VK_NONCONVERT=$1D;

VK_ACCEPT=$1E;

VK_MODECHANGE=$1F;

VK_SPACE=$20;

VK_PRIOR=$21;

VK_NEXT=$22;

VK_END=$23;

VK_HOME=$24;

VK_LEFT=$25;

VK_UP=$26;

VK_RIGHT=$27;

VK_DOWN=$28;

VK_SELECT=$29;

VK_PRINT=$2A;

VK_EXECUTE=$2B;

VK_SNAPSHOT=$2C;

VK_INSERT=$2D;

VK_DELETE=$2E;

VK_HELP=$2F;

VK_C0=$C0;//“`”和“~”

VK_BD=$BD;//“-”和“_”

VK_BB=$BB;//“=”和“+”

VK_DC=$DC;//“\”和“|”

VK_DB=$DB;//“[”和“{”

VK_DD=$DD;//“]”和“}”

VK_BA=$BA;//“;”和“:”

VK_DE=$DE;//“'”和“"”

VK_BC=$BC;//“,”和“<”

VK_BE=$BE;//“.”和“>”

VK_BF=$BF;//“/”和“”

{*VK_0thruVK_9arethesameasASCII'0'thru'9'(0x30-0x39)*}

VK_0=$30;

VK_1=$31;

VK_2=$32;

VK_3=$33;

VK_4=$34;

VK_5=$35;

VK_6=$36;

VK_7=$37;

VK_8=$38;

VK_9=$39;

{*VK_AthruVK_ZarethesameasASCII'A'thru'Z'(0x41-0x5A)*}

VK_A=$41;

VK_B=$42;

VK_C=$43;

VK_D=$44;

VK_E=$45;

VK_F=$46;

VK_G=$47;

VK_H=$48;

VK_I=$49;

VK_J=$4A;

VK_K=$4B;

VK_L=$4C;

VK_M=$4D;

VK_N=$4E;

VK_O=$4F;

VK_P=$50;

VK_Q=$51;

VK_R=$52;

VK_S=$53;

VK_T=$54;

VK_U=$55;

VK_V=$56;

VK_W=$57;

VK_X=$58;

VK_Y=$59;

VK_Z=$5A;

VK_LWIN=$5B;

VK_RWIN=$5C;

VK_APPS=$5D;

VK_NUMPAD0=$60;

VK_NUMPAD1=$61;

VK_NUMPAD2=$62;

VK_NUMPAD3=$63;

VK_NUMPAD4=$64;

VK_NUMPAD5=$65;

VK_NUMPAD6=$66;

VK_NUMPAD7=$67;

VK_NUMPAD8=$68;

VK_NUMPAD9=$69;

VK_MULTIPLY=$6A;

VK_ADD=$6B;

VK_SEPARATOR=$6C;

VK_SUBTRACT=$6D;

VK_DECIMAL=$6E;

VK_DIVIDE=$6F;

VK_F1=$70;

VK_F2=$71;

VK_F3=$72;

VK_F4=$73;

VK_F5=$74;

VK_F6=$75;

VK_F7=$76;

VK_F8=$77;

VK_F9=$78;

VK_F10=$79;

VK_F11=$7A;

VK_F12=$7B;

VK_F13=$7C;

VK_F14=$7D;

VK_F15=$7E;

VK_F16=$7F;

VK_F17=$80;

VK_F18=$81;

VK_F19=$82;

VK_F20=$83;

VK_F21=$84;

VK_F22=$85;

VK_F23=$86;

VK_F24=$87;

VK_NUMLOCK=$90;

VK_SCROLL=$91;

{*

*VK_L*&VK_R*-leftandrightAlt,CtrlandShiftvirtualkeys.

*UsedonlyasparameterstoGetAsyncKeyState()andGetKeyState().

*NootherAPIormessagewilldistinguishleftandrightkeysinthisway.

*}

VK_LSHIFT=$A0;

VK_RSHIFT=$A1;

VK_LCONTROL=$A2;

VK_RCONTROL=$A3;

VK_LMENU=$A4;

VK_RMENU=$A5;

VK_PROCESSKEY=$E5;

VK_ATTN=$F6;

VK_CRSEL=$F7;

VK_EXSEL=$F8;

VK_EREOF=$F9;

VK_PLAY=$FA;

VK_ZOOM=$FB;

VK_NONAME=$FC;

VK_PA1=$FD;

VK_OEM_CLEAR=$FE;

//功能函数

procedureSIKeyDown(Key:WORD);

procedureSIKeyUp(Key:WORD);

procedureSIKeyPress(Key:WORD;Interval:Cardinal=0);

procedureSIKeyInput(constText:String;Interval:Cardinal=0);

procedureSIMouseDown(Key:WORD);

procedureSIMouseUp(Key:WORD);

procedureSIMouseClick(Key:WORD;Interval:Cardinal=0);

procedureSIMouseWheel(dZ:Integer);

procedureSIMouseMoveTo(X,Y:Integer;MaxMove:Integer=20;Interval:Cardinal=0);

PerWidth:Integer;//每象素宽度单位

PerHeight:Integer;//每象素高度单位

{功能函数}

//按下指定的键。

Input.Itype:=INPUT_KEYBOARD;

withInput.kido

wVk:=Key;

//放开指定的键。

procedureSIKeyPress(Key:WORD;Interval:Cardinal);

ifInterval<>0thenSleep(Interval);

//模拟键盘输入指定的文本,文本中只能是单字节字符(#32~#126)

//以及Tab(#9)键和回车键(#13),其它字符会被忽略,

procedureSIKeyInput(constText:String;Interval:Cardinal);

TCharTable=record

Key:WORD;

Char:array[0..1]ofAnsiChar;

CharCount=50;

CharTable:array[0..CharCount-1]ofTCharTable=(

(Key:VK_A;Char:'aA'),(Key:VK_B;Char:'bB'),

(Key:VK_C;Char:'cC'),(Key:VK_D;Char:'dD'),

(Key:VK_E;Char:'eE'),(Key:VK_F;Char:'fF'),

(Key:VK_G;Char:'gG'),(Key:VK_H;Char:'hH'),

(Key:VK_I;Char:'iI'),(Key:VK_J;Char:'jJ'),

(Key:VK_K;Char:'kK'),(Key:VK_L;Char:'lL'),

(Key:VK_M;Char:'mM'),(Key:VK_N;Char:'nN'),

(Key:VK_O;Char:'oO'),(Key:VK_P;Char:'pP'),

(Key:VK_Q;Char:'qQ'),(Key:VK_R;Char:'rR'),

(Key:VK_S;Char:'sS'),(Key:VK_T;Char:'tT'),

(Key:VK_U;Char:'uU'),(Key:VK_V;Char:'vV'),

(Key:VK_W;Char:'wW'),(Key:VK_X;Char:'xX'),

(Key:VK_Y;Char:'yY'),(Key:VK_Z;Char:'zZ'),

(Key:VK_0;Char:'0)'),(Key:VK_1;Char:'1!'),

(Key:VK_2;Char:'2@'),(Key:VK_3;Char:'3#'),

(Key:VK_4;Char:'4$'),(Key:VK_5;Char:'5%'),

(Key:VK_6;Char:'6^'),(Key:VK_7;Char:'7&'),

(Key:VK_8;Char:'8*'),(Key:VK_9;Char:'9('),

(Key:VK_C0;Char:'`~'),(Key:VK_BD;Char:'-_'),

(Key:VK_BB;Char:'=+'),(Key:VK_DC;Char:'\|'),

(Key:VK_DB;Char:'[{'),(Key:VK_DD;Char:']}'),

(Key:VK_BA;Char:';:'),(Key:VK_DE;Char:#39+'"'),

(Key:VK_BC;Char:',<'),(Key:VK_BE;Char:'.>'),

(Key:VK_BF;Char:'/'),(Key:VK_SPACE;Char:''+#0),

(Key:VK_TAB;Char:#9#0),(Key:VK_RETURN;Char:#13#0));

CapsState,NeedShift:Boolean;

i,id:Integer;

CapsState:=((GetKeyState(VK_CAPITAL)and1)<>0);

fori:=1toLength(Text)do

forid:=0toCharCount-1do

if(CharTable[id].Char[0]=Text[i])or

(CharTable[id].Char[1]=Text[i])then

break;

ifid>=CharCountthencontinue;

NeedShift:=(CharTable[id].Char[1]=Text[i]);

if(CharTable[id].Char[0]>='a')and

(CharTable[id].Char[0]<='z')andCapsStatethen

NeedShift:=notNeedShift;

//按下上档键

ifNeedShiftthen

wVk:=VK_SHIFT;

//按下指定键

wVk:=CharTable[id].Key;

//放开指定键

//放开上档键

//按下鼠标的指定键。

caseKeyof

VK_LBUTTON:dwFlags:=MOUSEEVENTF_LEFTDOWN;

VK_RBUTTON:dwFlags:=MOUSEEVENTF_RIGHTDOWN;

VK_MBUTTON:dwFlags:=MOUSEEVENTF_MIDDLEDOWN;

elseexit;

//放开鼠标的指定键。

VK_LBUTTON:dwFlags:=MOUSEEVENTF_LEFTUP;

VK_RBUTTON:dwFlags:=MOUSEEVENTF_RIGHTUP;

VK_MBUTTON:dwFlags:=MOUSEEVENTF_MIDDLEUP;

procedureSIMouseClick(Key:WORD;Interval:Cardinal);

//滚动鼠标的滚轮。

mouseData:=DWORD(dZ);

dwFlags:=MOUSEEVENTF_WHEEL;

//将鼠标指针移动到指定位置,返回是否成功,

//X和Y为象素值,X和Y的值的范围不能超出屏幕,

//MaxMove为移动时的dX和dY的最大值,

procedureSIMouseMoveTo(X,Y:Integer;MaxMove:Integer;Interval:Cardinal);

p:TPoint;

n:Integer;

ifMaxMove<=0thenMaxMove:=$7FFFFFFF;

GetCursorPos(p);

while(p.X<>X)or(p.Y<>Y)do

n:=X-p.X;

ifAbs(n)>MaxMovethen

ifn>0thenn:=MaxMove

elsen:=-MaxMove;

p.X:=p.X+n;

n:=Y-p.Y;

p.Y:=p.Y+n;

dx:=p.X*PerWidth;

dy:=p.Y*PerHeight;

initialization

PerWidth:=($FFFFdiv(GetSystemMetrics(SM_CXSCREEN)-1));//每象素宽度单位

PerHeight:=($FFFFdiv(GetSystemMetrics(SM_CYSCREEN)-1));//每象素高度单位

//=================================================

叛逆的鲁鲁修loveCC于2019-07-2223:56:37发布

最近接触到这个函数,因此了解了一下,总结一下列在这。

我了解它的出发点是如何通过它向活动窗口输入字符,这是很多程序都有的功能(我猜VisualAssistX就用了这个功能)。

根据MSDN,此函数模拟按键操作,将一些消息插入键盘或鼠标的输入流中,Windows对它进行处理,生成相应的WM_KEYDOWN或WM_KEYUP事件,这些事件与普通键盘输入一起进入应用程序的消息循环,它们不仅可以转换为WM_CHAR消息,还可以转换为其它(诸如加速键)等消息。

使用它来发送字符消息,并没有看起来那么简单。这有两个需要考虑的问题:

1.输入法的转换。例如需要向活动窗口发送一些英文字符,我们可能想象这样来实现:获取对应键盘字符的虚拟键码,发送一个SendInput。但是如果活动窗口正在使用一个输入法,那么我们发送出去的消息,会进入输入法的Composition窗口,最终被转换为象形文字或被丢弃。只有当输入法关闭时,程序运行的效果才会像我们期望的那样,在活动窗口中显示出英文字符。

如上所述,若直接如想象中那样使用SendInput来输入字符,则必须分析活动窗口的输入法状态。而且输入英文时,要求关闭输入法,输入中文时,又要求打开输入法。若真要以这样的思路来实现,则必定是难以成功的。

那么,有没有不依赖活动窗口输入法状态的方式呢?

其实是有的,使用SendInput模拟键盘输入时,其参数是KEYBDINPUT结构,通过将其dwFlags成员设置KEYEVENTF_UNICODE就可以了。使用此方式,只需将KEYBDINPUT.wScan设置为字符的Unicode编码即可。对于英文字符,不需要关闭活动窗口的输入法;对于中文字符,也不要求活动窗口打开输入法和将字符转换为输入法编码。

MSDN对此方式的说明为:INPUT_KEYBOARD支持非键盘的输入方式,例如手写识别或语音识别,通过KEYEVENTF_UNICODE标识,这些方式与键盘(文本)输入别无二致。如果指定了KEYEVENTF_UNICODE,SendInput发送一个WM_KEYDOWN或WM_KEYUP消息给活动窗口的线程消息队列,消息的wParam参数为VK_PACKET。GetMessage或PeedMessage一旦获得此消息,就把它传递给TranslateMessage,TranslateMessage根据wScan中指定的Unicode字符产生一个WM_CHAR消息。若窗口是ANSI窗口,则Unicode字符会自动转换为相应的ANSI字符。

任何需要向活动窗口输入字符(包括英文)的功能均应使用这种方式来实现。事实上,键盘消息转换为字符消息的过程是很复杂的,这可能与键盘布局、区域、换档状态等诸多因素有关,这也是Windows要使用TranslateMessage来转换消息的原因。因此,不应该试图通过击键事件来意图向活动窗口输入特定的字符。

经测试,SendInput还有两个值得注意的地方:

1.没有为KEYBDINPUT.dwFlags指定KEYEVENTF_KEYUP标识时,SendInput将生成WM_KEYDOWN消息,否则生成WM_KEYUP消息,由于只有WM_KEYDOWN会转换为字符消息,因此,若以输入字符为目标,则不应指定KEYEVENTF_KEYUP标识。

输入法也可以处理SendInput发送的Unicode消息,具体方式不详。见MSDN中ImmGetProperty方法的参考:当dwIndex参数为IGP_PROPERTY时,IME_PROP_ACCEPT_WIDE_VKEY是一个可能的返回值,它表示IME会处理SendInput函数以VK_PACKET注入的Unicode字符,若返回值无该标识,则Unicode字符会直接发送给应用程序。

WinIo有很多缺点,SendInput几乎没有这些缺点。SendInput的模拟要比WinIo简单的多。事件是被直接插入到系统消息队列的,所以它的速度比WinIo要快。系统也会保证数据的完整性,不会出现数据包混乱的情况。利用“绝对移动”可以将鼠标指针移动到准确的位置,同鼠标的配置隔离不会出现兼容性的问题。SendInput的缺点也是最要命的,它会被一些程序屏蔽。所以说在SendInput与WInIo都可以使用的情况下优先考虑SendInput。另外SendInput与WInIo可以接合使用,一些程序对鼠标左键单击敏感,可以使用WinIo模拟鼠标左键单击,其它操作由SendInput模拟。

{*****************************************************************************}

如果你对windows中消息钩子的用法已经有所了解,那么你可以通过设置一个全局HOOK来模拟键盘消息,比如,你可以用WH_JOURNALPLAYBACK这个钩子来模拟按键。WH_JOURNALPLAYBACK是一个系统级的全局钩子,它和WH_JOURNALRECORD的功能是相对的,常用它们来记录并回放键盘鼠标操作。WH_JOURNALRECORD钩子用来将键盘鼠标的操作忠实地记录下来,记录下来的信息可以保存到文件中,而WH_JOURNALPLAYBACK则可以重现这些操作。当然亦可以单独使用WH_JOURNALPLAYBACK来模拟键盘操作。

SetWindowsHookEx函数,它可以用来安装消息钩子:

SetWindowsHookEx(

idHook:Integer;{hooktypeflag}

lpfn:TFNHookProc;{apointertothehookfunction}

hmod:HINST;{ahandletothemodulecontainingthehookfunction}

dwThreadId:DWORD{theidentifieroftheassociatedthread}

):HHOOK;

先安装WH_JOURNALPLAYBACK这个钩子,然后你需要自己写一个钩子函数,在系统调用它时,把你要模拟的事件传递给钩子参数lParam所指向的EVENTMSG区域,就可以达到模拟按键的效果。不过用这个钩子模拟键盘事件有一个副作用,就是它会锁定真实的鼠标键盘,不过如果你就是想在模拟的时候不会受真实键盘操作的干扰,那么用用它倒是个不错的主意。

在不需要监视系统消息时需要调用提供UnHookWindowsHookEx来解除对消息的监视。下面来建立程序,在Delphi中建立一个工程,在Form1上添加3个按钮用于程序操作。另外再添加一个按钮控件和一个Edit控件用于验证操作。

下面是Form1的全部代码

unitUnit1;

Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,

StdCtrls;

TForm1=class(TForm)

Button1:TButton;

Button2:TButton;

Button3:TButton;

Edit1:TEdit;

Button4:TButton;

procedureFormCreate(Sender:TObject);

procedureButton1Click(Sender:TObject);

procedureButton2Click(Sender:TObject);

procedureButton3Click(Sender:TObject);

private

{Privatedeclarations}

public

{Publicdeclarations}

Form1:TForm1;

EventArr:array[0..1000]ofEVENTMSG;

EventLog:Integer;

PlayLog:Integer;

hHook,hPlay:Integer;

recOK:Integer;

canPlay:Integer;

bDelay:Bool;

{$R*.DFM}

FunctionPlayProc(iCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall;

canPlay:=1;

ifiCode<0then//必须将消息传递到消息链的下一个接受单元

Result:=CallNextHookEx(hPlay,iCode,wParam,lParam)

elseifiCode=HC_SYSMODALONthen

canPlay:=0

elseifiCode=HC_SYSMODALOFFthen

canPlay:=1

elseif((canPlay=1)and(iCode=HC_GETNEXT))thenbegin

ifbDelaythenbegin

bDelay:=False;

Result:=50;

pEventMSG(lParam)^:=EventArr[PlayLog];

elseif((canPlay=1)and(iCode=HC_SKIP))thenbegin

bDelay:=True;

PlayLog:=PlayLog+1;

ifPlayLog>=EventLogthenbegin

UNHookWindowsHookEx(hPlay);

functionHookProc(iCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall;

recOK:=1;

ifiCode<0then

Result:=CallNextHookEx(hHook,iCode,wParam,lParam)

recOK:=0

recOK:=1

elseif((recOK>0)and(iCode=HC_ACTION))thenbegin

EventArr[EventLog]:=pEventMSG(lParam)^;

EventLog:=EventLog+1;

ifEventLog>=1000thenbegin

UnHookWindowsHookEx(hHook);

procedureTForm1.FormCreate(Sender:TObject);

Button1.Caption:='纪录';

Button2.Caption:='停止';

Button3.Caption:='回放';

Button4.Caption:='范例';

Button2.Enabled:=False;

Button3.Enabled:=False;

EventLog:=0;//建立键盘鼠标操作消息纪录链

hHook:=SetwindowsHookEx(WH_JOURNALRECORD,HookProc,HInstance,0);

Button2.Enabled:=True;

Button1.Enabled:=False;

procedureTForm1.Button2Click(Sender:TObject);

hHook:=0;

Button1.Enabled:=True;

Button3.Enabled:=True;

procedureTForm1.Button3Click(Sender:TObject);

PlayLog:=0;//建立键盘鼠标操作消息纪录回放链

hPlay:=SetwindowsHookEx(WH_JOURNALPLAYBACK,PlayProc,

HInstance,0);

在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&H60,(&H50or&H80)'把扫描码&H50与数据&H80进行或运算,可以把它的高位置1,得到断码,表示释放这个键

好了,现在的问题就是在delphi中如何向端口写入数据了。因为在windows中,普通应用程序是无权操作端口的,于是我们就需要一个驱动程序来帮助我们实现。在这里我们可以使用一个组件WINIO来完成读写端口操作。

下载该组件,解压缩后可以看到几个文件夹,其中Release文件夹下的3个文件就是我们需要的,这3个文件是WinIo.sys(用于winxp下的驱动程序),WINIO.VXD(用于win98下的驱动程序),WinIo.dll(封装函数的动态链接库),我们只需要调用WinIo.dll中的函数,然后WinIo.dll就会安装并调用驱动程序来完成相应的功能。

值得一提的是这个组件完全是绿色的,无需安装,你只需要把这3个文件复制到与你的程序相同的文件夹下就可以使用了。

用法很简单:

1.先用里面的InitializeWinIo函数安装驱动程序,

2.然后就可以用GetPortVal来读取端口或者用SetPortVal来写入端口了。

3.最后必须在中止应用函数之前或者不再需要WinIO库时调用ShutdownWinIo函数在内存中清除WinIO库。

好,让我们来做一个驱动级的键盘模拟吧。先把winio的3个文件拷贝到你的程序的文件夹下。

下面给出使用WINIO模拟按键的单元和使用方法:(注意是32位和64位的区别)

{****************************************************************************}

unitMNwinio;

KBC_KEY_CMD=$64;//键盘命令端口

KBC_KEY_DATA=$60;//键盘数据端口

functionInitializeWinIo:Boolean;stdcall;external'WinIo.dll'name'InitializeWinIo';

本函数初始化WioIO函数库。

必须在调用所有其它功能函数之前调用本函数。

如果函数调用成功,返回值为非零值。

如果调用失败,则返回值为0。

procedureTForm1.FormActivate(Sender:TObject);//通常在程序启动时调用

ifInitializeWinIo=Falsethen

Messagebox(handle,'初始化失败!','提示',MB_OK+MB_IconError)

functionInstallWinIoDriver(pszWinIoDriverPath:PString;IsDemandLoaded:boolean

=false):Boolean;stdcall;external'WinIo.dll'name'InstallWinIoDriver';

functionRemoveWinIoDriver:Boolean;stdcall;external'WinIo.dll'name

'RemoveWinIoDriver';

functionGetPortVal(PortAddr:Word;PortVal:PDWord;bSize:Byte):Boolean;

stdcall;external'WinIo.dll'name'GetPortVal';

functionSetPortVal(PortAddr:Word;PortVal:DWord;bSize:Byte):Boolean;

stdcall;external'WinIo.dll'name'SetPortVal';

functionGetPhysLong(PhysAddr:PByte;PhysVal:PDWord):Boolean;stdcall;

external'WinIo.dll'name'GetPhysLong';

functionSetPhysLong(PhysAddr:PByte;PhysVal:DWord):Boolean;stdcall;external

'WinIo.dll'name'SetPhysLong';

functionMapPhysToLin(PhysAddr:PByte;PhysSize:DWord;PhysMemHandle:PHandle):

PByte;stdcall;external'WinIo.dll'name'MapPhysToLin';

functionUnMapPhysicalMemory(PhysMemHandle:THandle;LinAddr:PByte):Boolean;

stdcall;external'WinIo.dll'name'UnmapPhysicalMemory';

procedureShutdownWinIo;stdcall;external'WinIo.dll'name'ShutdownWinIo';

{本函数在内存中清除WinIO库

本函数必须在中止应用函数之前或者不再需要WinIO库时调用

procedureTForm1.FormClose(Sender:TObject;varAction:TCloseAction);//通常在程序关闭时调用

ShutdownWinIo;

{**********以上为WINIO.dll中API函数的调用***************}

procedureKBCWait4IBE;//等待键盘缓冲区为空

dwVal:DWord;

repeat

GetPortVal($64,@dwVal,1);

{这句表示从&H64端口读取一个字节并把读出的数据放到变量dwVal中.GetPortVal函数的用法是GetPortVal(端口号,存放读出数据的变量地址,读入的长度}

until(dwValand$2)=0;

procedureMyKeyDown(vKeyCoad:Integer);//这个用来模拟按下键,参数vKeyCoad传入按键的虚拟码

btScancode:DWord;

btScancode:=MapVirtualKey(vKeyCoad,0);

KBCWait4IBE;//发送数据前应该先等待键盘缓冲区为空

SetPortVal($64,$D2,1);//发送键盘写入命令

{SetPortVal函数用于向端口写入数据,它的用法是:SetPortVal(端口号,欲写入的数据,写入数据的长度)}

KBCWait4IBE;

SetPortVal($60,btScancode,1);//写入按键信息,按下键

procedureMyKeyUp(vKeyCoad:Integer);//这个用来模拟释放键,参数vKeyCoad传入按键的虚拟码

THE END
1.同声传译app哪个好用?免费的同声传译软件下载HiTranslate最新版最大的优势就是它可以帮助您翻译您手机上的英文app,一键将那些没有适配中文的app翻译成中文,基于ai的人工智能翻译引擎,让您的翻译更像人工翻译!hitranslate官方简介嗨翻译--最受欢迎的语言翻译器,用于文本翻译和想象翻译。语言自由翻译器,口 http://www.danji100.com/k/tongshengchuanyiapp/
2.多语言翻译工具类软件大全下载点击下载 Hi翻译软件 23.14M / 2024-12-08 / v1.5.8 Hi翻译是一款多语种翻译软件,支持全球多种语言之间的互译,为用户提供快速、准确的翻译服务。软件基于先进的机器翻译技术和庞大的语料库,能够快速地将输入的文字或语音转化为目标语言的译文,并支持多种输入方式和多种输出格式,满足用户多样化的翻译需求。软件支持http://www.dianwannan.com/zt/dyyfygjlrj/
3.英译汉百度翻译器下载手机版2024最新免费安装文件下载 多功能语音翻译应用,多个常用场景的双语例句,翻译准确度高。 特色卖点 多语种文本翻译、拍照翻译、离线翻译、对话翻译、海量权威词库、实用口语、实物翻译、菜单翻译、免费词库 厂商联系方式 官方微博:@百度翻译 https://weibo.com/baidutranslate https://www.liqucn.com/app-rn-120732-12
4.同声传译下载2024安卓最新版手机app官方版免费安装下载普通下载 安全下载 需下载豌豆荚APP 简介 评论(0) 历史版本 小编点评 实时对话翻译 应用介绍 同声翻译软件,采用神经元人工智能AI引擎,将音频流实时翻译为不同语种的文本,采用流式传输接口,多分片并行请求,实时翻译和转化,可返回高准确率、高流畅度的翻译结果,广泛应用于国际论坛、智能会议、智慧教育、跨国交流https://m.wandoujia.com/mip/apps/8311050
5.TranslationDemojava源码下载平台根据TranslationDemo 的第一个安卓练手项目,翻译软件,我们的目标是实现一个简单的翻译功能。我们将使用百度翻译API和有道智云API来实现这一功能。首先,我们需要在项目中添加两个库:Retrofit和Glide。通过Retrofit,我们可以方便地调用百度翻译API;通过Glide,我们可以https://java.code.coder100.com/index/index/content/id/64215
6.英汉互译翻译软件哪个好?英汉互译app下载安装下载 慧译手机版是一款针对听力障的人进行辅助沟通并实时字幕翻译软件,它能在有网的条件下将听到的声音转换成文字,并且也能将文字转换成声音,它能提供实时来电字幕翻译功能。真正做 hi translate高级汉化版 5.32M / 2019-05-08 / v1.3.5 安卓去广告版 http://www.downyi.com/key/yhhyrj
7.iPhoneHD腾讯翻译君下载苹果iPhoneHD腾讯翻译君4.2.5免费苹果iPhone HD下载排行 中关村在线HD 有1841人推荐 可可英语 有人推荐 知米背单词 有人推荐 腾讯翻译君 有人推荐 苹果iPhone HD腾讯翻译君简介 极简匠心,免费无广告。大王卡免流系应用。支持英/日/韩/西/俄/法/德/泰/越/印尼/马来/意/葡/土等多种热门语言翻译,支持语音翻译、同声传译、文本翻译、拍照翻译、https://sj.zol.com.cn/apple/hd/detail_177446.shtml
8.higress本文主要围绕着higress-core组件的源码,研究higress-core自启动以来做了哪些事情。higress-core的代码主要位于pkg目录下。 higress-core源码整体分析? 启动入口:pkg/cmd/server.go? 在该文件下,主要进行了命令参数的解析,并调用NewServer来创建一个Server实例,调用该实例的Start方法来启动实例Server,调用waitForMonhttps://higress.cn/zh-cn/blog/higress-code/
9.屏幕翻译app下载大全手机屏幕实时翻译软件下载Screen Translation屏幕翻译App v3.1.0 系统工具319.18M更新时间:2024-07-28 直接下载 hi dictionary翻译 v2.2.9.4 办公学习39.26M更新时间:2023-05-31 直接下载 DB翻译官方版 v2.0.2 办公学习54.58M更新时间:2024-12-03 直接下载 谷歌翻译app v8.22.46.698958551.3 https://www.32r.com/zt/pmssfyapp/
10.第二章翻译的标准.pptx第二章翻译的标准 上传人:c*** IP属地:贵州上传时间:2023-05-24格式:PPTX页数:51大小:1.06MB积分:19版权申诉 已阅读5页,还剩46页未读,继续免费阅读 版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领https://m.renrendoc.com/paper/267409935.html
11.TranslationPalmermp3下载无损下载高速下载 1B 臻品全景声 下载 高速下载 1B HIRES Hi-Res 下载 高速下载 1B FLAC 无损flac 百度云网盘下载 高速下载 1B APE 无损ape 格式下载 高速下载 1B 320MP3 极高mp3 歌曲下载 高速下载 1B 128MP3 标准mp3 百度网盘下载 高速下载 1B 192OGG 较高ogg 下载 高速下载 1B 192AAC 较高aachttps://www.91flac.com/song/3890965
12.GB/T275352011猪流感HI抗体检测方法国家本标准规定了猪流感病毒血凝(HA)和血凝抑制(HI)试验的技术要求。 本标准适用于猪流感血清HI抗体的检测。 食品产业链内部商城,满足食品行业需求。 标准翻译(汉语与英语、日语、韩语互译):详询0535-2129195 标准英文版:http://www.trans1.cn/translation/show.php?itemid=11350 http://down.foodmate.net/standard/sort/3/33710.html
13.海思Hi3559(八)——简单Demo测试hi3559av100上运行linux+liteos双系统架构, 按照文档《Hi3559A╱CV100SDK安装及升级使用说明.pdf》上的操作,在完成编译烧写,然后启动后,开始执行5.4运行MPP业务 lite就可以在各自的目录下面编译出各自的sample_XXX.bin文件。在u-boot中使用tftp将生成的sample_XXX.bin liteos镜像文件烧写到a53端,然后reset运行https://www.pianshen.com/article/32022566162/
14.中国认证翻译:您需要中国认证翻译的21种情景HICOM*Mixed Marriage certificate certified translation 中法结婚证: 在许多西方国家,中法结婚申请相当于民事婚姻,但它完全是行政性质的,没有仪式。通常只需要不到一个小时,有时只需15分钟即可完成办理。 首先,需要在去民政局之前取得表格并填写完整。 在某些城市,“国际婚姻”将有一个特殊的流程,需要提交含有以下内容的https://www.hicom-asia.cn/?p=6761
15.英语电影字幕翻译(精选十篇)另一例是雪宝首次出现时跟大家打招呼:“Hi, Im Olaf and I love warm hugs.”电影的译者将这句话译为:“嘿,我是喜欢热情熊抱的雪宝。”翻译目的论认为,译文必须与其自身内部和原文保持连贯,据此,我们可以采用顺应策略,翻译成:“嘿,我是雪宝,我喜欢温暖的拥抱。” 2.电影字幕汉译的跨文化交际原则 霍尔兹·https://www.360wenmi.com/f/cnkey995g05g.html
16.基于Python3以及Ffmpeg配合GoogleTranslation(谷歌翻译)为你的本次使用基于Python3的AutoSub库对实时语音进行识别,然后再通过GoogleTranslation的在线API接口对语音识别后的内容进行翻译,这样就可以得到一份双语字幕(逐字稿),这里的双语不只针对国语+英语组合,也可以包含其他国家,包括小语种地区,非常方便。 首先需要安装ffmpeg,这个软件在之前有过介绍:Python3利用ffmpeg针对视频进行https://cloud.tencent.com/developer/news/970168
17.如何对基因组序列进行注释同源注释:从Phytozome上下载了7个植物的基因组蛋白序列(Arabidopsis thaliana, Carica papaya, Glycine max, G. raimondii, Populus trichocarpa, Theobroma cacao and Vitis vinifera), 使用TblastN将蛋白序列比对到组装序列上,E-value的阈值为1e-5. 将不同蛋白的BLAST的hits用Solar软件进行合并。GeneWise根据每个BLASThttp://www.360doc.com/content/21/1203/22/77380921_1007048095.shtml
18.HiminiI think that most of the maid outfits on this website are good, but if someone was looking for cute and cheap I would recommend this one to someone. It is very cute and simple and it's overall a good costume for the price. 2View Translation显示更多https://www.yesstyle.com/zh_CN/himini-%E5%A5%B3%E4%BB%86%E6%9C%8D%E8%A3%85/info.html/pid.1053075060
19.andItsTranslationofCultureandItsRetranslationintoSource Translation Culture .-.-ACase ofTheWomanWarriorandItsChinese Study Translation By Fei Tianqi Underthe of Supervision AssociateProfessorXuGuoxin SubmittedinPartialFulfillmentofthe Requirements MasterofArts forthe of Degree Department English Schoolof ForeignLanguages Universityhttps://max.book118.com/html/2017/1206/143020874.shtm
20.CSDN博客CSDN是全球知名中文IT技术交流平台,创建于1999年,包含原创博客、精品问答、职业培训、技术论坛、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区.https://hi.csdn.net/
21.ApitoolforlocalofflinetexttranslationsupportingApi tool for local offline text translation supporting multiple languages/支持多语言的本地离线文字翻译第一次启动需要下载模型,后续即可离线运行 如果你想使用原生LibreTranslate项目或想部署在docker,请访问https=韩语 ru=俄语 es=西班牙语 th=泰国语 it=意大利语 pt=葡萄牙语 ar=阿拉伯语 tr=土耳其语 hihttps://github.com/jianchang512/ott
22.的翻译是:Hi.sweety.hayeiunchyettranslationChinese中文hi.sweety.haye iunchyet translation of Chinese 翻译结果2复制译文编辑译文朗读译文返回顶部 Hi . sweety.haye iunchyet translation Chinese; 翻译结果3复制译文编辑译文朗读译文返回顶部 Hi.sweety.haye iunchyet translation Chinese 翻译结果4复制译文编辑译文朗读译文返回顶部 haye Hi . sweety iunchyet translation http://eyu.zaixian-fanyi.com/fan_yi_7726915