Run / RunWait

运行外部程序. 与 Run 不同, RunWait 将等到程序完成后才继续.

Run Target , WorkingDir, Options, &OutputVarPID
ExitCode := 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. 否则, 请指定以下一个或多个选项:

Max: 最大化运行

Min: 最小化运行

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

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

&OutputVarPID

类型: VarRef

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

在 Run 函数检索到 PID 之后, 由进程创建的窗口可能还不存在. 要等待至少一个窗口被创建, 请使用 WinWait "ahk_pid " OutputVarPID.

返回值

类型: 整数

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

错误处理

如果无法启动 Target, 则会抛出异常(即, 显示一个错误窗口) 并退出当前线程, 除非错误被 Try/Catch 语句捕获. 例如:

try
    Run "NonExistingFile"
catch
    MsgBox "File does not exist."

内置变量 A_LastError 被设置为操作系统的 GetLastError() 函数的结果.

Target 的解释

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

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

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

备注

当通过 ComSpec(cmd.exe) 运行程序时 -- 也许是因为你需要重定向程序的输入或输出 -- 如果可执行文件的路径或名称中包含空格, 那么整个字符串应该用一对外置引号来括起. 在下面的例子中, 外部的引号用黄色高亮显示:

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

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

Run 还可以打开指定的 CLSID 文件夹. 它们中的大多数可以通过使用 shell: 前缀打开. 有些不用前缀也能打开. 例如:

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

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

Verb(动词) 描述
*verb 任何系统定义或自定义的动词. 例如: Run "*Compile " A_ScriptFullPath. *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 处于等待状态时, 可以通过热键, 自定义菜单项计时器启动新的线程.

以管理员身份运行

对于可执行文件, *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) 对话框, 或者由于其他原因启动失败, 脚本就会直接退出.

如果在调用 ExitApp 之前启动新的脚本实例, 使用 /restart 可以确保不显示单一实例的提示弹窗.

如果禁用了 UAC, *RunAs 将不会提权启动进程. 在命令行中检查 /restart 可以确保在这种情况下脚本不会进入失控循环. 注意 /restart 是一个内置的开关, 所以其不包括在命令行参数数组中.

可以根据脚本的需要修改上面的例子:

AutoHotkey 的安装程序为 .ahk 文件注册 RunAs 动词, 这允许 Run "*RunAs script.ahk" 以管理员权限启动脚本.

RunAs, 进程函数, Exit, CLSID 列表, DllCall

示例

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

Run "notepad"

Run 能够启动 URLs:

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

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."

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

try Run("ReadMe.doc", , "Max")
if A_LastError
    MsgBox "The document could not be launched."

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

RunWait A_ComSpec " /c dir C:\ >>C:\DirTest.txt", , "Min"
Run "C:\DirTest.txt"
Run "properties C:\DirTest.txt"
Persistent ; 保持脚本不退出, 否则属性对话框将关闭.

Run 能够启动 CLSID:

下面打开回收站.

Run "::{645FF040-5081-101B-9F08-00AA002F954E}"

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

Run "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"

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

Run A_ComSpec "/c dir /b > C:\list.txt && type C:\list.txt && pause"

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

MsgBox RunWaitOne("dir " A_ScriptDir)

MsgBox RunWaitMany("
(
echo Put your commands here,
echo each one will be run,
echo and you'll get the output.
)")

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

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

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

ExecScript(Script, Wait:=true)
{
    shell := ComObject("WScript.Shell")
    exec := shell.Exec("AutoHotkey.exe /ErrorStdOut *")
    exec.StdIn.Write(Script)
    exec.StdIn.Close()
    if Wait
        return exec.StdOut.ReadAll()
}

; 示例:
ib := InputBox("Enter an expression to evaluate as a new script.",,, 'Ord("*")')
if ib.result = "Cancel"
    return
result := ExecScript('FileAppend ' ib.value ', "*"')
MsgBox "Result: " result