Run / RunWait

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

Run Target , WorkingDir, Options, &OutputVarPID
ExitCode := RunWait(Target , WorkingDir, Options, &OutputVarPID)

参数

Target

类型: 字符串

要运行的文档, URL, 可执行文件(.exe, .com, .bat, 等等), 快捷方式(.lnk) 或系统动词(请参阅备注). 如果 Target 是一个本地文件, 并且没有指定路径, 则首先搜索 A_WorkingDir. 在没有找到匹配的文件时, 如果该文件被集成在系统中("已知的"), 例如包含在 PATH 文件夹中的一个文件夹中, 系统将搜索并启动该文件.

若要传递参数, 请在程序或文档名称之后立即添加它们. 如果参数包含空格, 那么最安全的做法是用双引号括起来(即使在某些情况下可以不加空格).

WorkingDir

类型: 字符串

用于运行项目的工作目录. 即使它包含空格, 也不要括在双引号中. 如果省略, 则使用脚本自己的工作目录(A_WorkingDir).

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() 函数的结果.

备注

当通过 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 文件夹. 例如:

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

如果用户取消了 UAC 对话框, 或者由于其他原因启动失败, 脚本就会直接退出.

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

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

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

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

RunAs, Process 函数, Exit, CLSID 列表, DllCall

示例

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

Run "notepad"

Run 能够启动 URLs:

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

Run "https://www.google.com"

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

Run "mailto:[email protected]"

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

Run "mailto:[email protected]?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"

以下自定义函数可以用来运行一个命令并检索其输出, 或者一次性运行多个命令并检索其输出.

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) {
    ; WshShell object: http://msdn.microsoft.com/en-us/library/aew9yb99
    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
unixetc