窗口函数

来源:互联网 发布:软件项目重点难点分析 编辑:程序博客网 时间:2024/06/07 08:07

一、基本概念

什么是「窗口」:

「窗口」就是屏幕上的一个矩形区域,它接收使用者的输入并以文字或图形的格式显示输出内容。
对话框、滚动条、列表框、文本框、按钮、菜单都是窗口,桌面上的图标也是窗口。桌面也是窗口。
窗口也可以隐藏,所以还会有看不到的窗口。
什么是「窗体」
窗体指桌面上的独立窗口(桌面窗口的子窗口),也称为「顶层窗口」或「独立窗口」,作为其他子窗口的容器。对话框是一个窗体。
什么是「控件」
控件指窗体上的组件,控件是一个子窗口。文本框、按钮都是控件
什么是「焦点控件」
在一个窗体上,拥有输入焦点的控件称为焦点控件。
fap模拟程序在录制「模拟区块」时,将焦点控件作为「目标控件」。
什么是「窗口类名」
「窗口类名」是系统用来表示一组窗体属性的分类标识。 例如普通通对话框的类名是“#32770”,标准文本框的类名是“Edit”,标准按钮的类名是“Button”
什么是「控件ID」
「控件ID」是一个独立窗体用来唯一标识子窗口的数字编号,通常在一个独立的窗口上没有重复的控件ID。
窗体自身没有ID。
什么是「句柄」
句柄(handle)是一个整数。它标识一种系统资源,如窗口、位图等等,如果你要操作一种系统资源,必须先获得句柄。
什么是「窗口句柄」
窗口句柄(hwnd)是一个整数编号。用来标识一个窗口。如果你要控制窗口,必须先获得窗口句柄

二、查找窗口句柄

hwnd = win.find(class,title)
找独立窗体,返回找到窗体的句柄.
class参数为字符串格式指定窗体类,
title参数为字符串格式指定窗体标题(必须是完整标题或者不指定标题)

类名与标题可以忽略其中的一个,如果为空值或为nil值则匹配所有,

我们来看一个例子,使用一些浏览器在论坛上发贴子,有时候点一下设置字体颜色等对话框会突然浏览器卡死掉,写了好一会的贴子,放弃实在可惜。隐藏的死锁的对话框有时候按ALT可以切换到前台,有时候按ALT没有用,这时候我们就要用到模拟精灵了。
打开IE调出同样的取色对话框,用模拟精灵录制一个模拟区块发现类名是Internet Explorer_TridentDlgFrame,然后我们运行下面的LAScript脚本就可以激活卡死的浏览器了。
--查找取色对话框
hwnd = win.find("Internet Explorer_TridentDlgFrame","");

--关了你
win.close(hwnd);

如果不知道这些隐藏的死锁的对话框的类名,可以使用win库提供的枚举窗口函数搜索窗口。


hwnd = win.findX(parent,after,class,title)
在指定的窗口内查找子窗口,仅检查子窗口,而不检查更深层的子窗口的子窗口。
parent指定父窗体,如果为零表示桌面窗体。
after指定前面的窗体。0表示忽略这个参数第第一个子窗口开始查找。
class参数为字符串格式指定窗体类,
title参数为字符串格式指定窗体标题(必须是完整标题或者不指定标题)

类名与标题可以忽略其中的一个,如果为空值或为nil值则匹配所有,

hwnd = win.FindXX( hwndParent ,"类名","标题包含的字符串",ID)
在指定窗体中查找控件窗口。以所有子窗口作为查找范围(检查更深层的子窗口的子窗口)

hwndParent指定父窗体,查找范围是主窗体下面所有层的子窗口。而且查找速度非常快。
不要将hwndParent指定为0或nil。

类名与标题可以忽略其中的一个,如果为空值或为nil值则匹配所有,
标题可以是标题的任何部份(不需要完整的标题)。

最后一个参数为数字值指定控件ID。

三、枚举桌面窗体

win.findX只能忽略标题或者匹配完整的标题。可是标题很多时候都是动态的。
而win.findXX虽然可以指定标题的部份子串,可是并不适合用来查找顶层的独立窗体。

所以我们需要枚举桌面的全部窗体,然后分析窗体属性。
我们用泛型for循环来遍历所有桌面窗体。请看下面的例子。

--[[
首先我们自已来实现一个迭代器
参数cls指窗体类名,可以省略(表示忽略类名查找所有窗体),
返回值是一个迭代器函数。
--]]

