# Process

对进程执行下列的其中一个操作: 检查进程是否存在; 改变进程的优先级; 关闭进程; 等待进程关闭.

    Process, SubCommand , PIDOrName, Value

## 参数

SubCommand, Value
  他们互相依赖, 其用法描述如下.
PIDOrName

  此参数可以为下面的数字(PID) 或进程名称. 它还可以被留空来改变脚本自己的优先级.

  PID: 进程 ID, 唯一标识一个特定进程的数字(只有在此进程的生存期这个数字才有意义). 新运行的进程的 PID 可以通过 Run 命令获取. 同样的, 窗口的 PID 可以通过 WinGet 获取. Process 命令自身也可以获取 PID.

  Name: 进程的名称, 通常和它的可执行文件名相同(不带路径), 例如 notepad.exe 或 winword.exe. 一个名称可能匹配多个正在运行的进程, 但只会对第一个进程进行操作. 这个名称不区分大小写.

## 子命令

对于 SubCommand, 指定以下命令之一:

- Exist: 检查指定的进程是否存在.
- Close: 强制关闭第一个匹配进程.
- List: 还没有实现.
- Priority: 更改首个匹配进程的优先级.
- Wait: 等待指定的进程存在.
- WaitClose: 等待所有匹配进程关闭.

### Exist

检查指定的进程是否存在.

    Process, Exist , PIDOrName

如果存在一个匹配的进程则设置 ErrorLevel 为此进程 ID(PID), 否则为 0. 如果 PIDOrName 参数为空, 则获取脚本自身的 PID. 检索脚本的另一种单行的方法是 `PID := DllCall("GetCurrentProcessId")`.

### Close

强制关闭第一个匹配的进程.

    Process, Close , PIDOrName

如果成功终止一个匹配的进程, 则设置 ErrorLevel 为此进程原来的进程 ID(PID). 否则(没有找到匹配的进程或在终止匹配进程的过程中出现了问题) 它被设置为 0. 因为进程会被突然终止(可能在一个关键点中断它的工作或导致窗口中未保存数据的丢失), 因此只有当无法使用 WinClose 作用于它的窗口来关闭相应进程时才使用这种方法.

### List

虽然这个子命令还没有得到支持, 示例 #4 演示了如何通过 DllCall() 检索进程列表.

### Priority

更改第一个匹配进程的优先级.

    Process, Priority, PIDOrName, Level

更改首个匹配进程的优先级(如同 Windows 任务管理器中看到的那样)为 Level, 并设置 ErrorLevel 为它的进程 ID(PID). 如果 PIDOrName 参数为空, 则改变脚本自己的优先级. 如果没有找到匹配的进程或在改变匹配进程的优先级过程中出现了问题, 则 ErrorLevel 被设置为 0.

Level 应该为下列字母或单词的其中一个: L(或 Low)(低), B(或 BelowNormal)(低于标准), N(或 Normal)(普通), A(或 AboveNormal)(高于标准), H(或 High)(高), R(或 Realtime)(实时). 注: 任何不是设计为 Realtime 优先级运行的进程如果设置为这个优先级可能会降低系统的稳定性.

### Wait

等待指定的进程存在.

    Process, Wait, PIDOrName , Timeout

Timeout 指定秒数(可以为小数) 以便在超时前等待. 如果忽略 Timeout, 子命令将无限期地等待. 如果发现匹配进程, ErrorLevel 设置为其进程ID(PID). 如果子命令超时, ErrorLevel 设置为 0.

### WaitClose

等待所有匹配进程关闭.

    Process, WaitClose, PIDOrName , Timeout

Timeout 指定秒数(可以为小数) 以便在超时前等待. 如果忽略 Timeout, 子命令将无限期地等待. 如果所有的匹配进程都关闭了, ErrorLevel 设置为 0. 如果子命令超时, ErrorLevel 设置为仍然存在的首个匹配进程的 ID(PID).

## ErrorLevel

如果子命令失败或超时, ErrorLevel 设置为 0. 否则, 它将被设置为进程 ID(PID). 有关详细信息, 请参阅上面的子命令.

## 备注

对于 Wait 和 WaitClose 子命令: 每隔 100 毫秒检查目标进程一次; 当条件满足时, 子命令停止等待. 换句话说, 它不会等待超时过期, 而是像上面描述的那样立即设置 ErrorLevel, 然后继续执行脚本. 此外, 当子命令处于等待状态时, 可以通过热键, 自定义菜单或计时器启动新线程.

## 相关

Run, WinGet, WinClose, WinKill, WinWait, WinWaitClose, WinExist()

## 示例

启动记事本, 将其优先级设置为 "High" 并显示当前的 PID.

    Run notepad.exe,,, NewPID
    Process, Priority, %NewPID%, High
    MsgBox The newly launched Notepad's PID is %NewPID%.

对于记事本的出现, 最多等待 5.5 秒. 如果记事本出现在此秒数内, 则其优先级设置为 "Low", 并且脚本自身的优先级设置为 "High". 之后, 记事本将被关闭, 如果在 5 秒内未关闭, 将显示一个消息框.

    Process, Wait, notepad.exe, 5.5
    NewPID := ErrorLevel  ; 由于 ErrorLevel 会经常发生改变, 所以要立即保存这个值.
    if not NewPID
    {
        MsgBox The specified process did not appear within 5.5 seconds.
        return
    }
    ; 否则:
    MsgBox A matching process has appeared (Process ID is %NewPID%).
    Process, Priority, %NewPID%, Low
    Process, Priority,, High  ; 把脚本自己设置为高优先级.
    WinClose Untitled - Notepad
    Process, WaitClose, %NewPID%, 5
    if ErrorLevel ; PID 仍然存在.
        MsgBox The process did not close within 5 seconds.

