AutoIt 入门与提高1


AutoIt 入门与提高 crossdoor Page 1 4/24/2012 关于本教程 本教程主要针对那些想学习 AutoIt 但是找不到入门途径的朋友,希望我的这篇拙作能 带领你走进 AutoIt 的大门。 说一下本教程的使用方法: 1、 教程中没有对出现的函数进行详细的解释,因为帮助文档已经解释的很清楚了。遇 到不懂的函数,请参阅帮助文档。 2、 我尽量为每个小节都写一段实例,以代码来说明问题。但是希望看的朋友能细心点, 不要一目十行。这样既表现出你对我辛劳的尊重,也体现了你认真学习的态度。 3、 本教程均由我一人编写完成,其中难免会出现各种漏洞错误,希望这些错误不会给 你带去麻烦。如果你能把发现的错误告知于我,我非常感谢! 如果教程中出现了 BUG 或者你有看不懂的地方,可以给我发邮件,我的邮箱 地址:382869232@qq.com 或者到我的博客提出你的疑问,我的博客:crossdoor.cublog.cn 希望我们能够共同进步! CrossDoor 2010-06-01 目 录 第一章 AutoIt 基础 1、关于 AutoIt 2、变量、常量和数据结构 3、运算符、宏 4、流程控制 4.1、选择语句 4.2、分支语句 4.3、循环语句 5、函数 5.1、自定义函数 5.2、函数的参数传递 5.3、函数的变量作用域 5.4、函数的嵌套与递归 第二章 窗口 AutoIt 入门与提高 crossdoor Page 2 4/24/2012 1、第一个窗口程序 1.1、窗口消息 1.2、消息拦截 2、多窗口程序 2.1、父窗口与子窗口 2.2、GUI 嵌入外部进程窗口 第三章 字符串与变量转换 1、字符串处理 1.1、字符串长度 1.2、字符串截取 1.3、字符串替换 1.4、字符串分割 1.5、正则 2、变量转换 2.1、转换为指针 2.2、转换为句柄 2.3、转换为整数 2.4、转换为二进制数据 第四章 数组 1、一维数组 2、二维及多维数组 2.1、数组的维数 2.2、数组调整 第五章 注册表读写 1、读取注册表 2、写入注册表 第六章 文件读写 1、Ini 配置文件读写 2、Txt 文档读写 3、二进制文件读写 第七章 进程管理 1、进程列表 2、进程等待及结束 3、运行文件 第八章 窗口管理 1、窗口列表 2、窗口等待及结束 3、窗口自动化操作 AutoIt 入门与提高 crossdoor Page 3 4/24/2012 3.1、按键发送 3.2、控件控制 第九章 定时器的应用 1、内置定时器函数 2、API 定时器 第十章 Com 对象调用 1、创建和使用 Com 对象 2、拦截 Com 对象错误 第十一章 动态链接库调用 1、数据结构及 API 调用 第十二章 网络编程 1、Windows Socket 接口简介 2、TCP 应用程序设计 2.1、TCP 聊天服务端 2.2、TCP 聊天客户端 第十三章 ADO 数据库编程 1、ADO 简介及 SQL 语言 2、数据库连接与断开 3、数据库管理 3.1、执行 sql 语句 3.2、获取结果集中的数据 后记 1、一些算法 第一章 AutoIt 基础 1、关于 AutoIt AutoIt v3 官方主页 AutoIt v3 中文论坛 AutoIt v3 是用以编写并生成具有 BASIC 语言风格的脚本程序的免费软件, 它被设计用来在 Windows GUI(用户界面)中进行自动操作. 通过它可以组合使用 模拟键击,鼠标移动和窗口/控件操作等来实现自动化任务, 而这是其它语言所 无法做到或尚无可靠方法实现的 (比如 VBScript 和 SendKeys). AutoIt 非常小 巧,完全运行在所有 windows 操作系统上. (thesnow 注:现在已经不再支持 win 9x, 微软连 XP 都能放弃,何况一个 win 9x 支持), 并且不需要任何运行库. AutoIt 入门与提高 crossdoor Page 4 4/24/2012 AutoIt 最初是为 PC(个人电脑)的"批量处理"而设计, 用于对数千台 PC 进 行(同样的)配置. 现在,autoit 是一个支持复杂表达式, 自定义函数,循环等的 强大脚本软件. AutoIt 可以做的事:  简单易懂的类 BASIC 表达式  模拟键盘,鼠标动作事件  操作窗口与进程  直接与窗口的"标准控件"交互(设置/获取 文字,移动,关闭,等等)  脚本可以编译为标准可执行文件  创建用户图形界面接口(GUI)  COM 支持  正则表达式  直接调用外部 DLL 和 Windows API 函数  程序运行为功能(让程序运行于其它账户)  详细易懂的帮助文件于基于社区的支持论坛  完全兼容于 Windows 2000 / XP / 2003 / Vista / 2008  Unicode 与 64 位 运算支持  高精度,易使用的数学运算  可以运行于 Windows Vista Account Control (UAC) AutoIt 被设计得尽可能小, 并且不用依赖外部 DLL 文件或添加注册表项目 即可独立运行. 也可以安全的成为服务运行. 脚本可以使用 Aut2Exe 编译为可 独立运行的文件 此外我们还设计了 AutoIt 的 ActiveX 和 DLL 版本 —— AutoItX 这是个 组件化的语言(COM 同一 DLL 文件中的标准 DLL 函数). AutoItX 将使得您可以 加入一些 AutoIt 独有的特性到您最常用的脚本语言或程序设计语言中去! PS:本教程将以汉化版 AutoIt 3.3.6.1 为基础(大家可以到 AutoIt v3 中 文论坛下载并安装。) 2、变量、常量和数据结构 AutoIt 中只存在一种数据类型,那就是 Variant,Variant 变量存储任何数据类 型,对它执行各种操作和类型转换。需要注意的是,使用这种弱类型的变量会造 成不好的编程习惯。 Variant 变量的类型检查和计算在运行期间才进行,编译器不会提示代码中的 潜在错误,这些错误在进一步测试中才能发现。与其它的解释性代码一样,AU3 脚本中的许多操作需要直到执行时才能知道,这就是影响脚本代码效率的一大原 因。 AutoIt 入门与提高 crossdoor Page 5 4/24/2012 所谓变量,顾名思义就是一个可以变动的数据。每个变量都有自己的名字, 而且必须以英文字符"$"开头,其中只能包含 字母, 数字 和下划线_字符。下面 是一些有效的变量名:$var1、$my_variable。 AutoIt 中使用关键字 Dim,Local 和 Global 来声明并创建变量:Dim $var1。 也可以一次声明多个变量:Dim $var1,$my_variable。声明变量的同时也可以赋值: Dim $var1=1,$my_variable=”变量 2”。 Dim,Local,Global 这三者的不同之处在于其声明变量的作用域:Dim = 如果 同名的全局变量并不存在则作用域为局部(如果已有同名的全局变量存在则将复 用该变量!)。Global = 将创建的变量的作用域强制转换为全局的。Local = 将创 建的变量的作用域强制转换为 局部/函数 的。 所谓常量,就是一个不可更改值的数据。例如圆周率π =3.1415926,这就是 一个常量,一旦更改了它的值,那它就不是圆周率了。 常量声明使用 Const 关键字,就像:Const $const1 = 1, $const2=12 声明的常量可以用 Enum 关键字进行初始化,就像: Enum $const1 = 1, $const2, $const3 Enum STEP 2 $incr0, $incr2, $incr4 Enum STEP *2 $mult1, $mult2, $mult4 注意:常量不能声明为一个已经存在的变量。 如果把变量比作为只有一个口袋的钱包,那数据结构则可以看成是有很多个 口袋的钱包。 一个数据结构中有多个字段,每个字段中储存一个不同类型的变量值。例如 API 函数 RegisterClass 中要用到一个 WNDCLASS 的结构,这个结构按照 C 语言 的格式定义如下: typedef struct { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS, *pWNDCLASS; 一个定义好的数据结构是没有储存数据的,它就像是一个制作好但是还未使 用的钱包,里面虽然有很多可以放东西的口袋,但却全是空的。 至于数据结构的具体用法,后面的教程中我会参杂在其它的例子中一并介 绍。 AutoIt 入门与提高 crossdoor Page 6 4/24/2012 3、运算符、宏 AutoIt 支持以下这些赋值符号,数学运算符,比较和逻辑运算符。 运算符 详细信息 赋值运算 = 赋值,如 $var = 5 (赋值数字 5 到 $var) += 自增赋值,如 $var += 1 (添加 1 到 $var) -= 自减赋值. *= 自乘赋值. /= 自除赋值. &= 连续赋值. 如 $var = "one", 然后 $var &= 10 ($var 的结果为 "one10") 数学运算 + 使两个数相加. 如 10 + 20 (等于 30) - 使两个数相减.如 20 - 10 (等于 10) * 使两个数相乘.如 20 * 10 (等于 200) / 使两个数相除.如 20 / 10 (等于 2) & 使两个字符串连接起来.比如 "one" & 10 (等于"one10") ^ 提高某个数的幂.比如 2 ^ 4 (2 的 4 次方,等于 16) 比较运算 (大小写敏感的字符串需要使用 == 来比较) = 判断两个值是否相等. 比如 If $var= 5 Then (如果变量 $var 的值为 5 则条件 成立). 用于字符串时不区分大小写 == 判断两个字符串是否相等.左方和右方的值将会转化成字符串,并区分大小写,这个运 算只能用于区分字符串大小写的比较. <> 判断两个值是否不相等. 比较会对字符串大小写敏感. 要比较一个大小写敏感的不 等于操作使用 Not ("string1" == "string2") > 判断第一个值(左边)是否大于第二个值(右边).Strings are compared lexicographically even if the contents of the string happen to be numeric. >= 判断第一个值( 左边) 是否大于或等于第二个值 ( 右边).Strings are compared lexicographically even if the contents of the string happen to be numeric. < 判断第一个值(左边)是否小于第二个值(右边). Strings are compared lexicographically even if the contents of the string happen to be numeric. <= 判断第一个值(左边)是否小于或等于第二个值(右边). Strings are compared lexicographically even if the contents of the string happen to be numeric. AutoIt 入门与提高 crossdoor Page 7 4/24/2012 逻辑运算 AND 逻辑与运算. 如 If $var = 5 AND $var2 > 6 Then (如果变量 $var 的值为 5 而且 变量 $var2 的值大于 6 则条件成立 ) OR 逻辑或运算. 如 If $var = 5 OR $var2 > 6 Then (如果变量 $var 的值为 5 或者变 量 $var2 的值大于 6 则条件成立) NOT 逻辑非运算. 如 NOT 1 (结果为 False) 当一个表达式内含有多个运算符时, 其结合的先后顺序由运算符的优先级别 来控制。AutoIt 中运算符的优先级如下所示:( 处于同一优先级的两种运算符将 按 从左到右的顺序结合,越上面的运算符则优先级越高) NOT ^ * / + - & < > <= >= = <> == AND OR AutoIt 提供了一组宏,它们具备了常量属性,可以在代码中把它们当成字符 串引用,但是不可对它们进行赋值。 下面列出的是 AutoIt 所有的宏: 宏 详细信息 @AppDataCommonDir 公共 Application Data 文件夹所在路径 @AppDataDir 当前用户 Application Data 文件夹所在路径 @AutoItExe 当前脚本的完整路径. 已经编译的文件返回 EXE 文件所在完 整路径. @AutoItPID 当前运行脚本的进程 PID. @AutoItVersion AutoIt 版本号,如 3.2.3.12 @AutoItX64 Returns 1 if the script is running under the native x64 version of AutoIt. @COM_EventObj Object the COM event is being fired on. Only valid in a COM event Function. @CommonFilesDir Common Files 文件夹路径 @Compiled 脚本已经编译,返回 1.未编译,返回 0. @ComputerName 当前计算机的名称. @ComSpec %comspec%的值, 指定的第二个命令解释程序; 主要用于命令行使用, 如. Run(@ComSpec & " /k help | more") AutoIt 入门与提高 crossdoor Page 8 4/24/2012 @CPUArch Returns "X86" when the CPU is a 32-bit CPU and "X64" when the CPU is 64-bit. @CR 回车符, Chr(13); 用于换行. @CRLF @CR 和 @LF ;用于换行. @DesktopCommonDir 公共 Desktop 文件夹路径(桌面) @DesktopDir 当前用户 Desktop 文件夹路径(桌面) @DesktopHeight 桌面高度(像素) (垂直分辨率) @DesktopWidth 桌面宽度(像素) (水平分辨率) @DesktopDepth 像素颜色位深度(如 32 Bit). @DesktopRefresh 屏幕刷新率.(如 75 HZ) @DocumentsCommonDir 公共 Documents 文件夹路径(我的文档) @error 错误标识. 参见 SetError 函数. @exitCode 退出代码 @exitMethod 退出方法. 参见 OnAutoItExitRegister() 函数. @extended 扩展的函数返回值,使用于一些特定函数.如: StringReplace. @FavoritesCommonDir 公共 Favorites 文件夹路径 @FavoritesDir 当前用户的 Favorites 文件夹路径 @GUI_CtrlId 最后点击的控件标识(Control ID). 只是使用 event 函数时有 效. 请参考 GUICtrlSetOnEvent 函数. @GUI_CtrlHandle 最后点击的控件句柄(Control handle). 只是使用 event 函数 时有效. 请参考 GUICtrlSetOnEvent 函数. @GUI_DragId 拖动控件标识(Control ID). 只是使用 event 函数时有效. 请 参考 GUICtrlSetOnEvent 函数. @GUI_DragFile 拖动文件(到控件)的文件名. 只是使用 event 函数时有效. 请参考 GUICtrlSetOnEvent 函数. @GUI_DropId (拖动后)放下控件标识(Control ID). 只是使用 event 函数时 有效. 请参考 GUICtrlSetOnEvent 函数. @GUI_WinHandle 最后点击的 GUI 窗口句柄(GUI window handle). 只是使用 event 函数时有效. 请参考 GUICtrlSetOnEvent 函数. @HomeDrive 当前用户主目录所在的驱动器号.(主要用于确定系统所在分 区) @HomePath 当前用户主目录所在位置.(不包含盘符),如须得到完整路径, 请使用 @HomeDrive , @HomePath. @HomeShare 服务器和共享名称,包含当前用户主目录. AutoIt 入门与提高 crossdoor Page 9 4/24/2012 @HOUR 当前时钟的时值(24 时制),值的范围是 00 ~ 23 @HotKeyPressed 最后按下的热键. 参考 HotKeySet 函数. @IPAddress1 第一个网络适配器的 IP 地址.在某些电脑上可能会返回 127.0.0.1 @IPAddress2 第二个网络适配器的 IP 地址.若不存在则返回 0.0.0.0 @IPAddress3 第三个网络适配器的 IP 地址.若不存在则返回 0.0.0.0 @IPAddress4 第四个网络适配器的 IP 地址.若不存在则返回 0.0.0.0 @KBLayout 返回当前键盘布局的 代号。 @LF 换行, Chr(10); 代表用户行中断,进入下一行. @LogonDNSDomain 登录 DNS 域. @LogonDomain 登录域. @LogonServer 登录服务器. @MDAY 当前是一月中的第几天. (01 到 31) @MIN 当前的分钟数(00 到 59) @MON 当前月份(01 到 12) @MSEC 当前时钟毫秒值.范围为(00 到 999) @MUILang Returns code denoting Multi Language if available (Vista is OK by default). @MyDocumentsDir 我的文档的路径. @NumParams 调用用户函数的参数数量. @OSArch Returns one of the following: "X86", "IA64", "X64" - this is the architecture type of the currently running operating system. @OSBuild 返回操作系统的内部标号(build 号),如:Windows 2003 Server 返回的是 3790 @OSLang 返回表示操作系统语言的代号。 @OSServicePack 系统已安装的 Service pack 信息,比如"Service Pack 3" @OSType Returns "WIN32_NT" for NT/2000/XP/2003/Vista/2008/Win7/2008R2. @OSVersion Returns one of the following: "WIN_2008R2", "WIN_7", "WIN_2008", "WIN_VISTA", "WIN_2003", "WIN_XP", "WIN_XPe", "WIN_2000". @ProgramFilesDir 返回 Program Files 文件夹路径. @ProgramsCommonDir 「开始」菜单\程序目录所在路径(例:C:\Documents and Settings\All Users\「开始」菜单\程序)公共用户 AutoIt 入门与提高 crossdoor Page 10 4/24/2012 @ProgramsDir 「开始」菜单\程序 目录所在路径(例:C:\Documents and Settings\All Users\「开始」菜单\程序) 当前用户 @ScriptDir 脚本所在目录. (不包含反斜杠符号"\") @ScriptFullPath 等价于 @ScriptDir & "\" & @ScriptName @ScriptLineNumber 当前执行的脚本行号. 在调试循环语句是非常有用. (已经编 译的脚本中没意义) @ScriptName 当前运行的脚本的长文件名. @SEC 当前时钟的秒值,值域为 00 ~ 59 @StartMenuCommonDir 公共用户「开始」菜单 目录所在路径(例:C:\Documents and Settings\All Users\「开始」菜单) @StartMenuDir 当前用户的 「开始」菜单目录所在路径 @StartupCommonDir 公共用户的 启动 目录所在路径(例: C:\Documents and Settings\All Users\「开始」菜单\程序\启动) @StartupDir 当前用户的 启动 目录所在路径 @SW_DISABLE 屏蔽(禁用)指定窗口 @SW_ENABLE 恢复指定窗口(使其重新可用). @SW_HIDE 隐藏指定窗口并激活其它窗口. @SW_LOCK 锁定窗口,避免被重画. @SW_MAXIMIZE 最大化指定窗口. @SW_MINIMIZE 最小化指定窗口并激活下一个在 Z 轴(垂直屏幕)方向上的顶 层窗口. @SW_RESTORE 激活并显示指定窗口,如果该窗口已最小化或最大化则以其 原始大小和位置还原.一般来说,应用程序在还原一个最小化 窗口时应该应用此标志. @SW_SHOW 激活指定窗口并使其以当前大小和位置信息显示. @SW_SHOWDEFAULT 设置显示状态(SW_值),程序在启动应用程序时需指定该值. @SW_SHOWMAXIMIZED 激活并最大化指定窗口. @SW_SHOWMINIMIZED 激活并最小化指定窗口. @SW_SHOWMINNOACTIVE 最小化显示指定窗口.与 @SW_SHOWMINIMIZED 不同之 处在于该窗口将不被激活. @SW_SHOWNA 令指定窗口根据其当前大小和位置信息显示 . 与 @SW_SHOW 不同之处在于该窗口将不被激活. @SW_SHOWNOACTIVATE 令指定窗口以其上一次的大小和位置显示 . 与 @SW_SHOWNORMAL 不同之处在于该窗口将不被激活. @SW_SHOWNORMAL 激活并显示指定窗口,如果该窗口已最小化或最大化则以其 AutoIt 入门与提高 crossdoor Page 11 4/24/2012 原始大小和位置还原.一般来说,应用程序在首次显示窗口时 应该应用此标志. @SW_UNLOCK 取消锁定窗口,允许窗口被重画. @SystemDir Windows 下的 System (或 System32)文件夹所在路径(例: C:\WINDOWS\system32) @TAB Tab 字符, Chr(9) @TempDir 临时文件夹路径 @TRAY_ID 最 后 点 击 的 项 目 标 识 (item identifier), 用于 TraySet(Item)OnEvent 函数. @TrayIconFlashing 如果托盘图标为闪烁状态,返回 1; 反之,返回 0. @TrayIconVisible 如果托盘图标为可见状态,返回 1; 反之,返回 0. @UserProfileDir 返回当前用户的 Profile 文件夹路径. @UserName 当前登录的用户的名称. @WDAY 指示当天属该周的第几天,值域为 1 ~ 7,依次表示星期天到 星期六. @WindowsDir Windows 文件夹 所在路径,(例: C:\WINDOWS) @WorkingDir 当前/激活的工作目录(不包括结尾的反斜杠符号) @YDAY 指示当天属该年的第几天,值域为 001 ~ 366(若不是闰年则 为 001 ~ 365) @YEAR 当前年份(4 位数) 4、流程控制 流程控制是程序中很重要的部分。AutoIt 作为一种顺序执行的脚本,如果没 有了流程控制,那结果将让我不敢想象。 流程控制主要用于对某项条件进行判断,然后选择执行代码。详细说明请看 下文分析。 4.1、选择语句 当脚本代码执行到某一行,出现了分歧条件时,就需要使用选择语句来判断 程序接下来该如何执行了。 这就像是你开车走到了十字路口,于是接下来的路程你将面临多种选择,比 如说回家直走、上班左转、买菜右转,此时你就需要根据自身接下来要做的事情 判断下面的路要怎么走了。 选择语句的作用也正是如此,根据条件判断并选择要执行的动作。 AutoIt 中的选择语句有三种,分别为: AutoIt 入门与提高 crossdoor Page 12 4/24/2012  单一条件选择语句:If...Then 示例代码:(定义变量$a 的值为 1,然后使用单一条件选择语句判断,如果变量$a 的值 为 1 就退出脚本。) Dim $a = 1 If $a = 1 Then Exit  双条件选择语句:If...Else...EndIf 示例代码:(定义变量$a 的值为 2,然后使用双条件选择语句判断,如果变量$a 的值为 1 就退出脚本,否则就弹出消息框提示变量$a 的值。) Dim $a = 2 If $a = 1 Then Exit Else MsgBox(0, "变量$a 的值", "变量$a = " & $a) EndIf  条件选择语句:If...ElseIf...Else...EndIf 示例代码:(定义变量$a 的值为 2,然后使用条件选择语句判断,如果变量$a 的值为 1 就退出脚本,否则当$a 为 0 的时弹出一个空消息框,否则就弹出消息框提示变量$a 的值。) Dim $a = 2 If $a = 1 Then Exit ElseIf $a = 0 Then MsgBox(0, "", "") Else MsgBox(0, "变量$a 的值", "变量$a = " & $a) EndIf 注意:条件选择语句中的 ElseIf 可以接无数条。 4.2、分支语句 当脚本中出现的条件多达十几二十条,甚至更多时,如果继续使用条件选择 语句 If...ElseIf...Else...EndIf 来判断脚本接下来的流程,显然需要书写大段代码, 而且也不便于理解与今后的代码维护。 此时我们就可以选择使用分支语句来判断脚本接下来的流程了。AutoIt 中的 分支语句有两种,具体区别大家看示例: 第一种:Select...Case...EndSelect 示例代码: Dim $a = 2, $b = 1 Select Case $a = 1 AutoIt 入门与提高 crossdoor Page 13 4/24/2012 MsgBox(0, "", "变量$a = 1") Case $b = 2 MsgBox(0, "", "变量$b = 2") Case Else MsgBox(0, "", "变量$a 既不等于 1 也不等于 2") EndSelect 第二种:Switch...Case...EndSwitch 示例代码: Dim $a = 2 Switch $a Case 1 MsgBox(0, "", "变量$a = 1") Case 2 MsgBox(0, "", "变量$a = 2") Case Else MsgBox(0, "", "变量$a 既不等于 1 也不等于 2") EndSwitch 从上面的两个示例可以看出,Select 可以同时判断多个条件,而 Switch 则只 能一次判断一个条件。使用效果基本是相同的,大家可以在编写代码时自行选择。 4.3、循环语句 循环就是重复的执行一部分代码。根据重复执行的次数,可以分为有限循环 和无限循环。 其中有限循环为:For...To...Step...Next 示例代码 1:( Step 代表步进值,默认为 1) Dim $i For $i = 0 To 5 Step 1 MsgBox(0, "变量$i 的值", "变量$i = " & $i) Next MsgBox(0, "$i 最后的值", "$i = " & $i) 代码说明:变量$i 从 0 开始步进,每次步进的值为 1,步进到 5 时退出循环,并提示$i 的值。 示例代码 2: Dim $i For $i = 5 To 0 Step -1 MsgBox(0, "变量$i 的值", "变量$i = " & $i) Next MsgBox(0, "$i 最后的值", "$i = " & $i) 代码说明:变量$i 从 5 开始步进,每次步进的值为-1,步进到 0 时退出循环,并提示$i AutoIt 入门与提高 crossdoor Page 14 4/24/2012 的值。 当不知道需要执行多少次循环时,我们就可以选择使用无限循环。无限循环 有两种。 第一种:While...Wend 示例代码: Dim $i = 0 While $i < 100 $i += 1 Wend MsgBox(0, "$i 最后的值", "$i = " & $i) 代码说明:变量$i 初始定义值为 0,当它的值小于 100 时就一直执行循环。循环中的代 码只有一行$i += 1,意思为变量$i 自加 1。当变量$i 自加到大于等于 100 时退出循环,并提 示$i 的值。 从上面的例子可以看出 While 循环在条件成立时,将会一直执行循环体内的代码。也 就是说 While 循环是先判断循环条件,再执行循环代码。 第二种无限循环结构 Do...Until 则刚好于此相反,它是先执行一次循环代码,然后再 判断循环条件。示例代码: Dim $i = 0 Do $i += 1 Until $i < 100 MsgBox(0, "$i 最后的值", "$i = " & $i) 代码说明:变量$i 初始定义值为 0,当它的值小于 100 时就退出循环。因为变量$i 自加 了 1 之后才判断循环的条件,所以当退出循环时,提示$i 的值应该是 1。 5、函数 AutoIt 内置了超过 200 个函数,而且还有大量的 UDF 函数(UDF = User Defined Function,即用户自定义函数),除了这些函数之外,我们还可以把脚本中需要在多 处重复使用的代码写成自定义函数。 5.1、自定义函数 使用关键字:Func...Return...EndFunc,可以声明一个用户自定义函数。 示例代码:(写一个求矩形周长的函数,函数的两个参数分别为长和宽。) MsgBox(0, "长 3 宽 2 的矩形周长", _Zhouchang(3, 2)) Func _Zhouchang($Chang, $Kuan) Dim $Zhouchang ;定义一个局部变量,用来表示周长 $Zhouchang = ($Chang + $Kuan) * 2 Return $Zhouchang ;关键字 Return 返回周长 AutoIt 入门与提高 crossdoor Page 15 4/24/2012 EndFunc PS:分号 ; 后面的文字表示单行注释 5.2、函数的参数传递 AutoIt 中函数的参数传递有两种方式,一种是传值,一种是传址。 所谓传值,顾名思义就是传递数值。像上面举例写的那个求矩形周长的函数, 就是使用的传值方式。这种方式比较常用,理解也比较简单,所以就不多做解释。 我们来详细说明一下传址。从字面上可以看出,传址就是传递地址,也就是 把变量在内存中的地址传递到函数中,然后函数直接读取此地址上的数据,或者 是改写此地址上的数据。我们依旧使用求周长来举例说明: Dim $Zhouchang ;周长变量我们在函数外部定义 _Zhouchang(3, 2, $Zhouchang) MsgBox(0, "长 3 宽 2 的矩形周长",$Zhouchang) ;第三个参数前我们加上关键字 byref,这个关键字的作用就是表示后面的变量是传址 Func _Zhouchang($Chang, $Kuan, byref $Z) $Z = ($Chang + $Kuan) * 2 EndFunc ;函数没有返回值,因为周长直接写到了变量$Z 中,此函数中变量$Z 在内存的地址 与变量$Zhouchang 是一样的,所以数据存在$Z 就等于存到了$Zhouchang 5.3、函数的变量作用域 函数中使用到的变量,作用范围可分为两种。一种是局部变量,一种是全局 变量。 所谓局部变量,指的是只在函数内部起作用,不会影响到函数外部,即使在 函数外部存在另一个同名变量。 全局变量则不同,它的作用范围是整个程序。也就是说,不管变量在哪里被 修改了值,它影响的都是整个程序,而不单单局限于函数内部。 我们举个例子来说明一下: Global $QuanJu = 100 ;定义一个全局变量$QuanJu 等于 100 MsgBox(0,"","函数_A 的局部变量值为:" & _A() & @LF & "函数_B 的局部变量值为:" & _B() & @LF & "全局变量$QuanJu 的值为:" & $QuanJu) ;使用&符号串联字符串,宏@LF 表示换行 Func _A() Dim $JuBu $JuBu += 10;给局部变量赋值为自加 10 Return $JuBu;返回局部变量的值 EndFunc AutoIt 入门与提高 crossdoor Page 16 4/24/2012 Func _B() Dim $JuBu $JuBu = 2 ^ 4;给局部变量赋值为 2 的四次方 $QuanJu -= $JuBu ;全局变量自减函数_B 中的局部变量 Return $JuBu;返回局部变量的值 EndFunc 上面的实例中,两个自定义函数_A 和_B 里面都有一个名为$JuBu 的局部变 量,但是两个函数分别对它的赋值是不同的,从弹出的消息框可以看到结果,虽 然两个局部变量名称一样,但是值是不一样的。 而全局变量$QuanJu 在函数_B 中被改变了值,从消息框可以看出,它最后的 值就是被改变后的值。 因此,我们可以看出,局部变量的作用范围就是在函数内部,而全局变量则 作用于整个程序。 5.4、函数的嵌套与递归 函数的嵌套,从字面上就可以理解,就是在一个函数中调用另一个函数。在 上面说明变量作用范围的例子中,我就使用了函数的嵌套。 在内置函数 MsgBox 中,分别使用了自定义函数_A 和_B。所以这里我就不 再举例说明了,我们着重看一下函数的递归。 什么是函数的递归?其实递归也是在函数中调用函数,不过与嵌套的区别在 于,嵌套是调用其它函数,而递归则是调用自身。 也就是说,AutoIt 的自定义函数可以自己调用自己!需要注意的是,AutoIt 最大支持的递归深度为 5100 级,超过将会报错。也就是说,函数调用自身的次 数应该在 5100 次以内。 我们来看一个函数递归的示例: MsgBox(0, "递归阶乘", _Dg(5)) Func _Dg($n) If $n = 1 Then Return 1 Else Return $n * _Dg($n - 1) EndIf EndFunc 上面是一个递归求阶乘的例子,如果看的有点迷糊,那就暂时丢下好了,等以后熟练了 再来看。 AutoIt 入门与提高 crossdoor Page 17 4/24/2012 第二章 窗口 作为一个多任务操作系统,Windows 可以同时运行多个程序。有些程序在后 台运行,不需要像使用者显示信息,例如系统服务程序;有些程序则需要接受用 户的输入信息,并处理后输出,例如计算器。 为了接收用户输入的信息,以及向用户输出处理的结果,程序就必须建立一 个窗口来完成此项任务。 窗口有一个重要的属性,就是句柄。为了区别不同的窗口,从而达到结果输 出不会混淆的目的,Windows 系统给每个窗口都分配了一个唯一的句柄。通过这 个句柄我们可以操作自己建立的窗口,也可以操作其它程序建立的窗口,也就是 AutoIt 擅长的自动化操作(这里不讲自动化操作)。 这一章我们来学习如何使用 AutoIt 建立窗口。 AutoIt 入门与提高 crossdoor Page 18 4/24/2012 1、第一个窗口程序 创建一个空白的窗口:(把下面的代码复制到 SCITE 编辑器中,然后保存文件。遇到不懂的 函数可以把光标移动函数中间,然后按 F1 获取帮助。) Global Const $GUI_EVENT_CLOSE = -3;窗口关闭消息的值 GUICreate("我的第一个窗口") ; 创建一个居中显示的 GUI 窗口 GUISetState(@SW_SHOW) ; 显示一个空白的窗口 While 1 $msg = GUIGetMsg();捕获窗口消息 If $msg = $GUI_EVENT_CLOSE Then ExitLoop;使用关键字 ExitLoop 跳出 While 循环 WEnd GUIDelete();删除窗口界面 注意:ExitLoop 关键字是用来跳出循环的。While、Do、For 循环都可以使用它来跳出。当多层循环嵌 套时,它除了能跳出最近一层的循环外,还可以跳出外层的任意一层循环,后面接一个数字,表示第几层 循环(后面没跟数字时,表示使用默认值 1,跳出最靠近它的循环)。 一个完整的窗口,还应该包括一些控件,比如输入框、按钮之类的。与窗口 一样,这些控件也有句柄,我们称为控件句柄,对控件的操作则通过这些句柄来 进行。 因为窗口程序时通过事件驱动,例如按下最小化按钮、关闭按钮等。用户对 窗口进行的任何一个操作,都会产生一个消息。Windows 系统根据不同的消息, 来响应不同的操作。 程序运行期间会不断的产生消息,为了不错过这些消息我们有两种方式来处 理。一种是不断的循环消息,已达到不错过的目的;还有一种是使用 Event 模式, 当产生事件时就进行响应。 1.1、窗口消息 首先我们来看一下消息循环模式: Global Const $GUI_EVENT_CLOSE = -3;窗口关闭消息的值 GUICreate("我的第一个窗口") ; 创建一个居中显示的 GUI 窗口 $Input = GUICtrlCreateInput("1111", 10, 35, 300, 20) $btn = GUICtrlCreateButton("读取输入框", 40, 75, 90, 20) GUISetState(@SW_SHOW) ; 显示一个空白的窗口 While 1 ;死循环,直到捕获窗口的 退出消息才跳出 $msg = GUIGetMsg();捕获窗口消息 Select;使用分支判断窗口消息 Case $msg = $GUI_EVENT_CLOSE;退出消息 AutoIt 入门与提高 crossdoor Page 19 4/24/2012 ExitLoop Case $msg = $btn;按钮被点击 $D = GUICtrlRead($Input);读取输入框数据 MsgBox(0, "输入框的数据", $D) EndSelect WEnd GUIDelete();删除窗口界面 从上例可以看出,消息循环模式时,脚本处于一个死循环中,并且在循环里 不断的捕获窗口上产生的消息,然后再根据消息来进行操作。 这种模式是一直在被动的不断获取窗口的消息,在系统资源的耗费上相当的 不划算。而且因为脚本的主循环需要不断的处理消息,使得我们想要在主循环中 执行其它代码很不方便。因为这样很容易造成消息无法得到及时的响应。 接着来看一下 Event 模式: Global Const $GUI_EVENT_CLOSE = -3;窗口关闭消息的值 Opt("GUIOnEventMode", 1) ;开启 Event 模式 GUICreate("我的第一个窗口") ; 创建一个居中显示的 GUI 窗口 GUISetOnEvent($GUI_EVENT_CLOSE, "main");注册关闭消息到自定义函数 main 里面进行处理 $Input = GUICtrlCreateInput("1111", 10, 35, 300, 20) $btn = GUICtrlCreateButton("读取输入框", 40, 75, 90, 20) GUICtrlSetOnEvent($btn, "main");注册按钮点击消息到自定义函数 main 里面进行处理 GUISetState(@SW_SHOW) ; 显示一个空白的窗口 While 1 ;死循环,保证脚本不会退出 GUISetBkColor(RandomColor());修改窗口背景颜色 Sleep(3000) ;睡眠 3 秒 WEnd AutoIt 入门与提高 crossdoor Page 20 4/24/2012 Func main() Switch @GUI_CtrlId;根据宏@GUI_CtrlId 来判断消息 Case $GUI_EVENT_CLOSE Exit Case $btn $D = GUICtrlRead($Input);读取输入框数据 MsgBox(0, "输入框的数据", $D) EndSwitch EndFunc Func RandomColor() Return "0x" & Hex(Random(0, 255, 1), 2) & Hex(Random(0, 255, 1), 2) & Hex(Random(0, 255, 1), 2) EndFunc ;产生一个随机的 RGB 颜色值 从上例可以看出,Event 模式时,当窗口产生消息时,才会对消息进行响应, 而不会一直处于消息检测状态。这样一来,相较于消息循环模式将会节省大量的 CPU。 而且在主循环中,我们还可以做其它的事情,而不用担心会造成消息的延迟。 (如果在消息循环模式下,主循环也睡眠 3 秒的话,那窗口的消息将会被延迟甚 至不响应。) 所以我推荐大家在处理窗口消息时,要尽量使用 Event 模式。 1.2、消息拦截 AutoIt 提供了一个内置函数 GUIRegisterMsg,可以让我们为已知 Windows 消 息代码(WM_MSG)注册一个自定义的函数来进行操作。 具体的使用我们来看实例: Global Const $GUI_EVENT_CLOSE = -3;窗口关闭消息的值 Global Const $WM_ENTERSIZEMOVE = 0x0231;窗口移动消息的值 Global Const $WM_EXITSIZEMOVE = 0x0232;窗口结束移动消息的值 Opt("GUIOnEventMode", 1) ;开启 Event 模式 $Gui = GUICreate("我的第一个窗口") ; 创建一个居中显示的 GUI 窗口 GUISetOnEvent($GUI_EVENT_CLOSE, "main");注册关闭消息到自定义函数 main 里面进行处理 $Input = GUICtrlCreateInput("1111", 10, 35, 300, 20) $btn = GUICtrlCreateButton("读取输入框", 40, 75, 90, 20) GUICtrlSetOnEvent($btn, "main");注册按钮点击消息到自定义函数 main 里面进行处理 GUISetState(@SW_SHOW) ; 显示一个空白的窗口 GUIRegisterMsg($WM_ENTERSIZEMOVE, "WM_ENTERSIZEMOVE");产生窗口移动消息时,执行 自定义函数 WM_ENTERSIZEMOVE GUIRegisterMsg($WM_EXITSIZEMOVE, "WM_EXITSIZEMOVE");窗口移动结束时,执行自定义函 数 WM_EXITSIZEMOVE AutoIt 入门与提高 crossdoor Page 21 4/24/2012 While 1 ;死循环,保证脚本不会退出 GUISetBkColor(RandomColor());修改窗口背景颜色 Sleep(3000) ;睡眠 3 秒 WEnd Func main() Switch @GUI_CtrlId;根据宏@GUI_CtrlId 来判断消息 Case $GUI_EVENT_CLOSE Exit Case $btn $D = GUICtrlRead($Input);读取输入框数据 MsgBox(0, "输入框的数据", $D) EndSwitch EndFunc Func RandomColor() Return "0x" & Hex(Random(0, 255, 1), 2) & Hex(Random(0, 255, 1), 2) & Hex(Random(0, 255, 1), 2) EndFunc ;产生一个随机的 RGB 颜色值 Func WM_ENTERSIZEMOVE($hWndGUI, $MsgID, $WParam, $LParam) WinSetTrans($Gui, "", 130) EndFunc ;窗口移动时,设置窗口透明值为 130 Func WM_EXITSIZEMOVE($hWndGUI, $MsgID, $WParam, $LParam) WinSetTrans($Gui, "", 255) EndFunc ; 窗口结束移动时,设置窗口透明值为 255,也就是不透明 上面的代码运行后,当窗口移动时,你会发现窗口变成半透明的了,这是因 为脚本把窗口移动的消息拦截下来了,在发生移动的时候,脚本把窗口设置成了 半透明。除此以外,我们还可以拦截其它的消息,比如列表控件的双击、单击消 息,组合列表框控件的点击、选择消息等。 对于自己编写的程序的窗口,我们可以使用此种方法来拦截消息。但是对于 外部程序的窗口则不能这样做,若非要拦截外部程序窗口的消息,唯一的方法就 是注入到目标程序的进程中去。此处不讨论这些,略过。 2、多窗口程序 在实际的应用中,程序可能需要用到多窗口来实现,典型的例子就是 QQ, 除了主界面之外,还有聊天窗口、资料设置窗口、好友查找窗口等。 窗口与窗口之间的关系除了平等之外,还有父子窗口。也就是以其中一个窗 口为主窗口,其余窗口均为主窗口的下属窗口。平等关系的我们就不说了,因为 跟父子窗口差不多,所以我们就说一下父子窗口好了。 AutoIt 入门与提高 crossdoor Page 22 4/24/2012 2.1、父窗口与子窗口 AutoIt 内置的窗口创建函数 GUICreate 有八个参数,通过查看帮助,我们 可以看到,最后一个参数可以为新建的窗口指定父窗口的句柄,这样一来新窗口 就将成为一个子窗口。 如果窗口需要指定样式,比如去掉关闭按钮、去掉最小化按钮等等,也可以 在使用 GUICreate 创建时一并设置。GUICreate 函数的第六和第七两个参数 就是设置窗口样式的,至于具体的样式我们可以查看帮助。如果要使用默认样式 可以设置为-1。 GUICreate 函数创建窗口成功后将会返回一个真正的句柄。为什么要这么 说呢?这是因为 AutoIt 在创建控件时,返回的并不是真正的控件句柄,而是控 件标识,所谓标识就是一个整数。 也就是说,如果你在窗口上创建了十个按钮,那么这十个按钮返回的并非是 按钮控件的句柄,而是一到十的十个整数。(这里只是举例,实际应用中可能并 非刚好是一到十。) 要取得控件的真正句柄,我们需要用到 GUICtrlGetHandle 这个函数,此 函数可以根据控件标识返回控件句柄。 以上这些在此只是顺带说明,不做具体的实例说明。本小节我们还是以父窗 口和子窗口为主题。 关于父窗口与子窗口的应用我们来看一段实例: Global Const $GUI_EVENT_CLOSE = -3;窗口关闭消息的值 Dim $Child_Gui;定义一个变量用于存放子窗口的句柄 Opt("GUIOnEventMode", 1) ;开启 Event 模式 $Gui = GUICreate("我的第一个窗口") ; 创建一个居中显示的 GUI 窗口 GUISetOnEvent($GUI_EVENT_CLOSE, "main");注册关闭消息到自定义函数 main 里面进行处理 $Input = GUICtrlCreateInput("1111", 10, 35, 300, 20) $btn = GUICtrlCreateButton("读取输入框", 40, 75, 90, 20) GUICtrlSetOnEvent($btn, "main") $Show_btn = GUICtrlCreateButton("显示子窗口", 40, 100, 90, 20) GUICtrlSetOnEvent($Show_btn, "main") GUISetState(@SW_SHOW) ; 显示一个空白的窗口 While 1 ;死循环,保证脚本不会退出 GUISetBkColor(RandomColor());修改窗口背景颜色 Sleep(1000) AutoIt 入门与提高 crossdoor Page 23 4/24/2012 WEnd Func main() Switch @GUI_CtrlId;根据宏@GUI_CtrlId 来判断消息 Case $GUI_EVENT_CLOSE Switch @GUI_WinHandle;根据宏@GUI_WinHandle 来判断产生关闭消息的窗口消息 Case $GUI Exit Case $Child_Gui GUIDelete($Child_Gui) EndSwitch Case $btn $D = GUICtrlRead($Input);读取输入框数据 MsgBox(0, "输入框的数据", $D) Case $Show_btn Child_Gui () EndSwitch EndFunc Func RandomColor() Return "0x" & Hex(Random(0, 255, 1), 2) & Hex(Random(0, 255, 1), 2) & Hex(Random(0, 255, 1), 2) EndFunc ;产生一个随机的 RGB 颜色值 Func Child_Gui () $Child_Gui = GUICreate("我是子窗口", 200, 40, -1, -1, -1, -1, $Gui) GUISetOnEvent($GUI_EVENT_CLOSE, "main") GUISetState(@SW_SHOW) EndFunc ;子窗口 细心的朋友肯定会发现,在自定义函数中我们嵌套使用了两个分支判断语句 Switch。扩展一下,我们还可以在分支判断语句里面使用循环语句 While,而 且循环语句内还可以再使用循环语句,具体的使用,大家可以自己写代码熟悉一 下。 在上面的代码中,当子窗口显示后,主窗口的背景色将保持不变,转而是子 窗口的背景色被不断修改。造成这个现象的原因在于 GUISetBkColor 函数,查看 帮助可以看到这个函数有两个参数,其中第二个参数是需要操作的窗口的句柄, 这个参数是可以省略的。 在省略状况下,函数将会使用最近一次使用过的窗口句柄。当子窗口出现时, 最近一次使用的窗口句柄变成了子窗口的,所以背景颜色被改变的窗口就从父窗 口变成了子窗口。 AutoIt 入门与提高 crossdoor Page 24 4/24/2012 2.2、GUI 嵌入外部进程窗口 除了上节中说明的父子窗口,还有一种特殊的父子窗口。玩过网游的朋友肯 定会发现,游戏界面内部的窗口,无论如何都不能被移除游戏界面之外。就像是 一个窗口被镶嵌在另一个窗口内部,无法取出一样。 AutoIt 可以使用 API 来达成这种效果,此处我们只演示一下这种效果,不对 API 的调用做详细的解释,等到后文会有具体的说明章节。 我们来看实例代码:(窗口一个窗口,然后嵌入到记事本中) Run("notepad.exe") ;运行记事本 WinWait("无标题 - 记事本") ;等待记事本窗口出现 $Gui = GuiCreate("被装进了记事本", 240, 120) GuiSetState() DllCall("user32.dll", "int", "SetParent", "hwnd", $Gui, "hwnd",WinGetHandle("无 标题 - 记事本")) ;使用 API 把脚步建立的窗口嵌入记事本窗口中 Do ;Do 循环,当窗口消息等于退出消息,或者记事本窗口消失时,就退出循环 Until GuiGetMsg() =-3 Or Not WinExists("无标题 - 记事本") 上面的代码只是创建了一个空白的窗口嵌入到记事本窗口中,实际的运用还 要看具体的目的。比如说在全屏游戏时,嵌入一个窗口来显示当前时间,这样就 可以不用切换到桌面来查看时间。 第三章 字符串与变量转换 在程序中,字符串的处理与变量之间的转换占了很重要的一席之地。学习好 处理字符串与转换变量,将使你写起程序来更得心应手。 1、字符串处理 与大多数编程语言一样,AutoIt 中定义一个字符串也是使用双引号括起来。 但是除此之外,AutoIt 还支持使用单引号来表示字符串变量。 AutoIt 入门与提高 crossdoor Page 25 4/24/2012 例如,下面两种字符串变量的赋值是等价的: Dim $var = '中国人' Dim $var = "中国人" 虽然上面两种方式是等价的,但是推荐大家在大多数情况下使用双引号。 如果我们需要在字符串中包含引号,要怎么办? 因为 AutoIt 支持单双引号定义字符串,于是当我们需要包含一种引号在字 符串内时,我们可以使用另一种引号来定义,看代码: Dim $var1 = '"中国人' ;用单引号包含双引号 Dim $var2 = "'中国人" ;用双引号包含单引号 MsgBox(0, "", $var1 & @LF & $var2) ;宏@LF 用于换行,具体说明请查看宏列表 那如果一个字符串中,两种引号都需要出现,怎么办?此时我们可以使用字 符串连接符&来实现,具体看代码: Dim $var = '"中国人' & "'中国人" MsgBox(0, "", $var) 1.1、字符串长度 获取字符串长度,AutoIt 有内置的函数可用。StringLen 函数可以获取字符 串的长度,但是需要注意的是,这个函数是将一个字符当做一长度,这个字符不 管是英文字符还是中文字符都被当成长度为一。而在字符的编码中,中文字符的 字节数比英文的要长。这里我们只说明一下,不做深入了解。 下面我们看一下如何获取字符串长度: Dim $var = "中国人 Chinese" $Len = StringLen ($var) MsgBox(0, "字符串长度", $Len) 运行代码后,我们可以从消息框中看到,字符串的长度为 10,其中包括三个 中文字符和七个英文字符。 从上面的代码中大家应了解到 StringLen 函数获取的长度是字符串的字符 数,而非字节数。AutoIt 没有一个内置函数 SizeOf 来返回字符串的字节数,所 以若要取字符串的字节数则需要我们自己写函数来完成此项任务。 1.2、字符串截取 一个字符串,在实际的应用中,有时我们只需要用到左边几个字符或右边几 个字符,当然也可能是中间的一段字符。此时我们就需要截取字符串。 AutoIt 用于这方面的函数有以下几个: StringLeft(返回字符串中从左开始指定数量的字符) Dim $var ="中国的国庆是十一" MsgBox(0, "", StringLeft ($var, 3)) ;返回字符串的左边三个字符 StringRight(返回字符串中从右开始指定数量的字符) AutoIt 入门与提高 crossdoor Page 26 4/24/2012 Dim $var = "中国的国庆是十一" MsgBox(0, "", StringRight ($var, 3)) ;返回字符串的右边三个字符 StringMid(取字符串的部分字符) Dim $var = "中国的国庆是十一" MsgBox(0, "", StringMid ($var, 3, 5)) ;从字符串第三个字符开始取 5 个字符 StringTrimLeft(删除字符串中从左开始指定数量的字符) Dim $var = "中国的国庆是十一" MsgBox(0, "", StringTrimLeft ($var, 3)) ;删除字符串左边的三个字符 StringTrimRight(删除字符串中从左右始指定数量的字符) Dim $var = "中国的国庆是十一" MsgBox(0, "", StringTrimRight($var, 3)) ;删除字符串右边的三个字符 1.3、字符串替换 在实际应用过程中,有时候我们会需要把字符串中的部分字符替换成其它 的,这个时候我们就需要用到 StringReplace 函数。 看实例说明:(把国庆是十一替换成劳动节是五一) Dim $var = "中国的国庆是十一" MsgBox(0, "", StringReplace ($var, "国庆是十一", "劳动节是五一")) 上面的代码会把字符串中符合要求的字符全部替换,可能上例对这点的体现 不明显,我们换个例子来说明: Dim $var = "abc 中国的国庆是十一 abc" MsgBox(0, "", StringReplace ($var, "abc", "123"));abc 替换为 123 如果我们只想替换一次,那要怎么办?通过查看帮助,我们可以看到 StringReplace 函数一共有五个参数,其中后两个参数可以省略。第四个参数 可以帮助我们实现目的,指定需要替换的次数,看实例: Dim $var = "abc 中国的国庆是十一 abc" MsgBox(0, "", StringReplace ($var, "abc", "123", 1)) StringReplace 函数的第五个参数是用于区分大小写的,在默认的情况下, 字符串的替换时不区分大小写的。具体的应用我就不再举例说明,大家可以自己 写代码练习使用这个参数。 1.4、字符串分割 实际的应用中,有时候我们会需要把字符串拆分为几段,然后再进行具体的 操作。比如说一段文章,我们希望把它的每一句都独立提取出来,这时候我们就 可以以句话来进行拆分。 AutoIt 中用于字符串拆分的函数是 StringSplit,从帮助中我们可以知道, AutoIt 入门与提高 crossdoor Page 27 4/24/2012 字符串拆分后,将会产生一个数组,用于存放拆分后的字符串。数组的第一个元 素存放的是拆分后字符串的数量,从第二个元素开始才是拆分后的字符串。 需要注意的是,不过字符串是否拆分成功,结果都将是一个数组。拆分失败 时,数组的第一个元素将是 1,第二个元素里面存放的是整个字符串。 我们来看一个简单的实例: Dim $var = "abc 中国的国庆是十一 abc" $d = StringSplit($var, "b");用字符 b 来拆分字符串 MsgBox(0, "", "子串数量:" & $d[0] & @LF & "第一个子串:" & $d[1]) $d = StringSplit($var, "e");用字符 e 来拆分字符串,因为不存在 e,所以拆分将失败 MsgBox(0, "", "子串数量:" & $d[0] & @LF & "第一个子串:" & $d[1]) 有时候在表驱动中,使用拆分的字符串也会有意想不到的效果。我用一段帮 助文档中的代码来作为实例,至于具体的分析就不做了,等大家以后再自己琢磨 吧: $attrib = FileGetAttrib(@ScriptDir) ;获取脚本目录的文件夹属性 If @error Then MsgBox(4096,"错误", "无法获得属性.") Exit EndIf $input = StringSplit("R,A,S,H,N,D,O,C,T",",") $output = StringSplit("只读 /, 存档 /, 系统 /, 隐藏 /, 普通 /, 目录 /, 脱机文件 /, 压缩 /, 临时 /", ",") For $i = 1 to 9 $attrib = StringReplace($attrib, $input[$i], $output[$i], 0, 1) Next $attrib = StringTrimRight($attrib, 2) ;移除末尾的反斜杠 MsgBox(0,"完整的文件属性:", $attrib) 1.5、正则 正则表达式通常用来验证字符串是否符合某个语法规则,很多情况下使用正 则来判断或截获符合规则的字符串,将会是脚本更加简捷。 正则最初是在 Unix 系统中出现的,后来被很多语言所引用,AutoIt 同样也 支持正则。要学好正则,建议大家到网上查找一些针对性的教程,我在这里只演 示一下正则的作用。 通常情况下,正则除了用来判断字符串是否符合某个规则之外,还可以用来 截获符合某个规则的字符串,另外还有个很重要的就是替换。 首先我们来看一下字符串的验证,正则验证字符串使用函数 StringRegExp: Dim $ip = "127.0.0.1" AutoIt 入门与提高 crossdoor Page 28 4/24/2012 StringRegExp($ip,"^((25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(25[0-5]|2[0-4]\d|[01]?\ d?\d)$") If Not @error Then MsgBox(0," ", "IP 地址格式正确。") Else MsgBox(0," ", "IP 地址格式错误。") EndIf Dim $id = "abc124" StringRegExp($id,"^[\w]{6,15}+$") ;判断一个字符串是否只使用了数字和字母及下划 线,且长度在 6-15 位。一般网上注册账号就是这样判断账号是否符合要求的 If Not @error Then MsgBox(0," ", "账号格式正确。") Else MsgBox(0," ", "账号格式错误。") EndIf 接下来我们看看如何用正则来截获字符串:(从一段字符中提取除 IP 地址) Dim $ip = "abc127.0.0.1aaa" $a = StringRegExp($ip,"((25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(25[0-5]|2[0-4]\d|[01]?\d ?\d)", 2) If Not @error Then MsgBox(0," ", "字符串中的 IP 地址:" & $a[0]) Else MsgBox(0," ", "字符串中没有 IP 地址。") EndIf 除了验证字符串是否符合规则以及从字符串中提取符合规则的字串,我们也 可以使用正则来替换字符串中符合规则的字串,要完成这项任务,我们需要用到 函数 StringRegExpReplace,我们来看替换的实例: Dim $ip = "abc127.0.0.1aaa" $a = StringRegExpReplace ($ip,"\d", "中") ;把字符串中的数字全部替换成中字 If Not @error Then MsgBox(0," ", "替换了数字后的字符串:" & $a) Else MsgBox(0," ", "字符串中没有数字可以替换") EndIf Dim $ip = "abc127.0.0.1aaa" $a = StringRegExpReplace ($ip,"\d", "中", 2);把字符串中的前 2 个数字替换成中字 If Not @error Then MsgBox(0," ", "替换了前 2 个数字后的字符串:" & $a) AutoIt 入门与提高 crossdoor Page 29 4/24/2012 Else MsgBox(0," ", "字符串中没有数字可以替换") EndIf 上例中的第二个替换,只替换了字符串中的前两个数字,这是因为使用了函 数 StringRegExpReplace 的第四个参数来制定需要替换的子串数量,通常情 况下如果省略这个参数,将会替换所有符合要求的字串。 从上面的几个例子可以看出,使用正则来判断字符串,关键在于正则表达式, 所以如果对这部分内容感兴趣的朋友,可以在网上找一份专门针对正则的教程来 学习。在此我就不再深入,因为我的正则也是半桶水,而且这也不属于 AutoIt 的范畴了。 2、变量转换 因为 AutoIt 的变量是 Variant 类型,这就可能导致脚本执行的过程中会出现 一些奇怪的问题。比如一个窗口的句柄我们可以直接当成字符串来使用,但是一 个符合窗口句柄格式的字符串,我们如果当成当口句柄来使用的话就不行了。 因此为了保证脚本在执行过程中不会出现上述的错误,我们在某些时刻就必 须保证变量类型的正确,此时就需要转换变量的类型了。 在其它编程语言中,因为对变量类型的严格控制,在代码编写过程中也经常 需要转换变量类型。比如说一个整型变量如果想在消息框中显示出来的话,像 AutoIt 那样直接使用肯定是不行的,需要转换成字符串类型之后再使用。 因此不管是不是在 AutoIt 中,变量类型的转换都很重要。这一节我们就讲一 下 AutoIt 中的变量类型转换。 2.1、转换为指针 指针这种类型的变量,在 AutoIt 中是很少见的,因为 AutoIt 没有指针。但是 在一些应用中,我们常常需要用到指针,所以 AutoIt 还是提供了一个内置函数 Ptr 来把变量转换成指针类型。 指针在 AutoIt 中一般是在调用 DLL 时需要用到,而且这个指针大多数都是 数据结构的指针,比如我们在调用 API 时就会经常需要创建一个数据结构,然 后再把结构中某个元素的指针作为调用 API 的参数使用。 因为实在不知道要举一个什么样的例子来说明把字符串变量转换成指针变 量,Ptr 函数的使用非常简单,而且要演示指针一时也想不到具体的例子,所以 这个实例暂时就不举了。我在这里就解释一下指针的概念吧。 因为程序中所有的变量都需存放在内存,为了能正确的访问到这些数据,就 必须给它们进行编号,这个编号就像是变量在内存里的地址,当需要访问的时候, 就可以根据地址找到变量。这个地址,就是我们说的指针。而指针变量,就是用 于存放变量地址的变量。 AutoIt 入门与提高 crossdoor Page 30 4/24/2012 2.2、转换为句柄 句柄是 Windows 给程序建立或访问的对象分配的一个唯一标识,在 Windows 系统中存在很多句柄,比如窗口、文件、位图等等。这一节我们要讲的是窗口句 柄。 系统分配给窗口的句柄是一个整数,在 AutoIt 中通常会以一个 16 进制数来 表示。要把一个整数或字符串转换成句柄,我们需要用到 HWND 函数,我们来 看一个实例: $hWnd = WinGetHandle("[class:Shell_TrayWnd]") ;获取任务栏窗口的句柄 WinSetState($hWnd, "", @SW_HIDE) ;使用任务栏句柄来隐藏任务栏 Sleep(1000) ;休息 1 秒 WinSetState($hWnd, "", @SW_SHOW) ;恢复显示任务栏 MsgBox(0, "", "我们把获取到的句柄转换成字符串,然后再次隐藏任务栏试试。") $sWnd = String($hWnd) ;把句柄转换成字符串,再次隐藏显示任务栏试试 WinSetState($sWnd, "", @SW_HIDE) ;使用任务栏句柄来隐藏任务栏 Sleep(1000) ;休息 1 秒 WinSetState($sWnd, "", @SW_SHOW) ;恢复显示任务栏 MsgBox(0, "", "上面的实验失败了,句柄不能是字符串,所以我们把字符串转换成句柄再 试试。") $Wnd = HWnd($sWnd) ;把句柄转换成字符串,再次隐藏显示任务栏试试 WinSetState($Wnd, "", @SW_HIDE) ;使用任务栏句柄来隐藏任务栏 Sleep(1000) ;休息 1 秒 WinSetState($Wnd, "", @SW_SHOW) ;恢复显示任务栏 2.3、转换为整数 脚本在用户的使用过程中,变量可能会产生一些我们不知道的变化,而有时 候这个变量又要确保它是一个整数,这样才能保证脚本接下来的功能不会发生错 误。 比如说从窗口接收一个用户输入的数据,然后用于计算,如果用户输入的不 是数字,那计算的结果肯定会发生错误,此时我们就需要把用户输入的数据先转 换成整数,确保变量类型的准确,然后再计算。 把一个变量转换为整数使用的是 Int 函数,这个应该很好理解,简单说明一 下: MsgBox(0, "整数转换示例", "10 转换后的结果:" & Int(10) & _ @LF & "w 转换后的结果" & Int("w")) 上面的代码运行后,w 转换的结果会是 0。根据帮助文档的说明,我们可以 知道,不是数字的字符串进行转换的结果就是 0。 再上面的代码中,我们用了一个以前没见过的符号_,这个下划线在 AutoIt 中的作用是换行。也就是说当一行的代码太长时,为了便于阅读,我们就可以使 AutoIt 入门与提高 crossdoor Page 31 4/24/2012 用下划线把这行代码分成两行甚至多行。 2.4、转换为二进制数据 这里说的二进制数据,其实就是字符串编码,AutoIt 中有一个函数可以很方 便的把字符串转换成各种编码值,当然这些编码值也可以转换回字符串。这里需 要用到的函数有两个,一个把字符串转换成二进制数据,一个把二进制数据转换 成字符串。 这两个函数分别是 StringToBinary、BinaryToString。这两个函数都有两个参 数,第一个参数是需要转换的数据,第二个参数是转换标志。 标志 [可选参数] 修改数据编码转换格式: 标志 = 1 (默认), 二进制数据为 ANSI 编码 标志 = 2, 二进制数据为 UTF16 小编码 标志 = 3, 二进制数据为 UTF16 大编码 标志 = 4, 二进制数据为 UTF8 编码 因为帮助中的演示代码很详细,所以这里我只举个简单的例子: $sString = "Hello,我是要转换的字符串!" $ASCII = StringToBinary($sString) MsgBox(0, "", "字符串的 ASCII 码:" & $ASCII & _ @LF & "ASCII 码转回的字符串:" & StringToBinary($ASCII)) 第四章 数组 数组,顾名思义就是一组相同类型的数。在 AutoIt 中,前面那句话是不成立 的,由于 AutoIt 特殊的变量类型,数组并不是只能存放同一类型的数据。 在程序中,数组也是一个变量,只是这个变量比较特别,因为它可以存储很 多个数据,这些数据可以是相同类型的,也可以是不同类型的。 数组变量可以只有一个成员,也可以有很多个成员,每个成员我们可以认为 是一个单独的变量,在 AutoIt 中它可以存放任何数据。 这些成员都通过数组的下标来访问。在 AutoIt 中,数组的开始下标是 0。也 AutoIt 入门与提高 crossdoor Page 32 4/24/2012 就是说,如果一个数组有 4 个成员,那这四个成员的下标依次是 0、1、2、3, 请不要惯性思维的认为还有个下标 4。 1、一维数组 在数组中,最简单的就是一维数组。数组的定义方式与变量相同,不同的在 于数组变量的名称格式,看一个定义数组的实例: Dim $array[2] 上例定义了一个拥有 2 个成员的数组,因为没给成员赋值,所以这个数组现 在还是空的,没存放任何数据。下面我们看看如何给数组赋值,有两种方法,一 种是定义数组时就赋值: Dim $array[2] = [1, 2] 还有一种是定义数组之后再赋值: Dim $array[2] $array[0] = 1 $array[1] = 2 我们用一个表格来说明下上面的数组: 数组名称 $array[0] $array[1] 数据 1 2 一维数组是最简单也是最容易被理解的数组,所以关于它的说明就到此为 止,下面我们看一个应用一维数组的例子: ;定义一个 7 元素数组,用来存放一周中每天的名称 Dim $week[7] = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"] Dim $day = 2 ;这个变量表示我们要查看的数组元素 If $day > 6 Then $day = 6 If $day < 0 Then $day = 0 ;用两个 If 来判断$day 变量的大小,是否超出数组 MsgBox(0, "", $week[$day]) 2、二维及多维数组 相较于一维数组,之上还有二维、三维等多维数组,AutoIt 最大支持的数组 维度有 64 维,这么恐怖的数组,不晓得在哪里会用得上。我们一般使用的大多 数是一维、二维数组,其它的几乎没什么几乎用到,毕竟我们使用 AutoIt 可不 是要做惊天动地的大事,我们只是想写点小程序帮助自己从一些繁琐的操作中解 脱出来而已。 关于数组围数的解释,我在网上看过一个比喻很好,这里引用一下。 AutoIt 入门与提高 crossdoor Page 33 4/24/2012 ━━━━━━━━━━━━━━━━━━━━━━━━━━ 关于多维数组的解释: ━━━━━━━━━━━━━━━━━━━━━━━━━━ 假如数组名称为“房间”代表房间位置: 一维:一幢平房共 10 个房间,第 5 个房间=房间[5] 二维:二幢平房,一幢 10 个房间。 则第一幢、第五个房间=房间[1][5] 三维:二幢 2 层楼房,一幢 10 个房间。 则第一幢、第二层、第五个房间=房间[1][2][5] ━━━━━━━━━━━━━━━━━━━━━━━━━━ 上面的解释,非常清楚的解释了多维数组的概念,如果还有朋友看不懂,那 实在抱歉,我能力有限,恐怕无法再帮助你了。 这一节的名称中虽然包含多维数组,但是我只对二维数组举一个应用实例。 扩展一下上面的一维数组,把它改成一个存放课程表的数组,具体看代码:(一 周上三天课,且每天三节课。) Dim $week[4][3] = [["周一", "周二", "周三"], ["语文", "数学", "数学"], ["英语", " 语文", "体育"], ["英语", "音乐", "班会"]] 上面的数组用一个表格来表示: 2.1、数组的维数 这节我们说一下如何获取数组的维数,AutoIt 中有一个内置函数 UBound, 使用它可以获取数组的维数,除此之外,这个函数还可以获取数组各维上的元素 个数。我们来看实例: Dim $week[4][3] = [["周一", "周二", "周三"], ["语文", "数学", "数学"], ["英语", " 语文", "体育"], ["英语", "音乐", "班会"]] $rows = UBound($week) $cols = UBound($week, 2) $dims = UBound($week, 0) MsgBox(0, "当前 " & $dims & "-维数组有", $rows & " 行, " & $cols & " 列") AutoIt 入门与提高 crossdoor Page 34 4/24/2012 2.2、数组调整 当我们需要定义一个数组来存放数据,但是不知道数据有多少个时,我们可 以使用 ReDim 关键字来调整数组。看实例:(仍然使用上面用的的数组,我们给 每天加一节课) Dim $week[4][3] = [["周一", "周二", "周三"], ["语文", "数学", "数学"], ["英语", " 语文", "体育"], ["英语", "音乐", "班会"]] ;先定义数组 ReDim $week[5][3] ;重定义大小 For $i = 0 To 2 $week[4][$i] = "美术" ;赋值,多加的那节课全部为美术课 Next 我们用一个表格来显示数组调整后的数据: ReDim 函数还有一个用处就是清理数据,因为 AutoIt 没有数组销毁函数,所 以如果要清理数组的话,我们可以使用这个函数,使用此函数把数组调成只有一 个元素的一维数组,这样就能达到清理数组数据的目的,但是这样做,是否能释 放内存资源就不得而知了。(注意:如果原数组是一维数组,那重定义成只有一 个元素的一维数组后,第一个元素数据将会被保留,此时可以给它赋一个空值, 以达到清理数据的目的。) 使用 ReDim 函数清理数组的实例我就不写了,大家有兴趣可以自己写来试 试。 第五章 注册表读写 注册表,是 Windows 系统的一个专用产物。我们可以把它理解成为一个数据 库,专门用来储存系统和应用程序设置信息的数据库。 与 VB 不同的是,AutoIt 内置了操作注册表的函数,而不需使用 API 来实现 这些功能。AutoIt 中操作注册表是一件很简单的事情,之所以要独立出来做一章 讲解,主要是为了方便大家查阅。 AutoIt 入门与提高 crossdoor Page 35 4/24/2012 1、读取注册表 读取注册表数据,我们使用的函数是 RegRead,通过查看帮助文档,我们知 道这个函数有两个参数,第一个是键名,第二个是值项。这里没什么可说的,实 例我直接复制帮助文档的好了: $var = RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVer sion", "ProgramFilesDir") MsgBox(4096, "Program files are in:", $var) 关于注册表的读取,还有两个特别的函数,它们分别是 RegEnumVal、 RegEnumKey。 RegEnumVal 用来读取指定键名下所有值项的数据,我们来看实例:(读取开 机启动的程序) For $i = 1 to 100 $var = RegEnumVal("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Current Version\Run", $i) if @error <> 0 Then ExitLoop MsgBox(4096, "开机启动项 #" & $i, $var) Next RegEnumKey 用来读取知道键名下所有的子键名称,我们来看实例: For $i= 1 to 10 $var = RegEnumKey("HKEY_LOCAL_MACHINE\SOFTWARE", $i) If @error <> 0 then ExitLoop MsgBox(4096, "安装的第 " & $i & " 个程序: ", $var) Next 2、写入注册表 注册表写入函数是 RegWrite,查看帮助可以得知这个函数有四个参数,其中 后三个参数可以省略,当省略的时候,就表示只创建第一个参数指定的键名,而 不写入值。如果第一个参数是一个新的键名,且后面三个参数也使用了的话,就 表示创建新键的同时写入值。 值的写入要注意一个很重要的地方,那就是注册表的数据类型,注册表的数 据类型主要有以下四种: 显示类型 数据类型 说明 REG_SZ 字符串 文本字符串 REG_MULTI_SZ 多字符串 含有多个文本值 的字符串 REG_BINARY 二进制数 二进制值,以十六 AutoIt 入门与提高 crossdoor Page 36 4/24/2012 进制显示。 REG_DWORD 双字 一个 32 位的二进 制值,显示为 8 位 的十六进制值。 如果要更新指定键的数据,也同样是使用 RegWrite 函数,使用方法与写入新 的数据是一样的。 我们来看一个实例: ; 创建一个新键,并同时写入一个单行字符串数据 RegWrite("HKEY_CURRENT_USER\Software\Test", "TestKey", "REG_SZ", "Hello this is a test") ; 更新刚才写入的字符串数据 RegWrite("HKEY_CURRENT_USER\Software\Test", "TestKey", "REG_SZ", " 我是新来的") ;写入一个多行字符串数据 RegWrite("HKEY_CURRENT_USER\Software\Test", "TestKey1", "REG_MULTI_SZ", "Hello1" & @LF & "Hello2") ;写入一个二进制数据 RegWrite("HKEY_CURRENT_USER\Software\Test", "TestKey2", "REG_BINARY", 0x01) ;只创建新建,不写数据 RegWrite("HKEY_CURRENT_USER\Software\Test1") 有写就有删,AutoIt 中删除注册表数据的函数是 RegDelete,查看帮助可以知 道这个函数有两个参数,第二个参数值项名可以省略,在省略的情况下,表示删 除第一个参数指定的键。看一个实例: ; 删除上例建立的 TestKey 值项 RegDelete("HKEY_CURRENT_USER\Software\Test", "TestKey") ; 删除上例建立的两个键 RegDelete ("HKEY_CURRENT_USER\Software\Test") RegDelete ("HKEY_CURRENT_USER\Software\Test1") 第六章 文件读写 Windows 的文件 I/O 操作使用的是文件句柄和读写指针,使用 API 操作的文 件不仅有普通文本文档,还有串口、磁盘设备、网络文件、控制台和目录等。相 对的,因为功能强大,所以 API 函数的使用也就比较复杂。 AutoIt 中提供了内置函数来进行文件读写操作,但是只能操作普通的文件。 AutoIt 入门与提高 crossdoor Page 37 4/24/2012 虽然功能减弱了,不过使用起来也简单许多。 在讲文件读写操作前,还有一点必须要说明的是,Windows 下的文件都有一 个后缀名,用于区分文件类型。这个后缀只是用来给用户区分文件类型的,但并 代表使用某种后缀的文件就真的是那种类型的文件。 比如说 Windows 下的可执行 PE 文件后缀名一般都是.EXE 和.COM,但是如 果你使用.TXT 做后缀的话,它也还是一个可执行 PE 文件,只是我们双击后不 能正常运行而已。要正常运行它,只要通过运行来打开就好了。或者修改注册表, 把.TXT 文件当成.EXE 来执行。 1、Ini 配置文件读写 AutoIt 读写 ini 文件可直接使用内置函数 IniWrite、IniRead。两个函数都有 四个参数,前三个是一样的,区别在于第四个参数,写入数据时,第四个参数是 需要写入的数据;读取数据时,第四个参数是读取失败后需要返回的默认值。 两个函数的使用都很简单,看个实例: ;在桌面的 test.ini 写入数据,不存在文件时会自动创建 IniWrite(@DesktopDir & "\test.ini", "setup", "test", "haha") ;读取刚才写入的数据 $a1 = IniRead(@DesktopDir & "\test.ini", "setup", "test", "") ;读取一个不存在的键名的数据 $a2 = IniRead(@DesktopDir & "\test.ini", "setup", "test2", "默认") MsgBox(0, "", "test 键值:" & $a1 & @LF & "test2 键值:" & $a2) 要删除 ini 文件中指定字段下的键名,或者删除指定的字段,可以使用 IniDelete 函数。这个函数有三个参数,第三个参数可省略。当省略时,表示要删 除指定的字段,不省略的话则表示删除字段下指定的键名。看例子: ;删除 test 键名 IniDelete(@DesktopDir & "\test.ini", "setup", "test") ;删除 setup 字段 IniDelete(@DesktopDir & "\test.ini", "setup") 如果需要读取 ini 中所有字段的名称,可以使用函数 IniReadSectionNames, 看帮助文档中的例子: $var = IniReadSectionNames(@WindowsDir & "\win.ini") If @error Then MsgBox(4096, "", "错误, 读取 INI 文件失败.") Else For $i = 1 To $var[0] MsgBox(4096, "", "字段名:" & $var[$i]) Next AutoIt 入门与提高 crossdoor Page 38 4/24/2012 EndIf 如果要读取 ini 文件中某个字段下所有的键名和值,可以使用函数 IniReadSection,看例子: $var = IniReadSection(@WindowsDir & "\win.ini") If @error Then MsgBox(4096, "", "错误, 读取 INI 文件失败.") Else For $i = 1 To $var[0][0] MsgBox(0, "", "关键字: " & $var[$i][0] & @LF & "值: " & $var[$i][1]) Next EndIf 有一点需要特别注意的是,使用 IniReadSectionNames 和 IniReadSection 函数 时,因为要兼容 9x 系统,这两个函数都只能读取 ini 文件的前 32767 个字符。当 文件大小超出限制时,将无法获取到完整的数据。 还有一点就是,像本章开头说的,ini 文件不一定就一定需要使用.ini 作为后 缀。如果使用.exe 做后缀的话,只有文件符合 ini 文件的格式,那也一样可以使 用 ini 文件读写函数读取和写入数据。 2、Txt 文档读写 AutoIt 读写文本文件,使用的函数有 FileOpen、FileClose、FileWrite、 FileWriteLine、FileRead、FileReadLine。 不管是读或写文件,在 AutoIt 中既可以使用文件句柄来进行操作,也可以直 接操作。我们先看如何直接操作: ;写入一个字符串并以换行符结尾,也就是按行写 FileWriteLine(@DesktopDir & "\test.txt", "第一行") ;紧接着文本原来的数据写入 test 字符串 FileWrite(@DesktopDir & "\test.txt", "test") ;读取前四个字节的数据 $var1 = FileRead(@DesktopDir & "\test.txt", 4) ;读取第二行的数据 $var2 = FileReadLine(@DesktopDir & "\test.txt", 2) MsgBox(0, "", "前 4 个字节: " & $var1 & @LF & "第二行: " & $var2) FileWrite、FileWriteLine 两个函数在写入数据的时候,若写入文件不存在, 则会自动创建。不过需要注意的是,当文件的路径也不存在时,就会写入失败。 FileWrite、FileWriteLine、FileRead、FileReadLine 这四个函数的第一个参数, 都可以是文件名,或文件句柄,上面的例子就是直接使用文件名来读写数据。这 样的做法在少量的读写操作时用起来还是不错的,但是当要进行大量的读写操作 时,这样做就会降低脚本的效率。因为牵扯到文件 I/O 操作,这里就不做说明。 我们只要知道当要进行大量次数的读写操作时,使用文件句柄来操作是比较 理想的办法。要获得文件的句柄可以使用 FileOpen 函数,成功打开文件后将会 AutoIt 入门与提高 crossdoor Page 39 4/24/2012 返回一个文件句柄,供我们进行读写操作,当操作结束后,记得使用 FileClose 函数关闭句柄。 FileOpen 函数有两个参数,第二个参数是我们要重点注意的地方,这个参数 将指定我们获取的句柄能对文件进行何种操作,这个参数默认是只读模式。 模 式 [可选参数] 指定以何种模式(读或写)打开文件: 可以是下列几种: 0 = 只读模式(默认) 1 = 写入模式(附加数据到文件尾部) 2 = 写入模式(先删除之前的内容) 8 = 如果目标目录不存在就创建(参考注意). 16 = 强制使用二进制(字节)模式(参考注意) 32 = 使用 Unicode UTF16 小编码读写模式,读取不会覆盖存在的 BOM. 64 = 使用 Unicode UTF16 大编码读写模式,读取不会覆盖存在的 BOM. 128 = 使用 Unicode UTF8 (带 BOM)读写模式,读取不会覆盖存在的 BOM. 256 = 使用 Unicode UTF8 (无 BOM)读写模式. 16384 = 当打开一个文件读取时(文件没有 BOM), 使用完整文件 UTF8 检测. 如果没 有使用这一模式,则只会检测文件最前端的 UTF8 标志. 文件夹路径必须存在(如果没有指定模式 '8' - 见注释). 一般情况下我们大多是使用前四种模式组合使用,至于后五种用的比较少, 而且解释起来因为要涉及到字符编码问题,所以就不解释了。 我们先来看一个只读模式的例子: $file = FileOpen(@DesktopDir & "\test.txt", 0) ;注意第二个参数 0 ; 检查打开的文件是否可读 If $file = -1 Then MsgBox(0, "错误", "不能打开文件.") Exit EndIf MsgBox(0, "全部数据", FileRead($file)) FileClose($file) ;关闭句柄 上例中 FileOpen 函数使用的是只读模式打开文件,所以我们只能读取文件的 内容,而不能写入,不信的话大家可以自己试试。要记住的是使用 FileOpen 函 数获取了文件句柄,在读写操作完毕后,一定要记住使用 FileClose 函数关闭句 柄。 接着我们来看看写入模式,写入数据有两个模式 1 和 2,查看上面的表格可 以知道两者的区别。1 是在文件末尾接着写入数据,2 则是先清空文件内容然后 再写入数据。 还有一个 8 模式,使用这个模式后,当文件的路径不存在时,就会自动创建 路径。我们就获取一个 2 加 8 模式的句柄,看实例: $file = FileOpen(@DesktopDir & "\t\test.txt", 2 + 8) ;注意第二个参数 ; 检查打开的文件是否可读 If $file = -1 Then MsgBox(0, "错误", "不能打开文件.") Exit AutoIt 入门与提高 crossdoor Page 40 4/24/2012 EndIf ;写入一个字符串并以换行符结尾,也就是按行写 FileWriteLine($file, "行") ;紧接着文本原来的数据写入 test 字符串 FileWrite($file, "t") MsgBox(0, "全部数据", FileRead($file)) ;句柄是写模式的,所以读数据会失败 FileClose($file) ;关闭句柄 上例的消息框显示将会是空,因为句柄不具备读取模式,所以无法读取内容, 但是数据写入是成功了的。大家可以手动打开文件看一下。 16 模式我们挪到下一节讲。 3、二进制文件读写 FileOpen 函数获取的还要一个 16 模式,通过这个模式来打开文件进行读写, 我们可以读取到文件内容的二进制数据,或者是按二进制修改文件数据。 我们来看一个例子: $file = FileOpen(@WindowsDir & "\Notepad.exe", 0 + 16) ; 检查打开的文件是否可读 If $file = -1 Then MsgBox(0, "错误", "不能打开文件.") Exit EndIf MsgBox(0, "记事本的二进制数据", FileRead($file)) FileClose($file) ;关闭句柄 上例中把记事本程序的二进制数据读取了出来,我们在 FileOpen 函数中使用 的模式是 0 加 16,也就是二进制读取模式。 除了读取,二进制也可以由写入模式。不过二进制写入一般都是替换原有数 据的情况比较多,因为要用到二进制模式写入数据的情况,大多是要修改 EXE 或 DLL 文件,因为这两种文件的特殊性,我们如果对其写入新数据,将会导致 原程序的损坏。而且我们大多都只是需要修改其中一些关键的数据而已,其它的 数据都希望保持不变。 举个例子说,比如一个网游客户端的主程序里有连接服务端的 IP,我们想要 修改这个 IP 的值,但是又要保持程序还能正常运行。此刻,我们就只需要把这 个 IP 替换掉即可。 要达成上例中的目的,我们要先把程序的二进制数据读取出来,然后再进行 替换,最后再重新写入数据。为什么要先读取再写入呢?这是因为文件句柄不能 同时具备读写两种模式,一个句柄只能是读或写一种模式。 因为教程的关系,无法附带一个 EXE 文件来演示对 PE 文件的二进制数据修 改,所以这里我就拿上面例子中创建的 txt 文件进行二进制数据修改的演示,虽 然文件类型不同,但是方法都是一样的。 我们看实例:(把文本中的 t 替换成 a) $file = FileOpen(@DesktopDir & "\t\test.txt", 0 + 16) ; 检查打开的文件是否可读 AutoIt 入门与提高 crossdoor Page 41 4/24/2012 If $file = -1 Then MsgBox(0, "错误", "不能打开文件.") Exit EndIf $dat = FileRead($file) ;先读出数据 FileClose($file) ;关闭句柄 $a = Hex(Asc("a"), 2) ;获取 a 的 ascii 码值的 16 进制数 $t = Hex(Asc("t"), 2) ;获取 a 的 ascii 码值的 16 进制数 $dat2 = StringReplace($dat, $t, $a) ;把 t 替换成 a $file = FileOpen(@DesktopDir & "\t\test.txt", 2 + 16) ;二进制写入 FileWrite($file, $dat2) ;写入修改后的数据 FileClose($file) ;关闭句柄 上例脚本执行完毕后,大家可以手动打开文本查看,第二行的 t 已经被替换 成 a 了。如果不理解为什么要使用字符的 ascii 码值的 16 进制数来进行替换的, 可以把读取到的二进制数据用消息框显示出来观察一下。 在二进制数据替换的过程中,有一个需要大家特别注意的地方,那就是目标 数据与替换数据的长度应该相等。当然,在文本文件中这个要求是无意义的。但 是在 PE 文件或者动态链接库文件中,因为文件结构的关系,一旦把文件数据修 改的与原文件大小不一,就会出现文件文法正常工作的情况。 所以当我们在使用二进制数据替换 EXE 之类的比较特殊的文件时,需要特 别注意目标数据与替换数据的长度要一样才行。 除了读写文件,还有个删除文件的函数 FileDelete,这个函数只有一个路径 参数,用于指定需要删除的文件。文件可以使用通配符,用来表示要删除某种类 型的文件,比如使用*.txt 表示删除所有的 txt 文件。 来看使用实例: FileDelete(@DesktopDir & "\test.txt") ;删除桌面的 test.txt FileDelete(@DesktopDir & "\t\*.txt") ;删除桌面 t 目录下的所有 txt 文件 要删除目录需要使用函数 DirRemove,看实例: DirRemove (@DesktopDir & "\t ") ;删除桌面的 t 目录 这个函数,还有第二个参数,是用来指定是否连同目录底下的子目录和文件 一起删除的,我就不做演示了,大家自己试试看。 第七章 进程管理 作为一个强大的脚本语言,AutoIt 当然也能创建和结束系统进程。所谓进程 AutoIt 入门与提高 crossdoor Page 42 4/24/2012 就是正在进行中的程序,硬盘上存放的可执行文件是不能称为进程的,在内存中 执行的文件才是进程。 进程就是可执行文件使用的资源总和,包括虚拟地址空间、对象句柄、数据、 代码等等。同一个可执行文件,之所以能建立多个进程,这是因为不同的进程之 间的地址空间等资源是相互隔离的。 1、进程列表 AutoIt 内置函数中有一个获取进程列表的函数 ProcessList,使用这个这个函 数可以获取一个简单的进程表单,其中包括进程名和进程 PID。 我们来看看实例: #Include $ProcessList = ProcessList() _ArrayDisplay($ProcessList, "进程列表") 上例的第一行出现了一个之前没见过的关键字#Include,这个关键字的作用 是把其它脚本包含到当前的脚本当中,这样我们就可以使用在其它脚本中写好的 自定义函数了。通常我们把这个包含的脚本叫做 UDF(User Defined Function)。 像上例中的第三行,使用的就是 Array.au3 这个 UDF 中定义的函数。UDF 除 了用来定义函数,也同样可以用来定义变量或者数据结构。当我们要写个大工程 时,有时一个变量需要在多个脚本中引用,为了方便修改与管理,此时我们就可 以把这个变量在一个独立的脚本文件中定义,然后在其它脚本中引用即可。 一个进程除了进程名和 PID 之外,还有许多其它的信息,比如父进程、子进 程、子线程、引用的 DLL 模块等等。要得到这些信息,使用内置函数 ProcessList 显然是不可行的。 此时我们可以借助 API 来实现,API 中有一个进程快照函数就可以完全胜任 我们的需求,这个函数就是 CreateToolhelp32Snapshot。关于这个函数的用法我就 不在此讲了,如果想了解的朋友,可以看下我以前写的一个 AU3 进程管理器, 就完全是使用这个函数来实现的。 2、进程等待及结束 AutoIt 中进程等待函数有两个,ProcessWait 等待进程出现,ProcessWaitClose 等待进程结束。这两个函数的用法很简单,此处就不写实例了。 AutoIt 中结束进程可以使用函数 ProcessClose,使用此函数结束进程时,如 果使用的参数是进程名,且这个进程名存在多个进程,那 PID 最高的进程将会 被结束。也就是说,如果进程中有很多同名进程的话,使用进程名来结束进程, 只能结束一个进程,而不会结束所有的同名进程。这个函数的使用也很简单,所 以也不写实例了。 AutoIt 入门与提高 crossdoor Page 43 4/24/2012 3、运行文件 在 AutoIt 中,如果要运行一个文件,可以使用的函数有 Run、RunWait、RunAs、 RunAsWait、ShellExecute、ShellExecuteWait。 Run 和 ShellExecute 两个函数是以系统当前的用户运行一个文件。 RunWait 和 ShellExecuteWait 两个函数是以系统当前的用户运行一个文件, 并且等待创建的程序结束后再继续执行脚本。 RunAs 和 RunAsWait 两个函数则可以指定一个系统用户来运行一个文件。 这里我们只讲 Run、RunWait、ShellExecute、ShellExecuteWait 四个函数,另 外两个因为不常用所以不讲,如果有兴趣的朋友可以自己试试看。 先来看 Run:(帮助中的例子,最大化执行记事本程序) Run(@WindowsDir & "\Notepad.exe", "", @SW_MAXIMIZE) 接着试试 RunWait:(帮助中的例子,等待创建的记事本进程结束) $val = RunWait(@WindowsDir & "\Notepad.exe", "", @SW_MAXIMIZE) MsgBox(0, "", "退出代码:" & $val) 如果要执行批处理指令怎么办?我们可以使用一个宏@ComSpec 来达成目 的,这个宏的值是系统命令解释程序的路径。看个例子: Run(@ComSpec & " /k help | more ") 上例在批处理指令之前加了一个/k 参数,此参数的作用是在指令执行完毕 后,保留 cmd 窗口。如果想不想保留 cmd 窗口的话,就使用/c 参数。 Run 和 RunWait 两个函数只能执行可执行程序,且文件扩展名为 EXE、BAT、 COM 或 PIF。如果文件扩展名不是这几个,那函数将会执行失败。 如果一个文件是可执行程序,但扩展名不是上述的几个,那要如何执行?办 法有两个,一是修改注册表,把此类扩展名的文件设置为 EXE 类型的文件;另 一种就是使用 API 来执行文件。 我们先看下改注册表的方法:(把.txx 类型的文件设置为 EXE 类型文件) RegWrite("HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.txx", " Content Type", "REG_SZ", " application/x-msdownload") RegWrite("HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.txx", "", "REG_SZ", "exefile") ;复制一个记事本程序到桌面,并改后缀为.txx FileCopy(@WindowsDir & "\Notepad.exe", @DesktopDir & "\Notepad.txx") Run(@DesktopDir & "\Notepad.txx") 上例运行后,查看任务管理器,将会看到一个以.txx 为后缀的进程。 Windows 中可以使用 WinExec 和 ShellExecute 这两个 API 来执行一个可执行 文件,当然还有一个进程创建函数 CreateProcess 也同样可以用来执行一个可执 行文件。至于三者的区别,有兴趣的朋友可以谷歌看看,我这里不做解释。 三个 API 中,WinExec 是最简单,这里我就用这个来做实例: ;复制一个记事本程序到桌面,并改后缀为.aaaaa FileCopy(@WindowsDir&"\Notepad.exe",@DesktopDir&"\Notepad.aaaaa") _Exec(@DesktopDir & "\Notepad.aaaaa") AutoIt 入门与提高 crossdoor Page 44 4/24/2012 Func _Exec($lpCmdLine, $uCmdShow = @SW_SHOW) $Ret = DllCall("kernel32.dll", "Int", "WinExec", "str", $lpCmdLine, "Int", $uCmdShow) If $Ret[0] < 32 Then Return SetError(1, 0, $Ret[0]) EndIf Return $Ret[0] EndFunc 上例中 WinExec 这个 API 被我写成了一个自定义函数_Exec。运行成功后, 在进程管理器中我们会发现一个后缀为.aaaaa 的进程。 如果要执行的文件不是一个可执行文件,那我们可以使用 ShellExecute、 ShellExecuteWait 函数来实现目的。这两个函数会根据文件后缀,寻找关联的程 序运行文件,例如一个 rar 压缩包文件,将会使用 winrar 程序来打开它。 我们来看一个实例: ShellExecute(@WindowsDir & " \win.ini") ShellExecuteWait 函数也是一样: $val = ShellExecuteWait(@WindowsDir & " \win.ini") MsgBox(0, "", "退出代码:" & $val) 要知道的是 ShellExecute、ShellExecuteWait 函数也可以执行可执行文件,但 是这两个函数执行后不会返回进程的 PID。 AutoIt 入门与提高 crossdoor Page 45 4/24/2012 第八章 窗口管理 1、窗口列表 AutoIt 中要获取到系统当前所有的窗口可以使用函数 WinList,此函数会返回 一个二维数组,用来存放当前系统所有窗口的句柄和标题。需要注意的是,这个 函数会连隐藏窗口的句柄一起返回。看实例: #Include $WinList = WinList() _ArrayDisplay($WinList, "窗口列表") 2、窗口等待及结束 有时候我们需要在某个特定窗口存在的情况下,才执行脚本,此时就可以用 到窗口等待函数 WinWait,此函数执行后会暂停脚本直到指定窗口出现。看实例: Run("notepad.exe") $s = WinWait("无标题 - 记事本", "", 3) If $s = 0 Then MsgBox(0, "", "等待窗口失败!") Else MsgBox(0, "", "等待窗口的句柄为:" & $s) EndIf 上例将等待记事本窗口 3 秒,超时后将继续执行脚本。如果要无限时等待, 可以删除第三个参数,或者第三个参数使用 0。 如果要结束一个窗口,可以使用函数 WinClose,此函数的第一个参数既可以 用窗口标题,也可以使用窗口句柄。需要注意的是,当使用窗口标题做参数时, 如果存在多个同标题的窗口,函数将关闭最后一个被激活的窗口。看个例子: Run("notepad.exe") WinWait("无标题 - 记事本", "", 3) WinClose("无标题 - 记事本") 要结束窗口还有一个函数 WinKill,使用此函数将会强行结束窗口。 3、窗口自动化操作 自动化操作窗口是 AutoIt 的强项,AutoIt 内置了许多函数来实现窗口的自动 化操作。 AutoIt 入门与提高 crossdoor Page 46 4/24/2012 3.1、按键发送 使用函数 Send,我们可以实现向激活窗口发送按键。例如: Send("#r") 这句代码将会打开运行窗口。参数中的#代表 Win 键。具体的大家请查看帮 助文档。 Send 函数只能对当前的激活窗口发送按键,如果我们要后台操作的话,可以 使用函数 ControlSend,此函数可以让我们对未激活的窗口进行按键模拟操作, 看实例: Run("notepad.exe") WinWait("无标题 - 记事本", "", 3) Send("#d") ;激活到桌面,这样记事本就不是当前激活窗口了 ControlSend("无标题 - 记事本", "", "Edit1", "后台发送字符串 abc") 3.2、控件控制 控件控制基本上都是用在自动安装方面比较多,使用的函数一般是 ControlClick、ControlSetText、ControlCommand 这三个。ControlClick 用来模拟 鼠标点击, ControlSetText 用来设置路径之类的文字数据,最后一个 ControlCommand 功能比较多,可以用来选择列表框的选项,也可以用来操作单 选框,这几个函数帮助文档的例子都很详细,大家多看看帮助文档吧,我就不写 例子了,实在是不知道要怎么写。 如果有朋友看了帮助觉得收获不大,可以去论坛的自动安装区看看,里面很 多自动安装的脚本,这部分内容很简单,没什么可说的。 AutoIt 入门与提高 crossdoor Page 47 4/24/2012 第九章 定时器的应用 脚本中经常会遇到需要同时操作多个功能,如果对功能的实时性要求不是很 高,我们一般都会选择定时器来实现。当然,在 AU3 中,除了定时器,我们也 没有别的办法,因为 AU3 不支持多线程,所以定时器就显得格外重要。 什么是定时器呢?顾名思义,就是定时执行指定函数的意思。 1、内置定时器函数 AU3 使用定时器有内置函数,AdlibRegister 用来注册定时器,AdlibUnRegister 用来释放定时器。 下面我们来看段代码,关于定时器的使用。我们将建立两个图标控件,然后 使用定时器来定时更换控件的图标。 #include Local $n1, $n2, $msg GUICreate("内置定时器", 250, 250) $n1 = GUICtrlCreateIcon("shell32.dll", 7, 20, 15, 32, 32);icon 控件 $n2 = GUICtrlCreateIcon("shell32.dll", 7, 20, 55, 32, 32) GUISetState() AdlibRegister("n1",1000);设置定时器 1,函数 n1,时间 1000 毫秒 AdlibRegister("n2",300);定时器 2,函数 n2,时间 300 毫秒 While 1 $msg = GUIGetMsg() If $msg = $GUI_EVENT_CLOSE Then ExitLoop WEnd GUIDelete() AdlibUnRegister("n1");卸载关联在 n1 函数的定时器 AdlibUnRegister("n2") Func n1() GUICtrlSetImage($n1, "shell32.dll", Random(1,10,1)) ;在这里利用 GUICtrlSetImage 实现更换图标的功能,具体参数看帮助 EndFunc AutoIt 入门与提高 crossdoor Page 48 4/24/2012 Func n2() GUICtrlSetImage($n2, "shell32.dll", Random(1,30,1)) EndFunc 上面的代码执行后,就会看到窗口上的两个图标会不断的自动更换。 2、API 定时器 一般来说 AU3 内置的定时器就足够使用了,有时候我们也会使用 API 的定 时器。 API 定时器涉及到系统 API 的调用,本来放到后面的动态链接库小节里面讲 会比较好,但既然讲到定时器了,就在这里讲了吧。 API 的定时器需要用到两个函数,分别是 SetTimer 和 KillTimer,分别用来注 册定时器和关闭定时器。 调用 API 我们需要使用到 DllCall 函数,这个函数的具体用法这里先不讲, 我们只讲定时器。 API 意思就是预先定义好的函数接口,windows 系统预先定义了很多的 API 供程序开发人员调用,这些函数都被写在 DLL 文件中,要调用这些 DLL 里的函 数,我们首先需要了解调用的函数原型。 先看注册定时器的 SetTimer 原型: UINT_PTR SetTimer( HWND hWnd, // 窗口句柄 UINT_PTR nIDEvent, //定时器 ID,多定时器时可以通过 ID 判断是哪个定时器 UINT uElapse, // 时间间隔,单位为毫秒 TIMERPROC lpTimerFunc // 回调函数 ); 我们调用 SetTimer 时,4 个参数可以只使用后面 2 个,前面 2 个不管。最后 一个参数需要使用一个回调函数的指针,AU3 是不支持指针的,我们要怎么办? 查阅帮助文档可以翻阅到 DllCallbackRegister 函数,此函数的功能就是给自 定义函数创建一个回调,然后我们可以使用 DllCallbackGetPtr 函数获取回调函数 的指针,这样就成功的曲线救国了。 SetTimer 函数执行成功,将会返回一个定时器的 ID,这个 ID 我们可以在关 闭定时器时使用。 接着我们看一下 KillTimer 我原型: BOOL KillTimer( HWND hWnd, UINT_PTR uIDEvent ) 这个函数很简单,参数意义同 SetTimer,执行之后返回一个布尔值。第一个 句柄参数,我们使用的时候不管它。 下面我们来看下具体的应用脚本。使用 3 个定时器,分别在窗口中定时更新 AutoIt 入门与提高 crossdoor Page 49 4/24/2012 时间、加 1 操作、2 的自乘操作。 看代码吧,不多说了: Global $t2, $t3 = 1 $Form1 = GUICreate("API 定时器实例", 272, 139) $Label1 = GUICtrlCreateLabel("00:00:00:00", 8, 8, 146, 17);时间 $Label2 = GUICtrlCreateLabel("0", 8, 56, 150, 17);+1 $Label3 = GUICtrlCreateLabel("", 8, 104, 148, 17);2 的自乘 $Button1 = GUICtrlCreateButton("关闭定时器 1", 168, 8, 89, 25, 0) $Button2 = GUICtrlCreateButton("关闭定时器 2", 168, 48, 89, 25, 0) $Button3 = GUICtrlCreateButton("关闭定时器 3", 168, 96, 89, 25, 0) GUISetState(@SW_SHOW) $Timer = DllCallbackRegister("Timer", "int", "hwnd;uint;uint;dword");创建自定义函数 Timer 的回调,API 定时器函数 SetTimer 需要 $TimerDLL = DllCall("user32.dll", "uint", "SetTimer", "hwnd", 0, "uint", 0, "int", 1000, "ptr", DllCallbackGetPtr($Timer));1000 毫秒 执行一次 $Timer2DLL = DllCall("user32.dll", "uint", "SetTimer", "hwnd", 0, "uint", 0, "int", 200, "ptr", DllCallbackGetPtr($Timer));200 毫秒执 行一次 $Timer3DLL = DllCall("user32.dll", "uint", "SetTimer", "hwnd", 0, "uint", 0, "int", 2000, "ptr", DllCallbackGetPtr($Timer));2000 毫秒 执行一次 While 1 Switch GUIGetMsg() Case - 3 ExitLoop Case $Button1 DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $TimerDLL[0]);关闭定时器 Case $Button2 DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $Timer2DLL[0]) Case $Button3 DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $Timer3DLL[0]) EndSwitch WEnd GUIDelete() DllCallbackFree($Timer);关闭回调 Func Timer($hWnd, $uiMsg, $idEvent, $dwTime) AutoIt 入门与提高 crossdoor Page 50 4/24/2012 Switch $idEvent;根据定时器 ID 来进行操作 Case $TimerDLL[0] GUICtrlSetData($Label1, @HOUR & ":" & @MIN & ":" & @SEC & ":" & @MSEC);更新时间 Case $Timer2DLL[0] $t2 += 1 GUICtrlSetData($Label2, $t2);更新+1 Case $Timer3DLL[0] $t3 *= 2 GUICtrlSetData($Label3, $t3);更新 2 的自乘 EndSwitch EndFunc 第十章 Com 对象调用 什么是 Com 对象?这个解释起来恐怕比较复杂,而且对于我们学习 AU3 也 没有太大的帮助。我只简单说一下,Com 对象可以看成是 API 的补充,API 组 件接口不足,就可以通过 Com 对象来做补充。 Com 对象跟 API 有很大的相似,但操作更简单,它把所有的操作都封装起来, 调用时不需要管过程,我们只需要关注结果。 举例来说,比如 ODBC 数据库编程,因为 ODBC 是调用很底层的 API 实现 数据库操作的,如果用 c 语言将会很轻松的调用这些接口,但是用 AU3 这类的 高级语言,就很困难,所以就出现了 DAO 这个面向对象的 Com 接口,供高级 语言调用。 1、创建和使用 Com 对象 在 AU3 中创建 Com 对象有专门的函数 ObjCreate,此函数有四个参数,第一 个参数是需要创建对象的类名,后面三个参数用于远程操作,这里我只讲本地操 作。 使用 ObjCreate 创建对象成功后,将会返回对象句柄,供后续操作,我们看 个创建 shell.application 类的例子,这个例子将会返回系统 windows 目录下的文 件列表,看代码吧: #include Dim $item[1][2];创建 2 维一个数组,存放获取的数据 $shell = ObjCreate("shell.application");建立对象 $fod = $shell.namespace(@WindowsDir) AutoIt 入门与提高 crossdoor Page 51 4/24/2012 $foditems = $fod.items() for $co in $foditems $item[UBound($item)-1][0] = $co.path;存放路径 $item[UBound($item)-1][1] = $co.size;存放大小 ReDim $item[UBound($item)+1][2] next _ArrayDisplay($item) 上例中有出现很多的.符号,这个符号在对象创建成功后,用来引用对象的方 法和属性。像上例中的$shell.namespace(@WindowsDir)就是引用对象的方法, $co.path 就是对象的属性。 2、拦截 Com 对象错误 对象建立成功后,执行的过程中难免会出现错误,我们必须把这些错误拦截 下来,方便查找错误的原因,然后做出修改。同时在编译脚本之后,通过拦截错 误,也可以避免因为错误导致脚本宕掉。 想要拦截 Com 对象错误,我们就需要使用到 ObjEvent 函数,这个函数可以 建立基于事件模式的对象。使用这个函数创建 AU3 的错误对象,然后再使用一 个自定义函数来显示错误。 我们来看看如何拦截错误: #include $Err = ObjEvent(“AutoIt.Error”, “ODBCJET_ErroHandler”);拦截 Com 错误 Dim $item[1][2];创建 2 维一个数组,存放获取的数据 $shell = ObjCreate(“shell.application”);建立对象 $fod = $shell.namespace(“d:\f.a”) ;一个错误的参数,引发错误 $foditems = $fod.items() for $co in $foditems $item[UBound($item)-1][0] = $co.path;存放路径 $item[Ubound($item)-1][1] = $co.size;存放大小 ReDim $item[Ubound($item)+1][2] next _ArrayDisplay($item) Func ODBCJET_ErroHandler();Com 错误截获 Msgbox(16,”Error”,”Intercepted a COM Error !”&@CRLF&@CRLF& _ “err.description is: “&@TAB&$Err.description&@CRLF& _ “err.windescription:”&@TAB&$Err.windescription&@CRLF& _ “err.number is: “&@TAB&hex($Err.number,8)& @CRLF& _ “err.lastdllerror is: “&@TAB&$Err.lastdllerror&@CRLF& _ “err.source is: “&@TAB&$Err.source&@CRLF& _ “err.helpfile is: “&@TAB&$Err.helpfile&@CRLF& _ “err.helpcontext is: “&@TAB&$Err.helpcontext ) EndFunc AutoIt 入门与提高 crossdoor Page 52 4/24/2012 第十一章 动态链接库调用 什么是动态链接库?简单的说就是我们通常见到的 DLL 文件。 动态链接库在 windows 中可谓无处不在,windows 编程不可避免的就会用到 动态链接库。虽然 AU3 无法编写 DLL,但调用却不成问题,我们可以使用其他 语言写好 DLL,然后再来供 AU3 脚本调用。 Windows 系统的 API 函数,就全部都是以动态链接库的形式提供的,所以不 论是调用系统 API 还是我们自己写的 DLL,只要 DLL 是标准的格式,那么 AU3 的调用方法就是一样的。 1、数据结构及 API 调用 如何调用动态链接库,其实我们在前面讲定时器的时候就已经说了一些。 AU3 调用 DLL 的函数有三个,DllOpen、DllCall、DllClose。 一般的情况下我们使用 DllCall 的时候比较多,因为多数时候我们都只会调用 DLL 内的 一个函数,所以就省去了 DllOpen、DllClose 来打开关闭 DLL。 在调用 API 的时候,经常会使用到数据结构,那么在这一节中我就顺便一起讲一下如 何在 AU3 中使用数据结构。 AU3 与数据结构相关的函数有五个,分别是 DllStructCreate、DllStructGetData、 DllStructGetPtr、DllStructGetSize、DllStructSetData,从字面意思上可以看出,这五个函数的 作用分别是创建数据结构、读取结构中的数据、取指针、取大小、设置数据。 相对于 C 语言的 struct 关键字,AU3 对数据结构的运用无疑是很不方便的。AU3 对数 据结构的支持虽然很弱,但应付一些常用问题还是可以的,只是使用起来比较烦琐。 这一节我们使用 API 函数 GetWindowRect 来做示例,这个函数可以获取窗口在屏幕上 的范围。此函数的原型为: BOOL GetWindowRect(HWND hWnd,LPRECT lpRect) 第一个参数是窗口句柄,第二个参数是窗口坐标的数据结构,执行后返回一个布尔值。 要使用此函数,我们首先要定义一个数据结构: $rectVal = DllStructCreate("long Left;long Top;long Right;long Bottom") 定义好数据结构之后,再使用 DllCall 调用 GetWindowRect 函数: DllCall("user32.dll","long","GetWindowRect","hwnd",$Gui,"ptr",Dll StructGetPtr($rectVal)) 注意:在调用时,我们第二个参数传入的是结构体变量$rectVal 的指针。 AutoIt 入门与提高 crossdoor Page 53 4/24/2012 我们来看一段完整的示例代码: $Gui=GUICreate("",360,360) $hEdit=GUICtrlCreateEdit("",5,5,350,350) GUISetState() $rectVal = DllStructCreate("long Left;long Top;long Right;long Bottom") DllCall("user32.dll","long","GetWindowRect","hwnd",$Gui,"ptr",Dll StructGetPtr($rectVal)) GUICtrlSetData($hEdit,"Left:"&DllStructGetData($rectVal,"Left")&@ CRLF& _ "Top:"&DllStructGetData($rectVal,"Top")&@CRLF& _ "Right:"&DllStructGetData($rectVal,"Right")&@CRLF& _ "Bottom:"&DllStructGetData($rectVal,"Bottom")) Do Until GUIGetMsg()=-3 从上面的简单例子中可以看到,当我们需要调用 DLL 内封装的函数时,第一个我们需 要知道的,就是目标函数的原型。如果是系统 API,我们可以通过 MSDN 获得原型,如果 是别人写的 DLL,那就需要作者提供了。 在上例中,我并未对 GetWindowRect 函数的调用成功与否作判断。在实际应用中,如 果这是一个关键功能,这样做显然是不行的。 第十二章 网络编程 现在是网络时代,网络生活已经成为了我们日常生活的一部分,不论是上网听歌,还是 联网打游戏,都需要网络的支持。 大多数的网络程序都分为两部分:客户端与服务端。例如浏览网页,浏览器是客户端, web 服务器是服务端。打网游时,我们需要下载客户端,而游戏运营商的服务器上则运行则 服务端。 本章我们将了解如何使用 AU3 来编写网络应用程序。 1、Windows Socket 接口简介 Windows socket 是 windows 下的网络编程接口。Windows 的各种接口都是以 API 的形 AutoIt 入门与提高 crossdoor Page 54 4/24/2012 式提供的,winsock 也不例外,微软也给我们提供了一组 API 供我们使用。 Winsock 接口提供的函数位于 TCP/IP 模型中的传输层和 internet 层面上,也就是说,我 们可以使用接口函数来编写使用 TCP、UDP 协议的程序。 在 AU3 中,我们并不需要直接调用 winsock 接口函数,因为 AU3 的开发者已经帮我们 把常用的函数封装好了,我们只需要直接调用即可。 AU3 中的网络编程相关函数,我就不在此一一列举,大家看后面的示例代码就清楚了。 2、TCP 应用程序设计 我们来创建一个简单的服务端程序,把接收到的任何数据都原样返回。看代码: ;首先要开启 TCP 服务 TCPStartup() ;使用本地 IP 和 33891 端口创建一个监听 sock $MainSocket = TCPListen(@IPAddress1, 33891) If $MainSocket = -1 Then MsgBox(0,"err","端口被占用!") Exit EndIf $ConnectedSocket = -1 While 1 ;socket=-1 时,不断尝试接受新 sock If $ConnectedSocket = -1 Then $ConnectedSocket = TCPAccept($MainSocket) Else ; 有 sock 时,尝试接收(最高)2048 字节的数据 $recv = TCPRecv($ConnectedSocket, 2048) If @error Then $ConnectedSocket = -1;接收失败,sock 值重置-1 Sleep(100) TcpSend ($ConnectedSocket, $recv) ;接收成功则原样发回 EndIf Sleep(100) WEnd ;结束前关闭 TCP 服务 TCPShutdown() 上面的 Server 端示例代码,功能非常简单,把从客户端接收到的任何数据都原样发回。 需要注意的是,因为 AU3 的弱点,我们无法创建多线程,来实现每一个客户端对应一 个线程控制,所以我们只能在同一时间处理一个客户端的连接。这种情况在实际应用中显然 是不行的,那么我们有什么办法解决么?答案当然是肯定的,不过这里我们先讲基础的,后 面再说如何解决这一问题。 简单分析一下上面的示例代码: AutoIt 入门与提高 crossdoor Page 55 4/24/2012 1、先打开 TCP 2、建立监听 sock 3、while 循环中实现:没有客户端时尝试接受新客户端 sock;有客户端时,尝试接收 客户数据并原样发回。 这段示例代码功能单一,主要目的是让大家熟悉一下函数的用法。为了方便大家测试, 我写了一段 Client 端的代码: TCPStartup() $ConnectedSocket = TCPConnect(@IPAddress1, 33891) If @error Then MsgBox(0, "err", "TCP 连接失败!errcode: " & @error) Else While 1 $szData = InputBox("发送数据给服务端", "输入一个要发送给服务端的数 据:") ; 如果点击了 InputBox 的取消按钮或者使用一个空数据将退出这个循环 If @error Or $szData = "" Then ExitLoop TCPSend($ConnectedSocket, $szData) If @error Then ExitLoop ; 如果发送失败(@error)将断开连接 Sleep(300) $recv = TCPRecv($ConnectedSocket, 2048) If Not @error And $recv<>"" Then MsgBox(0,"S 端返回数据",$recv) WEnd EndIf TCPShutdown() 因为 AU3 的局限性,我们不能对每一个客户端都建立相应线程,既然不能使用这种在 其他语言中常用的网络数据处理方式,自然也就无法使用 iocp 模型了。 那么有什么办法可以使我们喜爱的 AU3 编写多客户的支持的程序么?答案当然是可 以。而且实现的方法不止一种。 还记得我们前面说过的么?windows 窗口编程是基于消息的,微软提供了一个函数 WSAAsyncSelect,可以让我们把 sock 消息关联到窗口消息上,这样我们就可以做到基于事 件的网络编程。也就是说,如果每个客户端 sock 都有对应的窗口消息,那我们就可以支持 多个客户端了。基于此方法的 TCP 函数在官方论坛中,已经有高手写成了 UDF 供我们调用, 所以我在此不再详述。只贴一个地址: http://www.autoitscript.com/forum/topic/74325-tcp-udf-event-driven/page__hl__tcp 第二种方法是使用 com,创建 ObjCreate("MSWinsock.Winsock.1")。如果有人使用 vb 编写过网络程序,那就一定知道这个了。关于此种方法,我亦不想多说,如果有想了解的朋 友,可以自己 Google 一番。 第三种方法就是使用数组存放不同的客户端 sock,每一个 sock 对应一个数组元素。因 为 AU3 的特殊性,它不但可以对窗口消息创建响应事件,而且还必须使用一个循环来保持脚 本不退出。如此一来就等于是即可以基本事件,又需要基于过程。 我将以第三种方法为例,讲诉如何编写一个简单的聊天程序。 AutoIt 入门与提高 crossdoor Page 56 4/24/2012 2.1、TCP 聊天服务端 一个聊天程序,显然是需要支持多人聊天才行,也就是说,服务端必须能够同时处理多 个客户端传入的数据。 为了能同时支持多个客户端,我们可以创建一个数组把客户端的 sock 存放起来,然再 遍历 sock 数组,接收数据。 我们来看代码: Dim $MaxUser=10,$Connected=0 Dim $Sock[$MaxUser+1][2] ;sock 信息数组初始化 For $i=1 To $MaxUser $Sock[$i][0]=-1 ;sock $Sock[$i][1]=-1 ;用户名 Next TCPStartup() $MainSocket = TCPListen(@IPAddress1, 33891) If $MainSocket = -1 Then MsgBox(0,"err","端口被占用!") Exit EndIf While 1 ;尝试接受客户端连接 Accept() ;处理客户端的消息 Recv() ;中场休息一下 Sleep(50) WEnd TCPShutdown() Func Recv() For $i=1 To $MaxUser If $Sock[$i][0]<>-1 Then $Recv = TcpRecv ($Sock[$i][0], 100000) If @error Then Disconnect($i) ;数据接收出错,重置 sock 数组下 此索引的元素 If $Recv<>"" Then AutoIt 入门与提高 crossdoor Page 57 4/24/2012 If StringMid($Recv,1,1)="." Then If Not pm($Recv,$Sock[$i][1]) Then SendAll("[%Time%] " & $Sock[$i][1] & " : " & $Recv) Else SendAll("[%Time%] " & $Sock[$i][1] & " : " & $Recv) EndIf Sleep(100) EndIf EndIf Next EndFunc Func pm($Recv,$User);私聊 For $i=1 To $MaxUser If $Sock[$i][0]<>-1 Then If StringMid($Recv,1,StringLen($Sock[$i][1])+1) = "."&$Sock[$i][1] Then TCPSend($Sock[$i][0],"[%Time%] " & $User & " pm You: " & StringMid($Recv,StringLen($Sock[$i][1])+3)) Return True EndIf EndIf Next Return False EndFunc Func Disconnect($i) TCPCloseSocket($Sock[$i][0]) $Sock[$i][0]=-1 SendAll("[%Time%] " & $Sock[$i][1] & " has disconnected.") $Sock[$i][1]=-1 $Connected-=1 EndFunc Func Accept() If $MaxUser>$Connected Then ;客户端连接数小于最大用户数时,接收新的客户端连接 $Accept = TCPAccept($MainSocket) If $Accept <> -1 Then $index = GetIndex();取最靠前的空闲 sock 数组元素索引 $Timer = TimerInit() Do ;做一个 500ms 的循环,接收新连接的用户名 Sleep(20) AutoIt 入门与提高 crossdoor Page 58 4/24/2012 $Recv = TcpRecv($Accept, 1000) Until $Recv <> "" Or TimerDiff ($Timer) >= 500 If $Recv="" Then TCPCloseSocket($Accept);未接收到数据则关闭连接 Else If Check(StringStripWS($Recv,3)) Then ;检测到用户名已经存在的话,发送消息到客户端,然后关闭连接 TcpSend ($Accept, "Error:Username.Exists;") Sleep(200) TCPCloseSocket($Accept) Else ;如果用户名不存在,则保存 sock 和用户名到数组,并广播新用户 加入 $Sock[$index][0]=$Accept $Sock[$index][1]=StringStripWS($Recv,3) $Connected+=1 SendAll("[%Time%] " & $Sock[$index][1] & " has connected.") EndIf EndIf EndIf ElseIf $MaxUser=$Connected Then ;客户端连接数等于最大用户数时,给新连接的客户端发送相应消息,然后断开他 $Accept = TcpAccept ($MainSocket) If $Accept <> -1 Then $Timer = TimerInit() Do Sleep (15) $Recv = TcpRecv($Accept, 1000000) Until $Recv <> '' Or TimerDiff($Timer) >= 500 Sleep(250) TcpSend($Accept, 'Error:Max.Connections;') TcpCloseSocket($Accept) EndIf EndIf EndFunc Func GetIndex() For $i=1 To $MaxUser If $Sock[$i][0]=-1 Then Return $i Next EndFunc Func Check($Name) AutoIt 入门与提高 crossdoor Page 59 4/24/2012 For $i=1 To $MaxUser If $Sock[$i][1]==$Name Then Return True Next Return False EndFunc Func SendAll($Data) For $i=1 To $MaxUser If $Sock[$i][0]<>-1 Then TCPSend($Sock[$i][0],$Data) EndIf Next EndFunc 大概的解释一下: 我们创建了一个二维数组来存放客户端 sock 和用户名,当新客户端接入时,检测客户 端数量是否超出限制,如果超出则发送一个消息给新客户端,然后断开他;如果未超出,则 对用户名进行检测,若用户名已经有人用了,则发送消息给新客户端,然后断开他;若用户 名未使用,则把心客户端的 sock 和用户名存入数组。 对于不同的客户端,我们对 sock 数组进行遍历,接收客户端发送的数据,然后对数据 进行处理。 这段代码并不复杂,如果有朋友一次没看明白,就请仔细再看一遍。我就不再详细解释 了。 说一下使用这种方法的缺点,那就是效率低下,如果客户端数量太多的话,可能会无法 及时响应。不过,对于单线程的脚本在这方面的应用,我们需要追求效率么? 2.2、TCP 聊天客户端 接下来是客户端的编写。我们需要做一个窗口,来输入和查看聊天信息,所以客户端不 能像服务端一样,在后台运行。 具体看代码:(表达能力不行,汗~) #Include #Include #NoTrayIcon Opt('GUIOnEventMode', 1) TCPStartup() Dim $Server = -1 $SetGui= GUICreate ("连接服务端", 180, 100, -1, -1, -1, 128) GUISetOnEvent($GUI_EVENT_CLOSE, '_Exit') AutoIt 入门与提高 crossdoor Page 60 4/24/2012 GUICtrlCreateGroup ('', 5, 0, 170, 94) $IP = GUICtrlCreateInput (@IpAddress1, 12, 13, 100, 21, 1) $Port = GUICtrlCreateInput (33891, 117, 13, 50, 21, 1) $User = GUICtrlCreateInput ("Crossdoor", 12, 39, 156, 21, 1) $Connect = GUICtrlCreateButton ('连接', 12, 66, 100, 20, 0x0001) GUICtrlSetOnEvent ($Connect, 'Start') $Exit = GUICtrlCreateButton ('退出', 117, 66, 50, 20) GUICtrlSetOnEvent ($Exit, '_Exit') GUISetState (@SW_SHOW) WinSetOnTop ($SetGui, '', 1) $GUI = GUICreate ('聊天界面', 375, 275, -1, -1, -1, 128) GUISetOnEvent ($GUI_EVENT_CLOSE, 'Toggle') $History = GUICtrlCreateEdit ('', 0, 1, 375, 203, 2103360 + $ES_MULTILINE) GUICtrlSetBkColor ($History, 0xFFFFFF) GUICtrlSetColor ($History, 0x53BC53) $Chatmsg = GUICtrlCreateEdit ('', 0, 205, 290, 70, 2101248) $Send = GUICtrlCreateButton ('发送', 295, 205, 80, 70) GUICtrlSetOnEvent ($Send, '_send') HotKeySet("^{Enter}", "_send") ;Ctrl+回车发送消息 GUISetState (@SW_HIDE) While 1 Sleep (30) If $Server <> -1 Then $Recv = TcpRecv($Server, 1000000) If @Error Then GUISetState (@SW_HIDE, $GUI) WinSetOnTop ($GUI, '', 0) Sleep (100) MsgBox (48, '提示','失去了与服务端的连接。') Disconnect () EndIf If $Recv = 'Error:Username.Exists;' Then GUISetState (@SW_HIDE, $GUI) WinSetOnTop ($GUI, '', 0) Sleep (100) MsgBox (48, '提示','您的用户名已被使用,请更换后再次连接。') Disconnect () ElseIf $Recv = 'Error:Max.Connections;' Then GUISetState (@SW_HIDE, $GUI) WinSetOnTop ($GUI, '', 0) Sleep (100) AutoIt 入门与提高 crossdoor Page 61 4/24/2012 MsgBox (48, '提示','服务端连接人数已达上限,请稍候再试。') Disconnect () ElseIf $Recv <> '' Then _Log(StringReplace($Recv, '%Time%', @HOUR & ':' & @MIN)) EndIf EndIf WEnd Func _send() $Read = StringReplace(GUICtrlRead($Chatmsg), @CRLF, '') If $Read = '.clear' Then GUICtrlSetData ($History, '') ElseIf $Read = '.disconnect' Or $Read = '.exit' Then Disconnect () Else TcpSend($Server, $Read) EndIf GUICtrlSetData($Chatmsg, '') EndFunc Func Disconnect() GUICtrlSetData ($History, '') TcpCloseSocket ($Server) $Server = -1 GUISetState (@SW_HIDE, $GUI) WinSetOnTop ($GUI, '', 0) GUISetState (@SW_SHOW, $SetGui) WinSetOnTop ($SetGui, '', 1) EndFunc Func Toggle() GUICtrlSetData ($History, '') TcpCloseSocket ($Server) $Server = -1 GUISetState (@SW_HIDE, $GUI) WinSetOnTop ($GUI, '', 0) GUISetState (@SW_SHOW, $SetGui) WinSetOnTop ($SetGui, '', 1) EndFunc Func _Log($Data) GUICtrlSetData ($History, GUICtrlRead ($History) & $Data & @CRLF) _GUICtrlEdit_LineScroll ($History, 0, _GUICtrlEdit_GetLineCount ($History) - 1) AutoIt 入门与提高 crossdoor Page 62 4/24/2012 EndFunc Func Start() If GUICtrlRead ($User) == '' Or GUICtrlRead ($IP) == '' Or GUICtrlRead ($Port) == '' Then Return False $Server = TcpConnect (GUICtrlRead ($IP), GUICtrlRead ($Port)) If $Server = -1 Or @Error Then WinSetOnTop ($SetGui, '', 0) Sleep (100) MsgBox (16, '致命错误','无法连接服务端,请确保连接数据设置正确...') WinSetOnTop ($SetGui, '', 1) Return False EndIf Sleep(150) TcpSend ($Server, GUICtrlRead($User)) GUISetState (@SW_HIDE, $SetGui) WinSetOnTop ($SetGui, '', 0) WinSetTitle($GUI,"","聊天界面 " & GUICtrlRead ($User)) GUISetState (@SW_SHOW, $GUI) WinSetOnTop ($GUI, '', 1) EndFunc Func _Exit() Exit EndFunc Client 部分的代码我没有写注释,因为代码上色太麻烦了,上面一大段代码着色把我弄 得头晕眼花,大家自己仔细看看吧。 运行 Server 端后,再运行 Client 端,可以同时运行多个测试,不过用户名不能一样。 聊天程序默认是群组聊天模式,也就是像 QQ 群一样,发送的消息每个客户端都会看到。 如果想私聊的话,可以在聊天栏输入 .对方名称 聊天内容 例如想跟一个叫 test 的用户发私人消息 hello,则可以在聊天栏输入 .test hello 因为编码的关系,如果发送的聊天消息为中文的话,将会出现错误,聊天消息无法全部 接收。要解决这个问题可以把聊天消息转换后再发送,具体的实现方法我不再举例,大家自 己动手实现吧。 第十三章ADO 数据库编程 1、ADO 简介及 SQL 语言 现在市面上流行的数据库种类繁多,大型的有 Oracle、Db2、Sysbase 和 Sql Server 等, AutoIt 入门与提高 crossdoor Page 63 4/24/2012 小型的有 Access、MySql、FoxPro 等等。 尽管每种数据库现在都提供了 API 接口给编程者调用,但不同的数据库之间,接口也 是不同的。所以一旦需要更换数据库,那之前的代码则将全部报废。 为了能够用同种方法访问不同的数据库,一些软件厂商提出了通用数据库接口标准,如 微软的 ODBC、DAO、RDO、OLEDB 和 ADO 接口。这些接口对各类数据库的驱动程序进 行了封装,我们写应用程序时只需要通过标准的语法访问接口程序,至于不同数据库之间的 区别,将有接口程序面对。 既然有这么多种的接口可供调用,那我们要选择哪种才适合 AU3 呢? ODBC 是以 API 的形式提供了一组接口,因为使用的 API 比较底层,虽然 AU3 也可以 调用 API,但如果真的使用起来将会是非常麻烦的事情。 对于 AU3 这类高级语言来说,使用面向对象的 ADO、RDO 等接口显然是比较理想的。 了解将使用何种接口让 AU3 访问数据库后,我们再来了解一下什么是 SQL 语言。 SQL(结构化查询语言)是数据库系统的通用语言,利用它我们可以使用几乎相同的语 句在不同的数据库中执行相同的动作。 例如不论是在 access、mysql 或 oracle 等数据库中,想要查询某张表的数据,使用的都 是”Select * From 表名”这样的语句。 2、数据库连接与断开 我们以 ADO 为例,来讲述如何连接数据库。既然 ADO 是面向对象的接口,那我们第 一步要做的事情就是建立对象。 $Conn = ObjCreate("ADODB.Connection") ADO 的连接对象建立成功之后,我们使用 open 属性来打开数据库。 $Conn.open("DRIVER={SQL Server};SERVER=(local);UID=sa;PWD=123456;") 这样我们就可以使用 sa 账号,以 123456 作为密码打开(local)地址的数据库了。这里我 是以打开 Sql Server 数据库为例,如果需要操作其他数据库,只需要更改连接字串即可,例 如连接 Access 数据库可以这样: $Conn.open("Driver={Microsoft Access Driver (*.mdb)};dbq=test.mdb;") 成功打开数据库连接之后,要断开也很简单,可以使用 close 方法来操作。 $Conn.close 3、数据库管理 在连接上数据库之后,我们就可以对库中的数据进行管理了。不论是读取、更新、添加、 删除都将不是问题,利用 sql 语句我们可以很轻松的实现这一目的。 3.1、执行 sql 语句 ADO 提供了两种方法供我们执行 sql 语句,分别是 open 和 Execute。一般情况下,执行 添加、删除、更新等语句时,常用 Execute;读取的时候,则用 open。 注意:此处的 open 方法并不是连接对象的,而是记录集的,具体在下节回讲。 我们来看一条添加新数据的示例:(在 test 表中添加新数据) AutoIt 入门与提高 crossdoor Page 64 4/24/2012 $Conn.Execute("insert into test (Name,Tel) values ('张三','13911931773')") 接着我们再看如何更新数据: $Conn.Execute("UPDATE test set Tel = '119' WHERE Name = '张三'") 然后是删除数据: $Conn.Execute("DELETE FROM test WHERE Name = '张三'") 那如果要读取数据怎么办呢?我们先要创建一个记录集对象,然后再使用 open 方法操 作: $RS = ObjCreate("ADODB.Recordset") $RS.ActiveConnection = $Conn $RS.Open("Select * from test") 然后我们就可以取结果集中的数据了,具体方法在下节讲。 使用 open方法除了查询之外,同样可以执行增、改、删操作。使用记录集对象的 AddNew、 Delete、Update 等方法,在此我就不对这种方法举例了,如果有感兴趣的朋友,可以翻阅 msdn,参考 vb 的相关代码即可。 3.2、获取结果集中的数据 再创建了记录集对象,并成功的 Select 之后,我们要如何才能获得查到的数据呢?看代 码: $RS = ObjCreate("ADODB.Recordset") $RS.ActiveConnection = $Conn $RS.Open("Select * from test") If $RS.RecordCount > 0 Then For $i =1 To $RS.RecordCount $RS.Fields(0).Value ;第一条数据中第一个字段的值。 $RS.MoveNext ;游标移到下一条数据 Next EndIf 我们来看一段实例: #include Dim $sServer = '127.0.0.1', $sUsername = 'sa', $sPassword = '1234567' ;三个变量 分别是连接数据库用的地址、账号、密码 $Conn = ObjCreate("ADODB.Connection") $Conn.open("DRIVER={SQL Server};SERVER=" & $sServer & ";UID=" & $sUsername & ";PWD=" & $sPassword & ";") ;连接成功后我们来读取数据 $Conn.Execute("use master") ;首先要指定一个需要操作的库,这里用系统自带的 master 库来操作 $RS = ObjCreate("ADODB.Recordset");创建记录集对象 $RS.ActiveConnection = $Conn;设置记录集的激活链接属性来自$Conn AutoIt 入门与提高 crossdoor Page 65 4/24/2012 $RS.Open("Select * from sysdatabases ORDER BY Name");执行 Sql 语句,这个语句是查 询数据库中所有的库属性,并且按 Name 字段的数据进行排序 Dim $Select_Db[1][1] = [[0]];定义一个数组来存放查询到的数据 Dim $Count = 1;定义一个变量用来记录查询到的数据行数 While Not $RS.eof And Not $RS.bof;当记录指针处于第一条记录和最后一条记录之间时, 执行 while 循环 If @error = 1 Then ExitLoop If $Select_Db[0][0] = 0 Then;当数组二维$Select_Db[0][0]为 0 时,重定义数组的 第二维大小等于记录集查询到的字段数 ReDim $Select_Db[1][$RS.Fields.Count + 1];$RS.Fields.Count 为记录集查询 到的字段数 For $i = 0 To $RS.Fields.Count - 1 $Select_Db[0][$i + 1] = $RS.Fields($i).Name;$RS.Fields($i).Name 为字 段名,把字段名存入数组 Next EndIf ReDim $Select_Db[$Count + 1][$RS.Fields.Count + 1];数组第一维大小加 1,用于 存放数据 $Select_Db[0][0] = $Count;$Select_Db[0][0]存放查询到的数据行数 For $i = 0 To $RS.Fields.Count - 1 $Select_Db[$Count][$i + 1] = $RS.Fields($i).Value;$RS.Fields($i).Value 字段数据 Next $Count += 1;行数加 1 $RS.movenext;将游标移到下一条数据上 WEnd $RS.Close;关闭记录集对象 _ArrayDisplay($Select_Db, "数据库所有库属性");显示数组 后记 这篇教程从 10 年 6 月开始写,中间有很长一段时间处于停滞状态,实在惭愧。幸好还 未曾忘记,趁着过年放假,抽时间又开始写起来,总算是完成了。 记录一下完成的时间,2011-02-07(正月初五)。 总算是完成了,也算是放下了一件事情,当然更重要的是,希望能帮到看过此教程的朋 友。虽然我的水平有限的很,但即已斗胆写了此文,就再无退路,只能硬着头皮继续撑了。 如果教程中有错误,请一定要通知于我,我的联系方式在教程开头有写。 AutoIt 入门与提高 crossdoor Page 66 4/24/2012 1、一些算法 如果是编程科班出生的朋友,那你的 c 语言老师一定跟你说过,程序的灵魂是算法。 什么是算法?解决一个问题采取的方法和步骤就称为算法。 虽然 AU3 是脚本语言,但对于算法还是有必要做一些练习。在此我列举一些简单的题 目,大家可以在看我的答案之前,先自己试写一段代码看看。 判断一个数是否是完数:(完数,即完美数,一个数如果恰好等于除它本身外的因子之 和,这个数就称为完数。例如 6=1+2+3.(6 的因子是 1,2,3)) Func wanshu($a) Dim $intSum=0 For $j = 1 To Int($a/2) If Mod($a,$j) = 0 Then $intSum+=$j Next If $intSum=$a Then Return True Else Return False EndIf EndFunc 判断一个数是否为素数: Func prime($a) If $a<=1 Then Return False;1 和 0 不是素数也不是合数 For $i=2 To Round(Sqrt($a)) If Mod($a,$i) = 0 Then Return False Next Return True EndFunc 求两个数的最大公约数: Func gcd_dg($a,$b);最大公约数,递归实现 If $b=0 Then Return $a Else Return gcd_dg($b,Mod($a,$b)) EndIf EndFunc Func gcd($a,$b);最大公约数 For $i=$a To 1 Step -1 If Mod($a,$i)=0 And Mod($b,$i)=0 Then Return $i Next AutoIt 入门与提高 crossdoor Page 67 4/24/2012 EndFunc 求两个数的最小公倍数: Func lcm($a,$b);最小公倍数 Return $a*$b/gcd($a,$b) ; //最小公倍数等于两数之积除以最大公约数 EndFunc 计算斐波那契数列某项的值(斐波那契数列:1,1,2,3,5,8,13,21,34,55,„„ ): Func Calculate($n) ;递归实现 If $n <= 1 Then Return $n Else return Calculate($n - 1) + Calculate($n - 2) EndIf EndFunc 大牛生小牛问题,刚出生的小牛,长四年就可以每年生小牛一头。现有一头刚出生的小 牛,算出 20 年后共有多少头牛。(如果年数长一点,例如 200 年,我们将非常清楚的看到两 种算法不同的效率。) Func getCountEasy($y) ;递推实现 Dim $a[5] = [1,1,1,1,0] If $y<4 Then Return 1 Else For $i=4 To $y $a[4] = $a[3] + $a[0] $a[0] = $a[1] $a[1] = $a[2] $a[2] = $a[3] $a[3] = $a[4] Next return $a[4] EndIf EndFunc Func getCount($y) ;递归实现 If $y < 4 Then Return 1 Else return getCount($y - 1) + getCount($y - 4) EndIf EndFunc
还剩66页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

cross523

贡献于2014-09-05

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf