YStudio Y++

进程类

Y++ / YStudio 开发文档 · 一点滴

类对象支持库(不是扁平命令)。路径:支持库 → 扩展类库 → 进程类。

类型名必须写完整「进程类」。声明时自动创建,一般无需再调 创建*。

与扁平「进程操作」不同:本类侧重 启动外部程序 + 重定向标准输入/输出/错误,适合抓命令回显、做简易终端壳。枚举/结束任意 PID、取图标等仍用 进程操作(扁平)。

硬约束

  1. 类型名必须写「进程类」。
  2. 方法调用必须带括号。
  3. 启动 的第一参是 可执行文件路径或系统能解析的程序名(如 cmd.exe、powershell.exe、完整 .exe 路径);第二参是 参数字符串(不含程序名本身)。
  4. 默认 隐藏窗口 并把 stdout/stderr 接到管道;读写文本按 UTF-8。
  5. 没有「一条命令的回应已结束」协议:只有「进程已退出」或「管道里又写出了若干字节」可感知。一次性命令用 读全部();常驻壳优先 数据到达(&回调)(同 TCP),也可用 读等待()/接收()。
  6. 管道重定向 不是 Windows 真控制台(ConPTY):无彩色/光标/cls 等完整终端行为。

能执行什么

启动(程序路径, [参数], [显示窗口]) 本质是 CreateProcess:能启动本机任意可执行文件(权限与路径允许的前提下),不限于 CMD。

目标 示例
CMD 跑一条就退出 p.启动("cmd.exe", "/c dir") 再 p.读全部()
CMD 保持会话(简易壳) p.启动("cmd.exe", "/K"),用 写 + 数据到达(或 读等待)
Windows PowerShell 一条 p.启动("powershell.exe", "-NoProfile -Command \"Get-Date\"")
PowerShell 保持会话 p.启动("powershell.exe", "-NoLogo -NoExit"),同上 写 + 数据到达
PowerShell 7+(若已安装) p.启动("pwsh.exe", "-NoLogo -Command \"…\"")
任意 .exe p.启动("C:\\Tools\\app.exe", "--flag 1")
显示其窗口(不抓输出场景) p.启动("notepad.exe", "", 真)

说明:

  • 不能把整句 "cmd /c dir" 塞进第一参当「命令行」;程序与参数要拆开(或使用你自备的包装 exe)。
  • 批处理:p.启动("cmd.exe", "/c C:\\a.bat")。
  • 脚本:p.启动("powershell.exe", "-File C:\\a.ps1")。
  • 中文乱码:先让子进程用 UTF-8(如 CMD 参数里加 chcp 65001>nul & …),或优先 PowerShell。

启动 参数怎么写

进程类自身(三个形参)

形参 类型 含义
程序路径 文本 CreateProcess 的可执行文件。写 cmd.exe / powershell.exe / pwsh.exe / 完整 .exe 路径均可(系统能解析即可)。
参数 文本,可选 不含程序名 的参数串,原样传给该程序。空文本=无参数。
显示窗口 逻辑,可选,默认假 假=隐藏并适合抓输出;真=显示该程序窗口(记事本等)。

进程类不解析 /c、-Command 等——那些是 cmd / PowerShell 自己的开关,写在第二参里。

常用:cmd.exe 第二参

第二参写法 行为
/c 命令 执行一条(可含 &&、&、重定向)后 退出 → 配 读全部()
/c chcp 65001>nul & 命令 同上,并尽量用 UTF-8 代码页,减少中文乱码
/c "路径含空格\a.bat" 参数 跑批处理;路径有空格时自行按 cmd 规则加引号
/K 或 /K chcp 65001>nul 执行后 不退出(交互壳)→ 配 数据到达 + 写(行+"\n")
/K 命令 先执行命令再保持会话

说明:/c、/K 后面整段都是 cmd 命令行语法(内部命令如 dir/echo/cd,或再调别的 exe)。进程类只负责把第二参交给 cmd.exe。

常用:powershell.exe / pwsh.exe 第二参

第二参写法 行为
-NoProfile -Command "表达式" 执行表达式后退出 → 读全部()
-NoProfile -File "C:\a.ps1" 执行脚本后退出
-NoLogo -NoExit 进入交互会话不退出 → 数据到达 + 写
-NoLogo -NoExit -Command "…" 先跑一句再保持会话

引号:Y++ 字符串里写 PowerShell 时,内层双引号需转义,例如
"-NoProfile -Command \"Get-Date\""。

不支持 / 易混

误解 实际
第一参写 cmd /c dir 应 启动("cmd.exe", "/c dir")
以为进程类内置「所有 cmd 内部命令」API 没有;内部命令通过启动 cmd.exe 执行
交互壳用 读全部() 等「本条结束」 壳不退出则永不等到;改 数据到达 / 读等待,或每次 /c 新进程
当真 ConPTY 终端用 仅管道重定向,无彩色/cls 等完整控制台

