Run / RunWait

运行外部程序. 与 Run 不同, RunWait 会等待程序结束才继续往后执行.

Run, Target , WorkingDir, Options, OutputVarPID
RunWait, Target , WorkingDir, Options, OutputVarPID

参数

Target

要运行的文档, URL, 可执行文件(.exe, .com, .bat, 等等), 快捷方式(.lnk), CLSID系统动词(请参阅备注). 如果 Target 是本地文件且没有指定路径, 文件的位置通常取决于文件的类型和其他条件. 有关详情, 请参阅 Target 的解释.

要传递参数, 请让它们紧跟在程序或文档名的后面, 如下所示:

Run, MyProgram.exe Param1 Param2

如果程序/文档名称或参数包含空格, 则把它们括在双引号中是最可靠的(虽然它在某些情况下没有括在双引号中也能工作), 如下所示:

Run, "My Program.exe" "param with spaces"
WorkingDir

如果为空或省略, 则使用脚本自己的工作目录(A_WorkingDir). 否则, 指定新进程要使用的初始工作目录. 这通常也会影响 Target 中的相对路径, 但是命令行参数的解释取决于目标程序. 即使它包含空格, 也不要括在双引号中.

Options

如果为空或省略, 正常启动 Target, 并且当 Target 不能启动时, 将显示警告对话框. 否则, 请指定以下一个或多个选项:

Max: 最大化运行

Min: 最小化运行

Hide: 隐藏运行(不能和上面任意一个选项组合使用)

注意: 一些程序(例如 Calc.exe) 不会遵循请求的启动状态, 因此对于它们 Max/Min/Hide 没有效果.

UseErrorLevel: 可以单独指定 UseErrorLevel 或和上面的其中一个单词组合使用(把它和其他单词用空格分隔). 如果运行失败, 则使用此选项会跳过警告对话框, 设置 ErrorLevel 为单词 ERROR, 并让当前线程继续执行. 如果运行成功, 则 RunWait 会把 ErrorLevel 设置为程序的退出代码, 而 Run 把它设置为 0.

指定 UseErrorLevel 时, 内置变量 A_LastError 被设置为操作系统 GetLastError() 函数返回的结果. A_LastError 是一个介于 0 和 4294967295 之间的数字(总是为十进制而非十六进制). 零(0) 意味着成功, 但其他任何数字意味着运行失败. 每个数字对应一种特定的错误状态(要获取整个列表, 请在 www.microsoft.com 中搜索 "system error codes"). 像 ErrorLevel 一样, A_LastError 是与每个线程关联的设置, 即由其他线程产生的中断不会改变它. 然而, A_LastError 还可由 DllCall() 设置.

OutputVarPID

如果省略, 将不会存储对应的值. 否则, 请指定用来存储新运行程序的唯一进程 ID(PID)的输出变量的名称. 如果无法确定则 PID 被设置为空, 这经常发生在运行系统动词, 文档或快捷方式而不是直接的可执行文件时. RunWait 也支持此参数, 不过它获取的 OutputVarPID 必须在另一个线程中进行检查(否则, 这个 PID 将是无效的, 因为 RunWait 后的代码执行时相应的进程已经终止了).

在 Run 命令获取 PID, 此进程要创建的窗口可能还不存在. 要等待它至少创建一个窗口, 请使用 WinWait ahk_pid %OutputVarPID%.

错误处理

[v1.1.04+]: 此命令失败时会抛出异常. 想了解更多信息, 请参阅运行时错误.

Run: 只有在使用了 UseErrorLevel 时才会设置 ErrorLevel, 在这种情况下失败时 ErrorLevel 被设置为单词 ERROR 而成功时为 0.

RunWait: 设置 ErrorLevel 为程序的退出代码(32 位的有符号整数). 如果使用了 UseErrorLevel 且运行失败, 则把它设置为单词 ERROR.

Target 的解释

Run/RunWait 本身不解释命令行参数或搜索目标文件. 相反, 它尝试执行目标, 如下所示:

