珍藏版WinIo中文说明文档文档格式.docx
《珍藏版WinIo中文说明文档文档格式.docx》由会员分享,可在线阅读,更多相关《珍藏版WinIo中文说明文档文档格式.docx(29页珍藏版)》请在冰点文库上搜索。
bool_stdcallSetPortVal(WORDwPortAddr,DWORDdwPortVal,BYTEbSize);
使用本函数将一个字节/字/双字的数据写入输入或输出接口。
wPortAddr–输入输出口地址
dwPortVal–要写入口的数据
bSize–要写的数据个数,可以是1(BYTE),2(WORD)or4(DWORD).
PBYTE_stdcallMapPhysToLin(PBYTEpbPhysAddr,DWORDdwPhysSize,HANDLE*pPhysicalMemoryHandle)
使用此函数将物理内存的一部分映射到一个32位应用程序的线性地址空间。
下面是一个例子:
PBYTEpbLinAddr;
HANDLEPhysicalMemoryHandle;
pbLinAddr=MapPhysToLin(0xA0000,65536,&
PhysicalMemoryHandle);
该函数将把物理地址范围为0xA0000-0xAFFFF的地址空间映射到与应用程序对应的线性地址空间。
返回值为一个与物理地址0xA0000相关的线性地址。
如果出现错误,则返回值为NULL。
pbPhysAddr–指向物理地址的指针
dwPhysSize–需要映射的字节数
pPhysicalMemoryHandle–变量指针,如果调用成功,负责接收物理内存句柄。
随后本句柄在调用UnmapPhysicalMemory函数时作为其第一个参数。
bool_stdcallUnmapPhysicalMemory(HANDLEPhysicalMemoryHandle,PBYTE
pbLinAddr)
使用本函数解除原先使用MapPhysToLin函数映射的一段线性物理内存区域,该区域被映射到应用程序所属的线性地址空间。
Windows9x应用程序不需要调用此函数。
PhysicalMemoryHandle–物理内存区域所属的句柄,此参数由对MapPhysToLin函数的调用返回。
pbLinAddr–MapPhysToLin函数调用返回的线性地址。
bool_stdcallGetPhysLong(PBYTEpbPhysAddr,PDWORDpdwPhysVal);
从指定的物理地址读取一个双字数据。
pbPhysAddr–指向物理地址的指针。
pdwPhysVal–指向一个双字变量的指针,接收从物理内存中传来的数据。
如果此函数调用成功,返回一个非零值。
如果函数调用失败,则返回一个零值。
bool_stdcallSetPhysLong(PBYTEpbPhysAddr,DWORDdwPhysVal);
将一个双字型数据写入指定的物理地址。
pdwPhysVal–指定待写入物理内存地址出的双字型数据。
VBwinIO模拟键盘外挂2007-01-1201:
05键盘是我们使用计算机的一个很重要的输入设备了,即使在鼠标大行其道的今天,很多程序依然离不开键盘来操作。
但是有时候,一些重复性的,很繁琐的键盘操作总会让人疲惫,于是就有了用程序来代替人们按键的方法,这样可以把很多重复性的键盘操作交给程序来模拟,省了很多精力,按键精灵就是这样的一个软件。
那么我们怎样才能用VB来写一个程序,达到与按键精灵类似的功能呢?
那就让我们来先了解一下windows中响应键盘事件的机制。
当用户按下键盘上的一个键时,键盘内的芯片会检测到这个动作,并把这个信号传送到计算机。
如何区别是哪一个键被按下了呢?
键盘上的所有按键都有一个编码,称作键盘扫描码。
当你按下一个键时,这个键的扫描码就被传给系统。
扫描码是跟具体的硬件相关的,同一个键,在不同键盘上的扫描码有可能不同。
键盘控制器就是将这个扫描码传给计算机,然后交给键盘驱动程序。
键盘驱动程序会完成相关的工作,并把这个扫描码转换为键盘虚拟码。
什么是虚拟码呢?
因为扫描码与硬件相关,不具有通用性,为了统一键盘上所有键的编码,于是就提出了虚拟码概念。
无论什么键盘,同一个按键的虚拟码总是相同的,这样程序就可以识别了。
简单点说,虚拟码就是我们经常可以看到的像VK_A,VK_B这样的常数,比如键A的虚拟码是65,写成16进制就是&
H41,注意,人们经常用16进制来表示虚拟码。
当键盘驱动程序把扫描码转换为虚拟码后,会把这个键盘操作的扫描码和虚拟码还有其它信息一起传递给操作系统。
然后操作系统则会把这些信息封装在一个消息中,并把这个键盘消息插入到消息列队。
最后,要是不出意外的话,这个键盘消息最终会被送到当前的活动窗口那里,活动窗口所在的应用程序接收到这个消息后,就知道键盘上哪个键被按下,也就可以决定该作出什么响应给用户了。
这个过程可以简单的如下表示:
用户按下按键-----键盘驱动程序将此事件传递给操作系统-----操作系统将键盘事件插入消息队列-----键盘消息被发送到当前活动窗口
明白了这个过程,我们就可以编程实现在其中的某个环节来模拟键盘操作了。
在VB中,有多种方法可以实现键盘模拟,我们就介绍几种比较典型的。
1.局部级模拟
从上面的流程可以看出,键盘事件是最终被送到活动窗口,然后才引起目标程序响应的。
那么最直接的模拟方法就是:
直接伪造一个键盘消息发给目标程序。
哈哈,这实在是很简单,windows提供了几个这样的API函数可以实现直接向目标程序发送消息的功能,常用的有SendMessage和PostMessage,它们的区别是PostMessage函数直接把消息仍给目标程序就不管了,而SendMessage把消息发出去后,还要等待目标程序返回些什么东西才好。
这里要注意的是,模拟键盘消息一定要用PostMessage函数才好,用SendMessage是不正确的(因为模拟键盘消息是不需要返回值的,不然目标程序会没反应),切记切记!
PostMessage函数的VB声明如下:
DeclareFunctionPostMessageLib"
user32"
Alias"
PostMessageA"
(ByValhwndAsLong,ByValwMsgAsLong,ByValwParamAsLong,lParamAsAny)AsLong
参数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个字节,写成二进制就是00000000000000000000000000000000一共是32位,我们从右向左数,假设最右边那位为第0位(注意是从0而不是从1开始计数),最左边的就是第31位,那么该参数的的0-15位表示键的发送次数等扩展信息,16-23位为按键的扫描码,24-31位表示是按下键还是释放键。
大家一般习惯写成16进制的,那么就应该是&
H00000000,第0-15位一般为&
H0001,如果是按下键,那么24-31位为&
H00,释放键则为&
HC0,那么16-23位的扫描码怎么会得呢?
这需要用到一个API函数MapVirtualKey,这个函数可以将虚拟码转换为扫描码,或将扫描码转换为虚拟码,还可以把虚拟码转换为对应字符的ASCII码。
它的VB声明如下:
DeclareFunctionMapVirtualKeyLib"
MapVirtualKeyA"
(ByValwCodeAsLong,ByValwMapTypeAsLong)AsLong
参数wCode表示待转换的码,参数wMapType表示从什么转换为什么,如果是虚拟码转扫描码,则wMapType设置为0,如果是虚拟扫描码转虚拟码,则wMapType设置为1,如果是虚拟码转ASCII码,则wMapType设置为2.相信有了这些,我们就可以构造键盘事件的lParam参数了。
下面给出一个构造lParam参数的函数:
FunctionMakeKeyLparam(ByValVirtualKeyAsLong,ByValflagAsLong)AsLong
'
参数VirtualKey表示按键虚拟码,flag表示是按下键还是释放键,用WM_KEYDOWN和WM_KEYUP这两个常数表示
DimsAsString
DimFirstbyteAsString'
lparam参数的24-31位
Ifflag=WM_KEYDOWNThen'
如果是按下键
Firstbyte="
00"
Else
C0"
'
如果是释放键
EndIf
DimScancodeAsLong
获得键的扫描码
Scancode=MapVirtualKey(VirtualKey,0)
DimSecondbyteAsString'
lparam参数的16-23位,即虚拟键扫描码
Secondbyte=Right("
&
Hex(Scancode),2)
s=Firstbyte&
Secondbyte&
"
0001"
0001为lparam参数的0-15位,即发送次数和其它扩展信息
MakeKeyLparam=Val("
&
H"
s)
EndFunction
这个函数像这样调用,比如按下A键,那么lParam=MakeKeyLparam(VK_A,WM_KEYDOWN),很简单吧。
值得注意的是,即使你发送消息时设置了lParam参数的值,但是系统在传递消息时仍然可能会根据当时的情况重新设置该参数,那么目标程序收到的消息中lParam的值可能会和你发送时的有所不同。
所以,如果你很懒的话,还是直接把它设为0吧,对大多数程序不会有影响的,呵呵。
好了,做完以上的事情,现在我们可以向目标程序发送键盘消息了。
首先取得目标程序接受这个消息的控件的句柄,比如目标句柄是12345,那么我们来对目标模拟按下并释放A键,像这样:
(为了简单起见,lParam这个参数就不构造了,直接传0)
PostMessage12345,WM_KEYDOWN,VK_A,0&
按下A键
PostMessage12345,WM_UP,VK_A,0&
释放A键
好了,一次按键就完成了。
现在你可以迫不及待的打开记事本做实验,先用FindWindowEx这类API函数找到记事本程序的句柄,再向它发送键盘消息,期望记事本里能诡异的自动出现字符。
可是你马上就是失望了,咦,怎么一点反应也没有?
你欺骗感情啊~~~~~~~~~~55555555555555不是的哦,接着往下看啊。
一般目标程序都会含有多个控件,并不是每个控件都会对键盘消息作出反应,只有把键盘消息发送给接受它的控件才会得到期望的反应。
那记事本来说,它的编辑框其实是个edit类,只有这个控件才对键盘事件有反应,如果只是把消息发给记事本的窗体,那是没有用的。
现在你找出记事本那个编辑框的句柄,比如是54321,那么写如下代码:
PostMessage54321,WM_KEYDOWN,VK_F1,0&
按下F1键
PostMessage54321,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编码(可以输入中文的哦^_^),现在你可以写出一个完整的向记事本里自动写入字符的程序了,下面是一个例子,并附有这些消息常数的具体值:
PublicConstWM_KEYDOWN=&
H100
PublicConstWM_KEYUP=&
H101
PublicConstWM_CHAR=&
H102
PublicConstVK_A=&
H41
PrivateSubForm_Load()
dimhwndaslong
hwnd=XXXXXX'
XXXXX表示记事本编辑框的句柄
PostMessagehwnd,WM_KEYDOWN,VK_A,MakeKeyLparam(VK_A,WM_KEYDOWN)'
PostMessagehwnd,WM_CHAR,ASC("
A"
),MakeKeyLparam(VK_A,WM_KEYDOWN)'
输入字符A
PostMessagehwnd,WM_UP,VK_A,MakeKeyLparam(VK_A,WM_UP)'
EndSub
这就是通过局部键盘消息来模拟按键。
这个方法有一个极大的好处,就是:
它可以实现后台按键,也就是说他对你的前台操作不会有什么影响。
比如,你可以用这个方法做个程序在游戏中模拟按键来不断地执行某些重复的操作,而你则一边喝茶一边与QQ上的MM们聊得火热,它丝毫不会影响你的前台操作。
无论目标程序是否获得焦点都没有影响,这就是后台模拟按键的原理啦~~~~
2.全局级模拟
你会发现,用上面的方法模拟按键并不是对所有程序都有效的,有的程序啊,你向它发了一大堆消息,可是它却一点反应也没有。
这是怎么回事呢?
这就要看具体的情况了,有些程序(特别是一些游戏)出于某些原因,会禁止用户对它使用模拟按键程序,这个怎么实现呢?
比如可以在程序中检查一下,如果发现自己不是活动窗口,就不接受键盘消息。
或者仔细检查一下收到的键盘消息,你会发现真实的按键和模拟的按键消息总是有一些小差别,从这些小差别上,目标程序就能判断出:
这是假的!
是伪造的!
!
因此,如果用PostMessage发送局部消息模拟按键不成功的话,你可以试一试全局级的键盘消息,看看能不能骗过目标程序。
模拟全局键盘消息常见的可以有以下一些方法:
(1)用API函数keybd_event,这个函数可以用来模拟一个键盘事件,它的VB声明为:
DeclareSubkeybd_eventLib"
(ByValbVkAsByte,ByValbScanAsByte,ByValdwFlagsAsLong,ByValdwExtraInfoAsLong)
参数bVk表示要模拟的按键的虚拟码,bScan表示该按键的扫描码(一般可以传0),dwFlags表示是按下键还是释放键(按下键为0,释放键为2),dwExtraInfo是扩展标志,一般没有用。
比如要模拟按下A键,可以这样:
ConstKEYEVENTF_KEYUP=&
H2
keybd_eventVK_A,0,0,0'
keybd_eventVK_A,0,KEYEVENTF_KEYUP,0'
注意有时候按键的速度不要太快,否则会出问题,可以用API函数Sleep来进行延时,声明如下:
DeclareSubSleepLib"
kernel32"
(ByValdwMillisecondsAsLong)
参数dwMilliseconds表示延时的时间,以毫秒为单位。
那么如果要模拟按下功能键怎么做呢?
比如要按下Ctrl+C实现拷贝这个功能,可以这样:
keybd_eventVK_Ctrl,0,0,0'
按下Ctrl键
keybd_eventVK_C,0,0,0'
按下C键
Sleep500'
延时500毫秒
keybd_eventVK_C,0,KEYEVENTF_KEYUP,0'
释放C键
keybd_eventVK_Ctrl,0,KEYEVENTF_KEYUP,0'
释放Ctrl键
好了,现在你可以试试是不是可以骗过目标程序了,这个函数对大部分的窗口程序都有效,可是仍然有一部分游戏对它产生的键盘事件熟视无睹,这时候,你就要用上bScan这个参数了。
一般的,bScan都传0,但是如果目标程序是一些DirectX游戏,那么你就需要正确使用这个参数传入扫描码,用了它可以产生正确的硬件事件消息,以被游戏识别。
这样的话,就可以写成这样:
keybd_eventVK_A,MapVirtualKey(VK_A,0),0,0'
keybd_eventVK_A,MapVirtualKey(VK_A,0),KEYEVENTF_KEYUP,0'
以上就是用keybd_event函数来模拟键盘事件。
除了这个函数,SendInput函数也可以模拟全局键盘事件。
SendInput可以直接把一条消息插入到消息队列中,算是比较底层的了。
DeclareFunctionSendInputLib"
user32.dll"
(ByValnInputsAsLong,pInputsAsGENERALINPUT,ByValcbSizeAsLong)AsLong
参数:
nlnprts:
定义plnputs指向的结构的数目。
plnputs:
指向INPUT结构数组的指针。
每个结构代表插人到键盘或鼠标输入流中的一个事件。
cbSize:
定义INPUT结构的大小。
若cbSize不是INPUT结构的大小,则函数调用失败。
返回值:
函数返回被成功地插人键盘或鼠标输入流中的事件的数目。
若要获得更多的错误信息,可以调用GetlastError函数。
备注:
Sendlnput函数将INPUT结构中