两种用法(务必分清)

A. 一次性命令(推荐:执行 → 等结束 → 读回)

子进程 跑完会退出。读全部() 会等到退出(或超时),这就是「等待回应完成再读」。

进程类 p
如果 (p.启动("cmd.exe", "/c echo hi") == 假) {
    输出(p.取错误())
} 否则 {
    文本框1.内容 = p.读全部()   // 阻塞到进程结束;勿在超长任务里卡死 UI
}

PowerShell 同理:

进程类 p
如果 (p.启动("powershell.exe", "-NoProfile -Command \"Get-Process | Select-Object -First 3\"")) {
    文本框1.内容 = p.读全部(15000)
}

界面事件里若命令可能很久,请自己加超时:读全部(毫秒),或放到线程里跑。

B. 常驻壳(推荐:数据到达 实时收,同 TCP)

cmd /K、powershell -NoExit 不会在每条命令后退出,因此:

  • 不能用 读全部() 等「这条命令结束」(会一直等到整个 shell 退出)。
  • 可以像 TCP 一样实时收:注册 数据到达(&回调),有标准输出即在 UI 线程 回调。
步骤 方法
注册回调 数据到达(&收到) / 可选 进程退出(&退了)(须在启动前)
启动并保持 启动("cmd.exe", "/K …")
发送 写(行 + "\n")
接收 回调里拿 文本 数据(不必定时器)
进程类 shell

函数 收到(文本 数据) {
    输出框.加入文本(数据)
}

函数 退了(整数 码) {
    输出("壳退出 code=" + 到文本(码))
}

函数 主窗口.创建完毕 () {
    shell.数据到达(&收到)
    shell.进程退出(&退了)
    如果 (shell.启动("cmd.exe", "/K chcp 65001>nul") == 假) {
        输出框.文本 = shell.取错误()
    }
}

函数 发送一行 (文本 行) {
    shell.写(行 + "\n")
}

备选:读等待(超时) / 接收(超时)(阻塞等到有输出;UI 线程勿长时间堵,可放线程池)。定时器轮询 读() 也可用,但不是必须。

说明:无论事件还是 读等待,都只表示「又有若干字节」,不表示「本条命令的全部输出已结束」。若要严格「一条命令整段回显」,请改用模式 A:每次 /c + 读全部()。

若你坚持「每条命令:启动 → 等结束 → 读回」,不要保持会话,改为每次:

启动("cmd.exe", "/c " + 用户输入) + 读全部()。

------|------| | 启动并保持 | 启动("cmd.exe", "/K …") | | 发送 | 写(行 + "\n") | | 接收(阻塞等到有输出或超时) | 读等待(超时) 或同义名 接收(超时) |

界面程序请把监听放在线程池,避免卡住 UI:

进程类 shell
整数 g_听池
文本 g_待显
逻辑 g_听停

函数 听循环 () {
    当 (g_听停 == 假 且 shell.是否运行中()) {
        文本 t = shell.读等待(200)   // 有输出就返回;200ms 无数据则空转一圈
        如果 (t != "") {
            g_待显 = g_待显 + t
        }
    }
}

函数 主窗口.创建完毕 () {
    如果 (shell.启动("cmd.exe", "/K chcp 65001>nul") == 假) {
        输出框.文本 = shell.取错误()
        返回 ()
    }
    g_听停 = 假
    g_听池 = 创建线程池(1)
    投递任务(g_听池, &听循环)
    // 另用短定时器或空闲回调把 g_待显 刷到输出框(仅搬文本,不轮询进程)
}

函数 发送一行 (文本 行) {
    shell.写(行 + "\n")   // 发送后,听循环里的 读等待 会收到回显
}

说明:读等待 在「有任意输出」时返回,仍不表示「本条命令的全部输出已结束」(shell 不会为此发结束信号)。若要严格「一条命令整段回显」,请改用模式 A:每次 /c + 读全部()。

定时器轮询 读() 仍然可用,只是实现监听的一种方式,不是必须。

若你坚持「每条命令:启动 → 等结束 → 读回」,不要保持会话,改为每次:

启动("cmd.exe", "/c " + 用户输入) + 读全部()。


快速开始

进程类 p
如果 (p.启动("cmd.exe", "/c echo hi")) {
    输出(p.读全部())
} 否则 {
    输出(p.取错误())
}

进程类/创建

共 1 条。

创建进程

  • 签名:通用 创建进程()
  • 说明:创建一个进程会话。一般直接写「进程类 p」即可。用于启动外部程序并读取标准输出(与支持库「进程操作」无关)。

进程类/方法

共 19 条。