如果 Target 指定了名称, 但没有指定目录, 如果它是集成的("已知的"), 例如包含在 PATH 的文件夹之一中, 系统将搜索并启动该文件. 确切的搜索顺序取决于是否调用 CreateProcess 和/或 ShellExecuteEx. 如果调用 CreateProcess, 应用程序目录(其中包含 AutoHotkey 解释器或编译的脚本) 优先于 WorkingDir 指定的工作目录. 要避免这种情况, 请指定目录; 例如 .\program.exe.

当尝试 ShellExecuteEx 时, Target 被解释如下:

备注

与 Run 不同, RunWait 会等待 Target 关闭或退出, 此时 ErrorLevel 被设置为程序的退出代码(32 位的有符号整数). 一些程序即使它们仍在运行但看起来立即返回了; 这些程序启动了另外的进程.

如果 Target 包含逗号, 则必须对它们进行转义, 如同下面例子中的三次高亮标识:

Run rundll32.exe shell32.dll`,Control_RunDLL desk.cpl`,`, 3  ; 打开控制面板 > 显示属性 > 设置

通过 ComSpec(cmd.exe) 运行程序时(也许由于您需要重定向程序的输入和输出), 如果可执行程序的路径或名称包含空格, 则整个字符串必须括在一对外部的引号中. 在下面的例子中, 外部引号以黄色高亮显示:

Run %comSpec% /c ""C:\My Utility.exe" "param 1" "second param" >"C:\My File.txt""

如果无法启动 目标, 将显示包含此错误信息的窗口, 并结束当前线程, 除非在第 3 个参数中指定字符串 UseErrorLevel, 或者使用 Try/Catch 语句进行捕获.

Target 为准确路径时可轻微提升性能, 例如使用 Run, C:\Windows\Notepad.exe "C:\My Documents\Test.txt" 代替 Run, C:\My Documents\Test.txt.

特殊的 CLSID 可以通过 Run 打开. 它们中的大多数可以通过使用 shell: 前缀打开. 有些不用前缀也能打开. 例如:

Run shell:::{D20EA4E1-3957-11D2-A40B-0C5020524153} ; Windows Tools.
Run ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}  ; 打开此电脑(以前是我的电脑或计算机).
Run ::{645FF040-5081-101B-9F08-00AA002F954E}  ; 打开回收站.

系统动词对应于资源管理器中文件右键菜单的可用操作. 如果启动文件时没有指定动词, 则使用那种文件类型的默认动词(通常为 "打开"). 如果指定了, 则动词后应该跟着默认文件名. 目前支持下列动词:

动词 描述
*动词 [AHK_L 57+]: 任何系统定义或自定义的动词. 例如: Run *Compile %A_ScriptFullPath%. 在 Windows Vista 和更高版本中, 可以使用 *RunAs 动词代替 以管理员身份运行 右键菜单.
properties(属性)

显示指定文件的资源管理器属性窗口. 例如: Run, properties "C:\My File.txt"

注意: 当脚本终止时属性窗口会自动关闭. 要避免这种情况, 请使用 WinWait 等待窗口出现, 然后使用 WinWaitClose 等待用户关闭它.

find(查找) 在指定的文件夹中打开资源管理器的搜索助理或查找文件窗口实例. 例如: Run, find D:\
explore(浏览) 打开指定文件夹的资源管理器实例. 例如: Run, explore %A_ProgramFiles%.
edit(编辑) 打开指定的文件进行编辑. 如果指定的文件类型没有关联 "edit" 动作时, 它不起作用. 例如: Run, edit "C:\My File.txt"
open 打开指定的文件(一般不需要, 因为这对于大多数文件类型是默认的动作). 例如: Run, open "My File.txt".
print 使用关联的应用程序打印指定的文件, 如果有. 例如: Run, print "My File.txt"

当 RunWait 处于等待状态时, 可以通过热键, 自定义菜单项计时器启动新的线程.

以管理员权限运行(或是启动)[AHK_L 57+]

对于可执行文件, 命令 *RunAs 相当于右键菜单的 以管理员权限运行 命令. 例如, 以下代码尝试以管理员权限启动自身:

full_command_line := DllCall("GetCommandLine", "str")

if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
{
    try
    {
        if A_IsCompiled
            Run *RunAs "%A_ScriptFullPath%" /restart
        else
            Run *RunAs "%A_AhkPath%" /restart "%A_ScriptFullPath%"
    }
    ExitApp
}

MsgBox A_IsAdmin: %A_IsAdmin%`nCommand line: %full_command_line%