function win.gfind(cls)
     --泛型for开始循环以前调用这个函数创建一个闭包
     local after = _NULL -- 在闭包中保存值
     local cls = cls;
     local title = title local iter = function ()-- 迭代函数
          after = win.findX(_NULL,after,cls,"")
          if(after==_NULL)then
               return nil;
          else
               return after,win.getString(after);
          end
     end
     return iter; --返回一个迭代函数给泛型for;
end

--下面是使用win.gfind的一个示例,查找指定类名的窗体

win.consoleOpen();

for hwnd,title in win.gfind("#32770") do
     print(hwnd,title); --可以看到列出的窗口句柄及标题
     --在这里你可以使用string库的函数分析标题
     print("进程ID",win.PID(hwnd) );
     --找到窗体以看用break中断循环
end;

四、根据文件名、进程ID查找窗体。

strng.gfind枚举窗体的方法仍有不足,因为大多时候我们要查找的只是某个程序的窗体。
这就要用到一个新的函数win.fromFile

hwnd = win.fromFile(exefilename,"类名","标题包含的字符串")
hwnd = win.fromPID(pid,"类名","标题包含的字符串")

这两个函数的用法完全相同,不同的是第一个参数:
win.fromFile的第一个参数是应用程序的文件名, win.fromPID的每一个参数是进程ID。

后面两个都是可选参数,"类名"和"标题包含"都是可以省略的。
所有找到的窗体句柄都增加到返回值队列中。返回值可能有多个。

下面的写法都是正确的。