启动

  • 签名:逻辑 启动(文本 程序路径, [文本 参数], [逻辑 显示窗口=假])
  • 说明:启动外部程序;成功返回真。默认隐藏窗口并捕获输出。可启动 cmd / PowerShell / 任意 exe(见上文「能执行什么」)。
  • 参数一│文本 程序路径: 可执行文件(如 cmd.exe、powershell.exe、完整路径)。
  • 参数二│[文本 参数=”“]: 命令行参数(不含程序名)。
  • 参数三│[逻辑 显示窗口=假]: 真=显示窗口。

置标准输入

  • 签名:逻辑 置标准输入(管道类 子进程读端)
  • 说明:把管道类的读端交给子进程标准输入。须在启动前调用。设置后请用管道父写端发送,勿再用 写()。
  • 参数一│管道类 子进程读端: 匿名管道读端;父进程用对应写端发送

置标准输出

  • 签名:逻辑 置标准输出(管道类 子进程写端)
  • 说明:把管道类的写端交给子进程标准输出。须在启动前调用。设置后 读()/读全部() 不再含这段输出,请从父读端接收。
  • 参数一│管道类 子进程写端: 匿名管道写端;父进程用对应读端接收

置标准错误

  • 签名:逻辑 置标准错误(管道类 子进程写端)
  • 说明:把管道类的写端交给子进程标准错误。优先于默认「错误并入标准输出」。
  • 参数一│管道类 子进程写端

清除标准句柄

  • 签名:无 清除标准句柄()
  • 说明:取消已设置的标准输入/输出/错误管道,恢复内置重定向。

写

  • 签名:逻辑 写(文本 内容)
  • 说明:向标准输入写入文本(UTF-8)。交互壳每行末尾须带 \n。
  • 参数一│文本 内容: 写入内容。

数据到达

  • 签名:无 数据到达(子程序指针 回调)
  • 说明:注册「标准输出有新数据」回调(同 TCP类)。须在启动前注册;传 0 清除。UI 线程触发;形参 文本 数据。
  • 参数一│子程序指针 回调: 形如 函数 收到(文本 数据)。

进程退出

  • 签名:无 进程退出(子程序指针 回调)
  • 说明:注册「子进程已退出」回调;须在启动前;传 0 清除。UI 线程;形参 整数 退出码。

读

  • 签名:文本 读()
  • 说明:读取 当前已有 的标准输出(不阻塞)。无数据立刻返回空文本。

读等待

  • 签名:文本 读等待([整数 超时毫秒=-1], [整数 最大字节=0])
  • 说明:阻塞直到有标准输出、进程退出、或超时。有数据即返回(不必等进程结束)。UI 勿长时间堵在界面线程。
  • 参数一│[整数 超时毫秒=-1]: <0 无限等;0 等同 读();>0 最多等这么久
  • 参数二│[整数 最大字节=0]: ≤0 不限制

接收

  • 签名:文本 接收([整数 超时毫秒=-1], [整数 最大字节=0])
  • 说明:同 读等待()(发送/接收风格别名)。

读全部

  • 签名:文本 读全部([整数 超时毫秒=-1])
  • 说明:等待进程结束(或超时)后返回全部标准输出。仅适用于会退出的一次性进程。
  • 参数一│[整数 超时毫秒=-1]: ≤0=无限等待。

等待

  • 签名:逻辑 等待([整数 超时毫秒=-1])
  • 说明:等待子进程退出;返回真=已退出。
  • 参数一│[整数 超时毫秒=-1]: ≤0=无限等待。

结束

  • 签名:逻辑 结束()
  • 说明:强制结束子进程。

是否运行中

  • 签名:逻辑 是否运行中()
  • 说明:子进程是否仍在运行。

取进程ID

  • 签名:整数 取进程ID()
  • 说明:取子进程 PID。

取退出码

  • 签名:整数 取退出码()
  • 说明:取退出码;仍在运行时返回 -1。

取错误

  • 签名:文本 取错误()
  • 说明:取最后一次失败原因。

重置

  • 签名:无 重置()
  • 说明:结束并清理会话。

易错对照

错误 正确
类型写成「进程」 必须「进程类」
读全部输出() 方法名是 读全部()
交互壳里调 读全部() 等「这条命令结束」 进程不退出会一直等;改用 数据到达 / 读等待 或每次 /c 新进程
以为必须用定时器才能回显 否;优先 数据到达(&回调)(同 TCP);读等待/定时器只是备选
与扁平「运行/执行」混淆 要抓 stdout/stdin → 用本类;只要弹程序 → 扁平亦可
第一参写整句 cmd /c dir 拆成 启动("cmd.exe", "/c dir")
界面里对长任务无限 读全部() 加超时,或放到线程

相关

进程(扁平) · 管道类 · 定时器类 · 多线程