如果用户取消了 User Account Control (UAC) 对话框或者是其他原因启动失败, 脚本就会直接退出.

在使用了 /restart 启动参数, 且启动新进程实例的语句在 ExitApp 之前的时候, 需要确保 single instance(单一实例) 提示弹窗没有关闭.

如果 UAC 禁止了, *RunAs 将不提权启动进程. 在命令行检查 /restart 以防脚本进入无限重启的循环之中. 注意 /restart 为内置开关, 所以它没有列入命令行参数之中.

可以修改该示例以适应脚本的需要:

[v1.0.92.01+]: 如果 UAC 开启, AutoHotkey 安装程序为 .ahk 文件注册 RunAs 动作, 这允许 Run *RunAs script.ahk 默认以管理员权限启动脚本.

RunAs, Process, Exit, CLSID 列表, DllCall()

示例

Run 能够从任何目录下启动 Windows 系统程序. 注意, 可执行文件的扩展名如 .exe 可以省略.

Run, notepad

Run 能够启动 URL:

以下是在用户的默认网页浏览器中打开一个互联网地址.

Run, https://www.google.com

以下打开默认的电子邮件应用程序并填写收件人.

Run, mailto:someone@somedomain.com

下面的操作和上面一样, 加上主题和正文.

Run, mailto:someone@somedomain.com?subject=This is the subject line&body=This is the message body's text.

在最大化应用程序中打开文档, 并在失败时显示一个自定义的错误信息.

Run, ReadMe.doc, , Max UseErrorLevel
if (ErrorLevel = "ERROR")
    MsgBox The document could not be launched.

在最小化状态下运行 dir 命令, 并将输出结果存储在文本文件中. 之后, 文本文件及其属性对话框将被打开.

#Persistent
RunWait, %ComSpec% /c dir C:\ >>C:\DirTest.txt, , Min
Run, C:\DirTest.txt
Run, properties C:\DirTest.txt

Run 能够启动 CLSID:

下面打开回收站.

Run, ::{645ff040-5081-101b-9f08-00aa002f954e}

下面打开此电脑(以前是我的电脑或计算机).

Run, ::{20d04fe0-3aea-1069-a2d8-08002b30309d}

要连续运行多个命令, 请每个命令之间使用 "&&".

Run, %ComSpec% /c dir /b > C:\list.txt && type C:\list.txt && pause

以下函数可以用来运行一个命令并检索其输出, 或者一次性运行多个命令并检索其输出. 有关 WshShell 对象, 请参阅 Microsoft Docs.

MsgBox % RunWaitOne("dir " A_ScriptDir)

; ...或一次执行多条命令并获取其输出:
MsgBox % RunWaitMany("
(
echo 在这填入命令,
echo 会执行所有命令,
echo 并获取输出.
)")

RunWaitOne(command) {
    shell := ComObjCreate("WScript.Shell")
    ; 通过 cmd.exe 执行单条命令
    exec := shell.Exec(ComSpec " /C " command)
    ; 读取并返回命令的输出
    return exec.StdOut.ReadAll()
}

RunWaitMany(commands) {
    shell := ComObjCreate("WScript.Shell")
    ; 打开 cmd.exe 禁用命令显示
    exec := shell.Exec(ComSpec " /Q /K echo off")
    ; 发送并执行命令, 使用新行分隔
    exec.StdIn.WriteLine(commands "`nexit")  ; 保证执行完毕后退出!
    ; 读取并返回所有命令的输出
    return exec.StdOut.ReadAll()
}

在新的 AutoHotkey 进程中执行给定的代码.

ExecScript(Script, Wait:=true)
{
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec("AutoHotkey.exe /ErrorStdOut *")   ; 需根据实际情况修改 AutoHotkey.exe
    exec.StdIn.Write(script)
    exec.StdIn.Close()
    if Wait
        return exec.StdOut.ReadAll()
}

; 示例:
InputBox expr,, 输入一个表达式, 将用一个新的脚本来计算.,,,,,,, Asc("*")
result := ExecScript("FileAppend % (" expr "), *")
MsgBox % "Result: " result