hwnd = win.fromFile("文件名.exe")
hwnd = win.fromFile("文件名.exe","#32770")
hwnd = win.fromFile("文件名.exe","#32770","标题包含"

hwnd = win.fromPID(pid)
hwnd = win.fromPID(pid,"#32770")
hwnd = win.fromPID(pid,"#32770","标题包含")

如果需要全部的返回值的话,最好是把返回值放到table表构造器{}中创建一个table数组。

--读取所有句符合条件的名柄,将所有返回值放在{ }内部生成table数组
thwnd = { win.fromFile("文件名.exe","#32770","标题包含") };

--遍历数组
for i,hwnd in ipairs(thwnd) do
print(hwnd,win.class(hwnd),win.id(hwnd),win.getString(hwnd))
end


--读取所有句符合条件的名柄,将所有返回值放在{ }内部生成table数组
--不同的是这个函数的第一个参数指定进程ID
--通常win.exec运行外部文件成功会返回一个进程ID,
--通过win.tPID可以列出系统所有进程ID及详细信息。

thwnd = { win.fromPID(pid,"#32770","标题包含") };

--遍历数组
for i,hwnd in ipairs(thwnd) do
print(hwnd,win.class(hwnd),win.id(hwnd),win.getString(hwnd))
end;



win.fromFile win.fromPID 返回所有窗体(不包括控件),但是不一定是顶层窗体

hwnd = win.getTop(hwnd)--通过这个函数读取顶层窗体句柄


五、其他获取窗体句柄的函数

hwnd = win.fromPos(x,y)
可查找指定坐标的窗体,
参数x,y指定坐标值.

示例:

x,y = mouse.getPos(); --读取当前鼠标所在坐标
hwnd = win.fromPos(x,y); --从指定的坐标读取窗体句柄
win.setForeground(hwnd); --把找到的窗体激活并移动到最前面

hwnd = win.getParent(hwnd)
返回指定窗体的父窗体.
hwnd参数为窗体句柄.

hwnd = win.getTop(hwnd)
返回指定窗体的最顶层父窗体.
hwnd参数为窗体句柄.

hwnd = win.desktop();
返回顶层桌面窗体句柄

hwnd = win.getFocus(hwnd)
hwnd参数为窗体句柄,返回值为窗体上当前拥用输入焦点的控件句柄.

在「fap模拟程序」中,在每个「模拟区块」后面的「脚本区块」,可以用下面的函数获取前面执行过的「模拟区块」相关句柄。
m = ape:getModule("模拟区块名字");
hwnd = m:getWindow(); --读取「目标窗体」句柄
hwnd = m:getCtrl(); --读取「目标控件」句柄
hwnd = m:getCtrlX(); --读取「指向控件」句柄

六、显示隐藏窗体

win.show(hwnd[,true|false|mode])
参数hwnd指定窗口句柄,参数二设置为true显示,设置为false隐藏,其他可选项如下:

_SW_HIDE = 0
_SW_NORMAL = 1
_SW_SHOWMINIMIZED = 2
_SW_MAXIMIZE = 3
_SW_SHOWNOACTIVATE = 4
_SW_SHOW = 5
_SW_MINIMIZE = 6
_SW_SHOWMINNOACTIVE= 7
_SW_SHOWNA = 8
_SW_RESTORE = 9
_SW_SHOWDEFAULT = 10
_SW_MAX = 11

win.show(hwnd); --显示
win.show(hwnd,true); --显示
win.show(hwnd,false); --隐藏
win.show(hwnd,_SW_HIDE ); --隐藏

mode参数的使用方法与 web.show 函数相同

七、窗体控制命令

win.close(hwnd); --关闭窗口
win.min(hwnd); --最小化窗口
win.max(hwnd); --最大化窗口
win.restore(hwnd); --恢复窗口大

八、读取窗体属性

cls = win.class(hwnd) --读取类名
hid = win.id(hwnd) --读取控件ID,注意只有控件才有ID,普通窗体是没有ID的
str = win.getString(hwnd) --读取控件文本或者窗体标题。
pid = win.PID(hwnd); --读取窗口所在的进程ID
tid = win.threadID(hwnd); --读取指定窗体线程ID
hung = win.isHung(hwnd); --检测窗体是否失去响应,返回bool值。

九、窗口消息函数

win.postMessage(hwnd,msg,wparam,lparam)
--向目标窗口发送消息,参数都是数字值(wparam,lparam参数可以是字符串,将自动转换为指针)

win.sendMessage(hwnd,msg,wparam,lparam)
--向目标窗口发送消息,参数都是数字值(wparam,lparam参数可以是字符串,将自动转换为指针)

hwnd指定窗口句柄,msg指定消息,wparam、lparam指定参数。
使用方法与其他编程语言相同,至于不同消息的具体数值,可查阅windows编程相关资料。

十、前台窗体

hwnd = win.getForeground()
--返回桌面上最前面的窗体句柄。

win.setForeground(hwnd);
--把找到的窗体激活并移动到最前面

win.topMost(hwnd,true)
--设置窗体始终在最前面

win.topMost(hwnd,false)
--取消窗体始终在最前面

十一、窗体大小

x,y,x2,y2 = win.getRect(hwnd);
--hwnd参数指定窗体句柄。
--如果执行失败,返回nil值。
-- 如果执行成功,x,y返回值为左上角坐标,x2,y2为右下角坐标


win.setRect(hwnd,x,y,x2,y2)
--设置窗体位置与大小。
--hwnd为窗体句柄,x,y为左上角坐标, x2,y2为右下角坐标。

十二、控制台窗口

win.consoleOpen()
--打开控制台窗口,您可以使用标准IO库读取用户输入或输出信息,
--也可以直接用print()函数输出信息;


win.consoleClose()
--关闭控制台窗口

不要在调用io.open函数以后调用win.consoleOpen()函数,这会导致io.open打开的文件对象重定向到标准输入输出而无法使用。

 

十三、在窗体上拷贝或粘贴文本

win.paste(hwnd) --向窗体发送粘贴命令
win.copy(hwnd) --向窗体发送拷贝命令

十四、自动发送字符串

用法1、
指定窗口句柄

local hwnd = win.getForeground(); --读取前台窗体句柄。
hwnd = win.getFocus(hwnd); --读取前台窗体的焦点控件句柄。

win.sendString(hwnd,"") --以默认方式发送文本。
win.sendStringX(hwnd,"") --以ANSI方式发送文本。
win.sendStringXX(hwnd,"") --以SEND方式发送文本。
--失败返回nil,成功返回true;

用法2、
不指定窗口句柄,模拟精灵将自动获取前台窗体的焦点控件句柄

win.sendString("发送内容") --以默认方式发送文本。
win.sendStringX("发送内容") --以ANSI方式发送文本。
win.sendStringXX("发送内容") --以SEND方式发送文本。
--失败返回nil,成功返回true;

十五、后台模拟鼠标与按键

win库的鼠标键盘模拟函数与key库、mouse库不同,
不会影响真正的鼠标键盘,也看不到鼠标移动,但是可以让目标窗口收到鼠标键盘命令。
但这种方法对于大部份游戏无效。

这种方法主要用于桌面程序。
如果是网页上的web程序。请使用web窗体可以完美的支持后台操作而无需任何复杂的技术。

要准确的找到窗口句柄!
如果你发现下面的函数对于桌面程序没有起到预期的作用。大多时候只有一个原因。
就是句柄弄错。例如向窗体发送一个应当发向按钮的指令。或者向按钮发送一个应当发向对话框的指令。

1. win.mouseDown() 按下鼠标左键,有两种用法

win.mouseDown(hwnd,x,y) --指定窗口发送
win.mouseDown(x,y) --不指定窗口,由程序查找当前激激活窗口的焦点控件句柄

失败返回nil,成功返回true;

2. win.mouseUp() 释放鼠标左键,有两种用法

win.mouseUp(hwnd,x,y) --指定窗口发送
win.mouseUp(x,y) --不指定窗口,由程序查找当前激激活窗口的焦点控件句柄

失败返回nil,成功返回true;

3. win.keyDown() 发送按键 有两种用法

win.keyDown(hwnd,"键名") --指定窗口发送
win.keyDown("键名") --不指定窗口,由程序查找当前激激活窗口的焦点控件句柄

失败返回nil,成功返回true;

4. win.keyUp() 发送释放键 有两种用法

win.keyUp(hwnd,"键名") --指定窗口发送
win.keyUp("键名") --不指定窗口,由程序查找当前激激活窗口的焦点控件句柄

失败返回nil,成功返回true;

5. win.click() 点击指定按钮,有三种用法

win.click() --无参数,自动查找顶层窗口,自动查找焦点按钮并点击
win.click(hwnd) --指定窗体,自动查找焦点按钮并点击
win.click(hwnd,ID) --指定窗体,指定按钮ID(用模拟精灵的录制功能查查看按钮ID)

十六、显示自定义的对话框

对话框弹出以后当前线程就被阻塞等待用户操作,直到对话框被关闭。

str = win.openDlg("标题","All Files (*.*)|*.*||")
打开文件对话框,返回一组选择的文件路径,第一、第二个参数都可以省略。
允许选择多个文件,每个文件路径增加一个返回值。

str = win.saveDlg("标题","All Files (*.*)|*.*||")
保存文件对话框,第一、第二个参数均可省略

str = win.dirDlg("标题")
选择目录对话框,参数可省略

str = win.inputBox("信息","标题")
两个参数都可以省略,弹出一个输入对话框获取用户输入。
如果用户按取消返回空字符串。

win.messagePrint(str)
str参数指定显示信息,这是一个字符串格式参数.
这个函数的作用是在模拟精灵的浮动工具栏上打印信息.
如果str的第一个字符是@,则会在前面自动添加一个方向变化的箭头,多次调用就会出现一个旋转的指针。

ok = win.messageBoxTest("询问用户的信息","标题"))
返回布尔值表示用户是否按了“确定”按钮。