按下热键改变活动窗口进程优先级.

    #z:: ; Win+Z 热键
    WinGet, active_pid, PID, A
    WinGetTitle, active_title, A
    Gui, 5:Add, Text,, Press ESCAPE to cancel, or double-click a new`npriority level for the following window:`n%active_title%
    Gui, 5:Add, ListBox, vMyListBox gMyListBox r5, Normal|High|Low|BelowNormal|AboveNormal
    Gui, 5:Add, Button, default, OK
    Gui, 5:Show,, Set Priority
    return

    5GuiEscape:
    5GuiClose:
    Gui, Destroy
    return

    MyListBox:
    if (A_GuiEvent != "DoubleClick")
        return
    ; 否则直接进入下一个标签:
    5ButtonOK:
    GuiControlGet, MyListBox
    Gui, Destroy
    Process, Priority, %active_pid%, %MyListBox%
    if ErrorLevel
        MsgBox Success: Its priority was changed to "%MyListBox%".
    else
        MsgBox Error: Its priority could not be changed to "%MyListBox%".
    return

使用 DllCall() 获取正在运行的进程列表然后显示在消息框中.

    d := "  |  "  ; 字符串分隔符
    s := 4096  ; 缓存和数组的大小(4 KB)

    Process, Exist  ; 设置 ErrorLevel 为这个正在运行脚本的 PID.
    ; 使用 PROCESS_QUERY_INFORMATION(0x0400) 获取此脚本的句柄:
    h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel, "Ptr")
    ; 打开此进程的可调整的访问令牌(TOKEN_ADJUST_PRIVILEGES = 32):
    DllCall("Advapi32.dll\OpenProcessToken", "Ptr", h, "UInt", 32, "PtrP", t)
    VarSetCapacity(ti, 16, 0)  ; 特权结构
    NumPut(1, ti, 0, "UInt")  ; 特权数组中的一个条目...
    ; 获取调试特权的本地唯一标识符:
    DllCall("Advapi32.dll\LookupPrivilegeValue", "Ptr", 0, "Str", "SeDebugPrivilege", "Int64P", luid)
    NumPut(luid, ti, 4, "Int64")
    NumPut(2, ti, 12, "UInt")  ; 启用这个特权: SE_PRIVILEGE_ENABLED = 2
    ; 使用新的访问令牌更新此进程的特权:
    r := DllCall("Advapi32.dll\AdjustTokenPrivileges", "Ptr", t, "Int", false, "Ptr", &ti, "UInt", 0, "Ptr", 0, "Ptr", 0)
    DllCall("CloseHandle", "Ptr", t)  ; 关闭此访问令牌句柄以节约内存.
    DllCall("CloseHandle", "Ptr", h)  ; 关闭此进程句柄以节约内存.

    hModule := DllCall("LoadLibrary", "Str", "Psapi.dll")  ; 通过预加载来提升性能.
    s := VarSetCapacity(a, s)  ; 接收进程列表标识符的数组:
    c := 0  ; 用于进程标识符的计数器
    DllCall("Psapi.dll\EnumProcesses", "Ptr", &a, "UInt", s, "UIntP", r)
    Loop, % r // 4  ; 把数组解析为 DWORD(32 位) 的标识符:
    {
        id := NumGet(a, A_Index * 4, "UInt")
       ; 打开进程: PROCESS_VM_READ(0x0010) | PROCESS_QUERY_INFORMATION(0x0400)
        h := DllCall("OpenProcess", "UInt", 0x0010 | 0x0400, "Int", false, "UInt", id, "Ptr")
        if !h
            continue
        VarSetCapacity(n, s, 0)  ; 接收模块基础名称的缓存:
        e := DllCall("Psapi.dll\GetModuleBaseName", "Ptr", h, "Ptr", 0, "Str", n, "UInt", A_IsUnicode ? s//2 : s)
        if !e    ;  用于 64 位进程在 32 位模式时的回退方法:
            if e := DllCall("Psapi.dll\GetProcessImageFileName", "Ptr", h, "Str", n, "UInt", A_IsUnicode ? s//2 : s)
                SplitPath n, n
        DllCall("CloseHandle", "Ptr", h)  ; 关闭进程句柄以节约内存
        if (n && e)  ; 如果映像不是空的, 则添加到列表:
            l .= n . d, c++
    }
    DllCall("FreeLibrary", "Ptr", hModule)  ; 卸载库来释放内存.
    ;Sort, l, C  ; 取消注释这行来按字母顺序对列表进行排序.
    MsgBox, 0, %c% Processes, %l%

使用 COM 获取正在运行的进程列表.

    Gui, Add, ListView, x2 y0 w400 h500, Process Name|Command Line
    for proc in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
        LV_Add("", proc.Name, proc.CommandLine)
    Gui, Show,, Process List

    ; Win32_Process: http://msdn.microsoft.com/en-us/library/aa394372.aspx

dftg