简介:AutoIt V3.3.6.1绿色汉化特别版是一份面向Windows自动化脚本初学者与运维、测试人员的免安装工具包,围绕BASIC风格脚本编写与GUI自动化展开,可用于模拟键鼠、窗口与控件操作、执行程序和读写注册表,解决重复性批处理与桌面流程自动化问题。压缩包共194个文件,大小约6.44MB,其中129个au3脚本与函数库是核心示例和调用素材,另有11个exe工具、lua与properties配置、api提示文件及帮助文档,兼顾编写、调试和查阅。已有871人学习下载。读者可据此快速熟悉AutoIt v3的语法、标准库调用和SciTE编辑环境配置,借助API提示与示例脚本理解窗口控件交互、批处理及ActiveX/DLL扩展用法,并可将脚本编译为独立运行文件,适合边学边改、搭建自己的自动化小工具。
1. 解压即用的 AutoIt:3MB 目录里装着什么,能替你点掉哪些活
内网那台只能远程进的机器,没有部署接口,也没有管理员配合,你要做的却是把一套老客户端按七步向导点完,中间点错一步就得从头再来。这种「只有界面、没有 API」的活,AutoIt(脚本圈里常写成 AU3)是最省事的一把刀:语法贴着 Basic 的路子走,几行Run加WinWait把窗口等出来,再用ControlClick把按钮按下去,不用装运行时,也不用配编译环境。V3.3.6.1 绿色汉化特别版给的是一个解压就能用的完整工具链——解释器、编译器、编辑器、窗口信息抓取工具和中文帮助全在一个目录里,拷到 U 盘换台机器照样跑。它适合运维、测试、交付工程师处理重复的 Windows 界面操作,不适合拿去做 Web 自动化或跨平台任务。
2. 绿色包落地:目录分工、编码设置与第一个能跑起来的脚本
拿到压缩包,很多人第一反应是双击 SciTE 就开始写,结果脚本能编辑不能跑、跑了又乱码。绿色版省掉的是安装过程,不是环境判断。先把目录里谁负责什么弄清楚,后面所有排错都能少绕一半路。
2.1 解压后的目录分工与三个隐式前提
正常的绿色汉化包解压出来大致是这样一套结构,不同打包者会合并或改名,但核心几件不会少:
| 路径 | 作用 | 什么时候会用到 |
|---|---|---|
Au3.exe | 脚本解释器 | 直接运行.au3,调试阶段用 |
Au3Info.exe/Au3Info_x64.exe | 窗口信息抓取 | 抓窗口标题、控件 ID、坐标 |
Aut2Exe/Aut2Exe.exe | 编译器 | 把脚本打成独立 exe |
SciTE/SciTE.exe | 编辑器 | 写脚本、F5 运行、Ctrl+F7 编译 |
Include/ | UDF 扩展库 | #include <File.au3>这类引用 |
Examples/ | 官方示例 | 抄窗口、GUI、文件操作的写法 |
Icons/ | 图标资源 | 编译时给 exe 换图标 |
中文帮助.chm | 函数手册 | 查参数,比搜网页快 |
先把关键组件点一遍,确认包是完整的,别等到写完脚本才发现缺编译器:
@echo off :: 在解压目录根部执行,检查核心组件是否齐全 for %%F in (Au3.exe Au3Info.exe Aut2Exe\Aut2Exe.exe SciTE\SciTE.exe) do ( if exist "%%~fF" (echo [OK] %%F) else (echo [MISS] %%F) ) if exist Include\File.au3 (echo [OK] Include) else (echo [MISS] Include) pause这段批处理用的是for的文件遍历,%%~fF把相对路径展开成绝对路径再判断,比一行行if exist好维护。跑完看到[MISS]就别急着往下走,压缩包多半被杀软啃掉了Aut2Exe,重新解压并把目录加进白名单。
三个隐式前提得提前说清。第一,AutoIt 必须有桌面会话,脚本要跟窗口打交道,扔进没有交互桌面的服务里跑,WinWait永远返回 0。第二,解释器和编译产物都是原生程序,不依赖 .NET、不依赖 VC 运行库,这正是它能塞进 U 盘到处跑的原因,但也意味着它不会帮你做任何环境适配。第三,绿色版不写注册表,.au3文件不会自动关联,运行方式要么把脚本拖到Au3.exe上,要么命令行Au3.exe your.au3,要么在 SciTE 里按 F5。用哪个都行,我一般在调试期用 SciTE,交付前才编译。
2.2 汉化界面与脚本编码:乱码从来不是玄学
汉化包做的事主要是替换 SciTE 的语言文件和附带中文帮助。如果打开编辑器菜单还是英文,八成不是汉化失败,而是你启动的 SciTE 读的是另一份配置——SciTE 会优先加载同目录下的locale.properties,找不到才回落到内置英文。检查这个文件里有没有中文语言项,比重新下一份包快得多。
真正容易翻车的是脚本文件的编码。AutoIt3 的解析器对 Unicode 的支持是有条件的:带 BOM 的 UTF-8 能认,不带 BOM 的中文注释和中文路径就可能变成一堆问号。我踩过的坑很典型——脚本在本地跑得好好的,拷到另一台机器上,路径里的中文目录名全乱了,FileExists一律返回 False。后来统一成两条规矩:
; 规则一:路径不写死,全部用宏拼 Local $sWorkDir = @ScriptDir & "\data" Local $sLogFile = $sWorkDir & "\run.log" If Not FileExists($sWorkDir) Then DirCreate($sWorkDir) ; 规则二:需要输出中文时显式指定编码 Local $hFile = FileOpen($sLogFile, 2 + 128) ; 2=覆盖写, 128=UTF-8 FileWriteLine($hFile, "开始时间:" & @YEAR & "-" & @MON & "-" & @MDAY) FileClose($hFile)@ScriptDir是脚本自身所在目录的宏,编译成 exe 后指向 exe 所在目录,这样打包分发不会因为盘符变化而失效。FileOpen的第二个参数是标志位组合,2表示写入模式并清空原内容,128表示以 UTF-8 写出,不加这个标志,中文写进日志就是乱码。至于脚本文件本身,最省事的做法是保存为 ANSI(GBK),中文注释和中文常量都不会出问题;如果团队要求统一 UTF-8,那就保存成带 BOM 的 UTF-8,别用「UTF-8 无 BOM」。
提示:改完编码后一定重新打开一次文件确认,某些编辑器会在保存时悄悄去掉 BOM,你以为改了其实没改。
2.3 第一个能跑的脚本:Run、WinWait、ControlSetText 三件套
别一上来就写复杂流程,先用记事本验证「启动—等待—输入—关闭」这条链路通不通。链路通了,后面换成任何业务软件都是同一套骨架。
; demo_01.au3 —— 最小可运行链路 Opt("WinTitleMatchMode", 2) ; 2 = 标题包含匹配,最宽松也最好用 Opt("WinWaitDelay", 100) ; 每次窗口操作后等 100ms,给 UI 喘口气 Run("notepad.exe") ; 等待窗口出现,第二个参数是窗口文本,空串表示不校验 Local $hWin = WinWait("[CLASS:Notepad]", "", 10) If $hWin = 0 Then MsgBox(16, "失败", "10 秒内没有等到记事本窗口") Exit 1 EndIf WinActivate($hWin) WinWaitActive($hWin, "", 5) ; 直接给编辑框塞文本,不依赖光标位置 ControlSetText($hWin, "", "Edit1", "hello autoit 3.3.6.1") ; 关闭窗口并处理「是否保存」对话框 WinClose($hWin) Local $hDlg = WinWait("[CLASS:#32770]", "", 5) If $hDlg <> 0 Then ControlClick($hDlg, "", "Button2") ; Button2 一般是「不保存」 EndIf逐项说一下参数的含义,这些是后面所有脚本的地基。Opt("WinTitleMatchMode", 2)把标题匹配模式切成「包含」,也就是说写"记事本"就能命中「无标题 - 记事本」,代价是误命中概率上升;如果要做精确控制,改成 3 或者用高级模式(第 3 章展开)。WinWait的第三个参数是超时秒数,超时返回 0,这是唯一可靠的失败信号,所以每个WinWait后面都应该跟一个If $hWin = 0判断,而不是假设窗口一定出现。ControlSetText的第二个参数是窗口文本,一般留空;第三个参数"Edit1"是控件的 ClassNameNN,不是固定值,换一个软件就得用Au3Info重新抓。
[CLASS:#32770]是 Windows 标准对话框的窗口类名,保存确认、错误提示、UAC 之外的多数弹窗都是这个类,值得记下来。Button2同理需要现场抓,不同软件的按钮顺序不一样,「不保存」不一定是 Button2。
运行方式三选一:把demo_01.au3拖到Au3.exe图标上;命令行执行Au3.exe demo_01.au3;或者用 SciTE 打开后按 F5。跑通之后你会看到一个窗口闪一下就没了,这是正常的,脚本执行速度快于人的感知。
3. 窗口与控件定位:Au3Info 抓不到控件时怎么退一步
脚本写不下去,九成卡在「找不到窗口」和「点不到控件」这两件事上。AutoIt 的定位体系分三层:窗口靠标题/类名匹配,控件靠标识符寻址,动作靠消息投递或键鼠模拟。搞清楚每层的边界,才知道什么时候该换下一层。
3.1 变量与数据类型:Variant 的方便和代价
AutoIt 只有一种变量类型,叫 Variant,声明时不写类型,赋值时自动判断。这带来了写脚本的爽快,也埋了比较运算的坑——"10" > "9"在某些上下文里会按字符串比较得出 False。
Local $iRetry = 0 ; 数字 Local $sName = "setup" ; 字符串 Local $bDone = False ; 布尔 Local $aSteps[4] = ["解压", "安装", "配置", "校验"] For $i = 0 To UBound($aSteps) - 1 ConsoleWrite($i & " -> " & $aSteps[$i] & @CRLF) Next ; 显式转成数字再比较,避免字符串比较的坑 Local $sNum = "10" If Number($sNum) > 9 Then ConsoleWrite("数值比较正确" & @CRLF) ; 字符串拼接用 & ,不是 + Local $sPath = @ScriptDir & "\logs\" & $sName & "_" & @YEAR & @MON & @MDAY & ".log"Local声明函数内局部变量,脚本顶层用Global或直接赋值(默认是全局,容易污染,建议一律显式写Local/Global)。UBound($aSteps)返回数组长度,减 1 才是合法下标,这个-1忘了写就会数组越界。ConsoleWrite只在 SciTE 输出窗口或者编译成控制台程序时可见,如果你的脚本是双击运行的 GUI 程序,调试信息要改成写文件。@CRLF、@YEAR、@ScriptDir这类以@开头的是宏,等价于系统常量,用起来比函数调用轻。
需要传引用的场景用ByRef,比如让函数回填一个错误信息:
Func TryOpen($sPath, ByRef $sErr) If FileExists($sPath) Then Run($sPath) Return True EndIf $sErr = "文件不存在:" & $sPath Return False EndFuncByRef意味着函数内修改会作用到调用方的变量上,这是 AutoIt 里唯一的「多返回值」手段,用多了会难读,控制在错误信息、统计计数这类场景即可。
3.2 窗口匹配:五种模式和一个诊断组合
WinTitleMatchMode有 1 到 4(外加负数变体),实际常用的就前四种:
| 值 | 含义 | 适用场景 | 风险 |
|---|---|---|---|
| 1 | 从标题开头匹配 | 标题固定的老软件 | 标题加前缀就失效 |
| 2 | 标题包含子串 | 标题带文件名、带版本号 | 可能误命中同类窗口 |
| 3 | 标题完全相等 | 精确定位 | 标题一变就崩 |
| 4 | 高级模式,支持 CLASS/REGEXPTITLE | 多语言、标题动态变化 | 写法复杂,调试成本高 |
我默认用 2,遇到「多个窗口标题都含同一段文字」时切到 4,用窗口类名或正则把范围收窄:
Opt("WinTitleMatchMode", 4) ; 按窗口类名找,标题随便变都不影响 Local $h1 = WinWait("[CLASS:Notepad]", "", 10) ; 按正则找,适合标题里带时间戳、带文件名的场景 Local $h2 = WinWait("[REGEXPTITLE:(?i)安装.*向导]", "", 10) ; 类名 + 实例序号,同进程开了多个窗口时用 Local $h3 = WinGetHandle("[CLASS:CabinetWClass; INSTANCE:2]")写不出来的时候别猜,用三个函数把现场信息打出来,这是我排错的标准动作:
Local $h = WinGetHandle("[ACTIVE]") ; 当前活动窗口 ConsoleWrite("标题: " & WinGetTitle($h) & @CRLF) ConsoleWrite("类名: " & WinGetClassList($h) & @CRLF) ConsoleWrite("文本长度: " & StringLen(WinGetText($h)) & @CRLF) ConsoleWrite("坐标: " & WinGetPos($h)[0] & "," & WinGetPos($h)[1] & @CRLF)WinGetClassList返回的是窗口内所有控件的类名清单,一行一个,配合Au3Info看能快速判断这个界面到底是不是标准控件。如果返回的清单很短或者全是Static,说明这窗口大概率是自绘的,标准控件接口点不动,得看下一节。
3.3 控件标识的三种写法和选择顺序
Au3Info是这套工具里最有价值的一个 exe,把准星拖到目标控件上,右侧会显示Class、Instance、ID、Text四组信息。三种寻址方式各有适用面:
ClassNameNN,形如Edit1、Button2,表示「该类的第 N 个实例」。它对界面小改版不敏感,是最常用的写法。
ControlID,一个整数,是控件在窗口内的序号。它的优点是短,缺点是极不稳定,同一个软件换个版本,ID 全变,所以我基本不用。
句柄,用ControlGetHandle拿到控件的 HWND。它最精确,但窗口重建后句柄失效,适合在一次会话内多次操作同一个控件。
Local $hWin = WinWait("[CLASS:Notepad]", "", 5) ; 写法一:ClassNameNN ControlSetText($hWin, "", "Edit1", "第一段") ; 写法二:先取句柄,后续复用 Local $hEdit = ControlGetHandle($hWin, "", "Edit1") If $hEdit = 0 Then MsgBox(16, "失败", "没找到 Edit1,界面可能改版了") Exit 1 EndIf ControlSetText($hWin, "", $hEdit, "第二段") ; 读回内容做校验,别假设写进去就一定成功 Local $sBack = ControlGetText($hWin, "", $hEdit) If $sBack <> "第二段" Then ConsoleWrite("写入未生效" & @CRLF)ControlGetHandle返回 0 是最常见的失败信号,一定要判断,否则后面的操作全部静默失败,脚本看起来「跑了但没效果」。ControlGetText回读校验这一步很多人省掉,结果在权限不足或者控件只读的情况下,ControlSetText返回成功但内容没变,白排查半天。
如果控件是自绘的,上面三招全失效,退路有两条:一是ControlClick传空字符串加坐标偏移,也就是在窗口坐标系里点某个相对位置;二是MouseClick用屏幕绝对坐标。两条路的共同问题是受分辨率和 DPI 影响,第 5 章会细说。能用控件接口就不要用坐标,这是原则。
4. 编译成 exe:Aut2Exe 参数逐个拆与 UDF 引入路径
脚本在自己机器上跑通,只是完成了一半。交付给别人的时候,对方机器上没有这套绿色目录,你也不可能让业务方先去装个解释器。编译成独立 exe 是必走的一步,而这一步的参数选错,产物的行为会完全不一样。
4.1 Aut2Exe 命令行参数与压缩级别的取舍
SciTE 里按 Ctrl+F7 会调起图形界面的编译向导,但要做批量构建或者写进流水线,还是命令行靠谱:
:: 基础编译:输入脚本,输出 exe,指定图标 Aut2Exe\Aut2Exe.exe /in demo_01.au3 /out demo_01.exe /icon Icons\app.ico :: 控制台版本:带 ConsoleWrite 输出,调试期用 Aut2Exe\Aut2Exe.exe /in demo_01.au3 /out demo_01_dbg.exe /console :: 最高压缩,交付期用 Aut2Exe\Aut2Exe.exe /in demo_01.au3 /out demo_01.exe /comp 4/in和/out是必填项,路径带空格要加引号。/icon只影响资源图标,不影响运行。/console会生成一个带黑框的控制台程序,ConsoleWrite的输出能看到,代价是运行时多一个窗口,正式交付时要去掉。/comp是压缩级别,取值 0 到 4,数字越大产物越小、启动时的解压开销越大;我们这边一般用 4,因为交付物走内网分发,体积比那几十毫秒启动时间重要。还有个/pass可以给 exe 加启动密码,内部工具用不上,容易给自己找麻烦。
如果目标机器是 64 位且需要调用 64 位系统接口,绿色包里通常会带Aut2Exe_x64.exe,用哪个编译器决定了产物的位数。这一点经常被忽略:脚本里如果引用了WinAPI相关的 UDF,32 位和 64 位的结构体对齐方式不同,32 位下正常的代码换 64 位编译可能直接崩。
4.2 #include 与 #pragma:路径是绿色版最容易断的地方
#include有两种写法,语义完全不同:
#include <File.au3> ; 尖括号:从 Include 目录找 #include "lib\mylib.au3" ; 双引号:从脚本所在目录找尖括号的搜索路径依赖编译器自己推断 Include 目录的位置,绿色版换台机器、换个盘符之后,这个推断就可能失败,报「找不到包含文件」。解决办法有两个:要么把整个Include目录跟着脚本一起走,保持相对位置不变;要么在 SciTE 的配置文件里把openpath指到绝对路径。我一般选前者,因为交付物要能自包含。
#pragma是编译期指令,写在脚本头部,能覆盖命令行的一部分设置:
#pragma compile(Icon, "Icons\app.ico") #pragma compile(Out, "build\setup_tool.exe") #pragma compile(Compression, 4) #RequireAdmin ; 请求管理员权限 Opt("WinTitleMatchMode", 2)#RequireAdmin这条很关键,它会在 exe 的清单里写入requireAdministrator,双击时直接触发提权。很多脚本在开发机上手跑正常,交付后「点不动任何按钮」,就是因为目标窗口是高权限进程,而脚本是普通权限,第 5 章会展开这个坑。
4.3 单文件分发:能带走的和带不走的
编译出来的 exe 是自包含的,目标机器不需要装任何东西,但有几样东西它不会自己带:脚本里引用的外部文件、图标以外的资源、依赖的系统组件。所以打包前要做一次依赖体检:
; 打包自检:列出脚本依赖的外部文件 Local $aDeps[3] = ["config.ini", "driver\setup.exe", "lib\helper.dll"] For $i = 0 To UBound($aDeps) - 1 If FileExists(@ScriptDir & "\" & $aDeps[$i]) Then ConsoleWrite("[OK] " & $aDeps[$i] & @CRLF) Else ConsoleWrite("[MISS] " & $aDeps[$i] & @CRLF) EndIf Next这段检查看起来简陋,但比事后在客户现场发现少了个 dll 强得多。实际分发时我会把 exe 和这些依赖放在同一个目录,打成压缩包,解压即用,路径全部用@ScriptDir拼,这样对方放哪个盘都能跑。
还有一个绕不开的问题:AutoIt 编译产物被杀软误报的概率不低。原因是它的产物结构是「解释器 + 加密脚本」,这个特征和某些恶意程序的重打包方式撞了。这不是你能靠改代码解决的,可行的做法是走内网白名单、给 exe 做代码签名证书(企业内部分发一般有这个条件),或者在文档里写清楚让运维加信任。指望换个压缩级别就能躲过去,是浪费时间。
5. 避坑排查:昨天能跑今天不动,八成是这五处
脚本类故障的特点是不报错。窗口没等到,函数返回 0,你如果不判断,整个流程就走空,日志里干干净净,看起来像什么都没发生。下面五条是我这几年返工次数最多的。
5.1 手动双击正常,计划任务里全失败
现象:同一个 exe,手动双击跑得好好的,挂到计划任务里,日志只有第一行,后面全没了。
原因:计划任务默认跑在非交互会话里,没有可见桌面,WinWait等不到任何窗口,一路超时。如果任务配置成「不管用户是否登录都运行」,这个问题必现。
解决:把任务的运行账户改成「只在用户登录时运行」,并勾选「使用最高权限运行」。如果必须无人值守,就不能用窗口自动化这套思路,得换成命令行参数或者接口调用。这条没有绕过去的技术手段,属于方案选型错误。
5.2 目标窗口是管理员权限,脚本点不动
现象:ControlClick返回成功,界面毫无反应。ControlGetText能读到内容,但写不进去。
原因:Windows 的 UIPI 机制禁止低完整性级别进程向高完整性级别进程发送窗口消息。你的脚本是普通权限,目标程序是管理员权限,消息被系统直接丢弃,AutoIt 这一侧看到的是「调用完成」,因为它拿不到对方的拒绝反馈。
解决:脚本头部加#RequireAdmin重新编译,或者用管理员身份启动解释器调试。注意加了这个之后,每次运行都会弹 UAC 确认框,无人值守场景要配合计划任务的高权限配置。
5.3 高分屏上坐标全偏,控件坐标和屏幕坐标混用
现象:在 100% 缩放的开发机上正常,到了 125% 缩放的笔记本上,MouseClick点到了按钮外面。
原因:AutoIt 3.3.6.1 这个年代的程序不声明 DPI 感知,系统会对它做坐标虚拟化;而MouseClick用的是物理屏幕坐标,ControlClick带偏移量用的是窗口客户区坐标,两套坐标系混在一起必然错位。
解决:优先用ControlClick的控件寻址,完全绕开坐标;实在要用坐标,就在目标机器上实测标定,别用开发机的数值。另外可以调用系统接口把进程标成 DPI 感知,让坐标系统一,但这会影响整个进程的界面渲染,属于较大的改动。
5.4 中文路径和中文标题,本地好好的换机就乱
现象:本地跑通,拷到另一台机器上,FileExists对所有中文路径返回 False。
原因:脚本文件编码和目标机器的系统区域设置不一致。UTF-8 无 BOM 的脚本里写中文常量,解析器按 ANSI 读,字节序列就变了。中文版 Windows 和其它语言版本的默认代码页不同,问题只在跨机器时暴露。
解决:脚本统一保存为 ANSI(GBK)或带 BOM 的 UTF-8,路径一律用@ScriptDir拼接,不在代码里硬编码中文目录名。交付前在目标语言环境的机器上跑一次冒烟测试,这一步别省。
5.5 编译产物第一次运行特别慢,或者直接被拦
现象:双击 exe 后要等三五秒才有反应,或者干脆被安全软件静默隔离,什么提示都没有。
原因:/comp 4的产物需要先解压自身体积,首次运行还有系统的文件扫描开销;被隔离则是因为打包特征触发了启发式规则。
解决:启动慢的话把压缩级别降到 2 或 3,用体积换启动速度;被隔离就走白名单流程,别反复改代码试图绕过。另外编译后第一次运行前,先确认杀软日志,很多时候你以为的「脚本有 bug」,实际是文件已经不在了。
6. 把 WinWait 换成带重试的等待函数:一次改完,后面都省心
WinWait的超时值是固定的,网络慢一点、机器卡一点就超时,然后把整个流程带偏。我在一个批量部署脚本上吃过大亏:单机测试 3 秒能出来的窗口,在批量机器上因为同时跑着杀毒扫描,15 秒才出来,脚本早就放弃退出了。后来统一改成「短超时 + 多次重试 + 全程落日志」的写法,这类问题再没出现过。
; 带重试的等待窗口,返回句柄,失败返回 0 Func WaitWindow($sTitle, $iTimeout = 5, $iRetry = 6, $iInterval = 1000) For $i = 1 To $iRetry Local $h = WinWait($sTitle, "", $iTimeout) If $h <> 0 Then LogWrite("第 " & $i & " 次尝试命中窗口:" & $sTitle) Return $h EndIf LogWrite("第 " & $i & " 次等待超时,休眠 " & $iInterval & "ms 重试") Sleep($iInterval) Next LogWrite("重试 " & $iRetry & " 次仍未等到:" & $sTitle) Return 0 EndFunc Func LogWrite($sMsg) Local $sLine = @YEAR & "-" & @MON & "-" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & " " & $sMsg ConsoleWrite($sLine & @CRLF) FileWriteLine(@ScriptDir & "\run.log", $sLine) EndFunc ; 调用方只需要判断一次 Local $hMain = WaitWindow("[CLASS:Notepad]", 5, 6, 1000) If $hMain = 0 Then MsgBox(16, "失败", "主窗口未出现,详见 run.log") Exit 1 EndIf这里的取舍是:单次WinWait给短超时(5 秒),外层套 6 次重试,两次之间休眠 1 秒,最坏情况总等待 36 秒,最好的情况 1 秒内返回。相比直接把超时设成 60 秒,好处是每次失败都有日志,能看出到底是「一直没出现」还是「出现了但标题不匹配」——前者是环境问题,后者是匹配规则问题,排查方向完全不同。Sleep的间隔不要设太短,窗口创建本身需要时间,100ms 级别的轮询只是白白占 CPU。
日志函数里我特意同时写控制台和文件,调试期看控制台,交付后看文件,@ScriptDir保证日志跟着 exe 走,不会写到系统目录里找不到。这个习惯是从一次客户现场事故之后养成的:那次脚本卡在第三步,没有任何输出,我只能远程连上去,一步步手工重放,花了两个小时才定位到一个弹窗被安全软件换成了自家样式。从那以后我每次交付前都强制跑一遍带日志的版本,确认每一行时序都对得上,才敢打包。希望帮到你。
本文还有配套的精品资源,点击获取