id = win.messageBox("信息","标题",样式 )
第二第三个参数都可以省略。
可用第三个参数设置对话框样式,第二个参数设置标题。

简单的弹出一个对话题:

win.messageBox("这是一个简单的对话框");
win.messageBox("这是一个简单的对话框","我的标题");

弹出自定义样式的对话框(更多的样式常量请参考《模拟编程WINAPI使用手册》):

_MB_YESNOCANCEL = 0x00000003;
_MB_ICONQUESTION = 0x00000020;
_IDYES = 6;
_IDNO = 7;

id = win.messageBox("这是一个自定义对话框","标题",MB_YESNOCANCEL | MB_ICONQUESTION)

if(id == _IDYES)then
    win.messageBox("你按了[是]")
elseif(id == _IDNO)then
    win.messageBox("你按了[否]")
else
    win.messageBox("你按了[取消]")
end

十七、向窗口发送消息

1、发送消息到指定窗口,不等待返回值
win.postMessage(hwnd,msg,wParam,lParam);
hwnd参数指定窗口句柄,msg参数指定消息ID,wParam,lParam为附加参数。

2、发送消息到指定窗口等待返回值
win.sendMessage(hwnd,msg,wParam,lParam);
hwnd参数指定窗口句柄,msg参数指定消息ID,wParam,lParam为附加参数。

3、发送消息到指定线程,不等待返回值
win.postThreadMessage(tid,msg,wParam,lParam);
tid参数指定线程id,msg参数指定消息ID,wParam,lParam为附加参数。

下面是一个例子,通过发送消息自动关闭显示器。

--声明常量
_WM_SYSCOMMAND = 0x0112
_SC_MONITORPOWER = 0xF170
hwnd = win.fromFile( "Fairy_Ape.exe" ) --获取模拟精灵自身的主窗体句柄
 
 
--自动关闭显示器
win.sendMessage(hwnd,_WM_SYSCOMMAND,_SC_MONITORPOWER,1);
 
--延时5秒
delay(5000);
 
--打开显示器
win.sendMessage(hwnd,_WM_SYSCOMMAND,_SC_MONITORPOWER,-1);
转自http://www.yhhe.net/ape/book/fap/las/ns/win.html