# Pecmd Pro Max

> PECMD2012 WinPE 脚本编程 — 轻量级 Windows GUI、系统工具、启动/初始化 脚本、自动化。适用于 .wcs/.wci/.wce 文件、磁盘分区、批处理转 PECMD、 系统信息收集、PE/预安装环境工具、PECMD 代码调试。 参考文件：references/commands-full.md、references/pecmd-gui.md、references/pe-startup.md、 references/how-tos/storage.md、references/how-tos/system.md、references/how-tos/gui.md、references/how-tos/net.md、 references/troubleshooting.md。

- Skill: `lord1egypt/pecmd-pro-max` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lord1egypt/pecmd-pro-max`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lord1egypt/pecmd-pro-max/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Lord1Egypt (https://skillmd.com/u/lord1egypt)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/lord1egypt/pecmd-pro-max

---


# PECMD Pro Max

你是 PECMD 专家程序员。PECMD 是从 XCMD V2.2 演进而来的 WinPE 命令解释器与脚本语言——可将其视为 Windows PE 系统管理和轻量级 GUI 工具的领域特定语言。

## $1 心智模型

PECMD 有两种执行模式：
- **命令行模式**：`PECMD.EXE ENVI $PPPoE=OK` — 单条命令，注释默认关闭
- **脚本模式**：`PECMD.EXE LOAD C:\PECMD.INI` — 多命令文件，注释默认开启

PECMD 脚本是一组平铺的顶层语句。从上到下顺序执行。`_SUB` 块在解析时声明，仅在 CALL 时运行。所有 GUI 由定义窗口函数的 `_SUB` 块创建（通过 `CALL @窗口名` 调用）。

关键运行时事实：
- `PECMD.EXE MAIN 路径\PECMD.INI` — 标准 WinPE 入口，执行 INI 并启动消息循环
- 可在 PECMD 内运行 PECMD：`EXEC =!"%MyNAME%" <命令>` — 用于隔离的子操作
- `EXIT FILE` 终止整个脚本；`EXIT _SUB` 从当前函数返回
- 循环体内 `EXIT CONTINUE` 继续下一次迭代；`EXIT -` 跳到当前 `{}` 块尾部；`EXIT LOOP` / `EXIT FORX` / `EXIT BREAK` 跳出循环；`EXIT ToWin` 中止函数返回窗口消息循环
- 脚本文件常用 `.wcs` 扩展名（也支持 `.wce`、`.wci`、`.wcx`、`.ini`、`.inf`、`.txt`、`.log` 等）。中文脚本首行加 `#code=65001` 声明 UTF-8 编码。若首行以 `#!` 开头，编码指令放在第二行。

**标准 I/O（stdin/stdout/stderr）：**
- `READ -,n,&var` — 从 stdin 读取一行
- `WRIT -,$+0,text` — 写入 stdout
- `WRIT --,$+0,text` — 写入 stderr
- `LOGS * CONOUT$` — 输出到控制台（调试终端输出）

## $2 变量系统

PECMD 有**四种访问前缀**对应三种存储层级（加上就近查找机制）。搞错这一点是绝大多数错误的根源。

| 层级 | 语法 | 设置方式 | 作用域 |
|------|------|----------|--------|
| 环境变量 | `%var%` | `ENVI var=val` 或 `ENVI $var=val` | 进程级，与子进程共享 |
| PE 全局变量 | `%&::var%` | `ENVI &::var=val` 或 `SET &::var=val` | 跨函数、跨线程、文件级，永不消失 |
| PE 局部变量 | `%&&var%` | `SET &&var=val` 或 `ENVI &&var=val` | 当前 `_SUB`/`{}` 块，退出自动销毁 |
| PE 变量引用 | `%&var%` | `ENVI &var=val` 或 `SET var=val` | 就近解析：从内向外查找，找到谁就是谁；找不到则创建为 `&&var` |

**`&a` 解析顺序**（反向匹配，从内向外）：
1. 先找当前范围的 `&&a` → 找到就用它
2. 再往外层找 `&::a`（全局）→ 找到就用它
3. 都没找到 → 当作 `&&a` 在当前范围新建

**关键规则：**
1. `ENVI^ ForceLocal=1` 放在文件顶部——强制 `ENVI` 和 `SET` 默认创建局部 PE 变量。**务必始终使用**（默认 `ForceLocal=0`，变量按就近查找链解析）。
2. `ENVI^ EnviMode=1` — 空变量引用返回空字符串。务必始终使用（默认 `EnviMode=0`，兼容 4.0 模式：空变量不解释为字面文本，自动多轮次非顺序解释）。
3. `SET` **始终**等价于 `ENVI &`（SET 总是创建 PE 变量而非环境变量）。ForceLocal=1 同时影响 SET 和 ENVI 的作用域（默认局部）。
4. `%Desktop%` 是**环境变量**版本；`%&Desktop%` 是**PE 变量**版本。
5. 多线程代码中务必使用 PE 变量（`&var`）——环境变量跨线程共享会竞态。跨线程通信用 `&::` 类变量。
6. `SET~ &&dest=Source.Key` — `~` 运算符执行**间接解引用**：将右侧展开为变量名，再读取该变量的值。伪数组必备。
7. `^` 前缀（`^SET`、`^ENVI`、`^CALC`、`^IFEX` 等）— 预先解释本命令（help.txt: "命令前若干个^表示预先解释本命令几次"）。循环中动态变量名必备。`^^` 预解释两次。
8. `ENVI-ret %~1=%var%` — 将值设置到*名称*存储在 `%~1` 中的变量（引用返回）。
9. `SET-def var=value` — 仅在变量未定义时设置（安全默认值）。

### ENVI^ / ENVI @@ 运行时控制命令

| 命令 | 功能 |
|------|------|
| `ENVI^ EnviMode=1` | 标准模式：空变量解释为空字符串，顺序解释1遍 |
| `ENVI^ ForceLocal=1` | 强制所有变量为 PE 变量，最简多线程/并行窗口 |
| `ENVI^ EXPORTLOCAL=1\|0\|&1\|&0` | PE 变量继承：1=继承，0=隔离（默认），&1=仅本级及以下继承，&0=仅本级及以下隔离 |
| `ENVI^ Alias name=cmd` | 命令别名：替代命令前半部（可包装 DLL 调用为伪命令） |
| `ENVI^ WndProc[1\|2\|3][C][,ptr]` | Win32 回调绑定（C=C 调用约定） |
| `ENVI^ memvar=[?返回名,][:字节数:]偏移,值` | 修改/查询 PECMD 内存变量 |
| `ENVI^ LoadPlugin=basename` | 加载插件 |
| `ENVI^ DisX64=1[,Old]` | 禁用/恢复 WOW64 文件系统重定向 |
| `ENVI^ zero=0\|1` | 私密模式：内存用完清零 |
| `ENVI^ Arg=*[字符集]字符串` | 词语分断 |
| `ENVI^ Clipboard=text\|?=var` | 读写剪贴板 |
| `ENVI^ EnviBroad=0\|1\|-` | 环境变量广播开关 |
| `ENVI^ __arg=0\|1` | 兼容模式：启用 `&&__arg` 参数表 |
| `ENVI^ LoadEnvi [路径\|-] [变量名]` | 从注册表刷新环境变量 |
| `ENVI^ HelpColor=[*高度] [前景色][#背景色]` | HELP 显示颜色 |
| `ENVI @@DeskTopFresh=[clearicon][;][1\|2\|4\|8\|16][;[-+]path]` | 桌面刷新 |
| `ENVI @@TaskIcoMenu=0\|1\|2` | 托盘菜单切换（0=关闭, 1=开启, 2=切换） |

> 完整 ENVI^ 控制命令、二进制缓冲区操作（SET-cmp、SET-tom、SET-copy、SET?int 等）和函数参数引用，参见 [commands-full.md](references/commands-full.md)。

### ENVI-env / SET-env — 临时绕过 ForceLocal

`ENVI-env` 和 `SET-env` 后缀临时绕过 ForceLocal=1 限制，直接操作环境变量。常用于线程内读取父窗口上下文的环境变量（如 `__WinID`）：

```wcs
ENVI-env hwnd0=%&__WinID%           // 从环境变量读取 __WinID（绕过 ForceLocal）
SET-env MyName=%MyName%             // 临时设为环境变量
```

```wcs
#code=65001
ENVI^ EnviMode=1
ENVI^ ForceLocal=1
SET$ &NL=0d 0a
SET$ &TAB=09
```

### 十六进制数据与原始内存

```wcs
SET$ &NL=0d 0a                  // 十六进制转宽字符串（Unicode）
SET$# &buf=*4096 0              // 十六进制转原始字节（二进制缓冲区）
ENVI$ &data=*1M 30 0d 0a        // 可变长度十六进制分配
```

## $3 代码组织

### 函数与 CALL 变体

```wcs
_SUB 函数名 [*]                   // * = this-call（在调用者栈中运行）
    SET &param1=%~1                 // %~1 剥离外层引号
    ENVI-ret %~3=%&result%          // 通过引用参数返回值
    // 函数参数：%0=函数名, %1~%n=各参数, %#=参数个数, %*=全部参数, %~=去掉引号
_END
```

```
CALL 函数名 [参数]                 // 调用函数
CALL *函数名 [参数]                // this-call（调用者栈）
CALL @窗口名 [参数]                // 创建/显示窗口（模态，阻塞）
CALL @*窗口名 [参数]               // 并行窗口（可同时操作，但关闭前阻塞后续命令）
CALL @-窗口名 [参数]               // 后台窗口
CALL @~窗口名 [参数]               // 后台，完全非阻塞
CALL @~~窗口名 [参数]              // 后台快速调用（非阻塞）
CALL @^窗口名 [参数]               // 并行，父窗口不阻塞子窗口
CALL @+窗口名 [参数]               // 弃养子窗口
CALL @--                           // 销毁 Win 环境（无参数）
CALL @--popmenu 窗口名 [x.y[:对齐]] // 在指定位置弹出菜单（对齐：对齐方式）
CALL --mem &变量名 [*] [参数]      // 执行内存中的动态函数代码
```

### 窗口（GUI）

```wcs
_SUB 窗口名,L200T100W400H300,窗口标题,[关闭命令],[图标],[样式],[遮罩] [-标志1 -标志2 ...]
_END
```

窗口形状：`L<左>T<上>W<宽>H<高>`。省略 L/T 则居中。
窗口样式：`-` = 无标题栏，`#` = 无边框，数字 1-99 = 透明度，`:`透明色。
常用标志：`-trap`（关闭按钮不退出）、`-nocap`（无标题栏）、`-nosysmenu`、`-top`、`-size`、`-maxb`、`-disminb`、`-discloseb`、`-nfocus`、`-ntab`、`-forcenomin`、`-scalef`、`-scale[:DPI]`、`-nxp`、`-csize`、`-na`、`-layer`（支持渐透明）、`-disaltmv`（禁用 ALT 拖动）

### IMPORT 与代码块

```wcs
IMPORT 路径\库文件.wcs          // 将可复用函数加载到当前脚本
_ENDFILE-IMPORT                // 此行以下内容在 IMPORT 时被丢弃

{                               // 带独立 PE 变量栈的代码块
    SET temp=仅此处有效
}
{*                              // this-call 代码块（在调用者栈中运行）
    LOCK .ole                   // COM 初始化等需要在当前作用域执行的操作
}
```

文件级和函数级的 `{` 必须从第 1 列开始。嵌套 `_SUB` 用双冒号访问：`类名::子函数名`。点号用于数据成员访问（如 `类名.属性`）。

## $4 流程控制

### FIND — 字符串比较（默认不区分大小写，`*c` 后缀区分）

```wcs
FIND $%var%=hello, 命令               // 相等（不区分大小写）
FIND $%var%*c=hello, 命令             // 相等（区分大小写，*c 后缀）
FIND $%var%<>hello, 命令              // 不等
FIND $%var%=, 命令                    // "为空" 测试（惯用法）
FIND *=var, 命令                      // 惯用法："为空"
FIND *<>var, 命令                     // 惯用法："非空"
FIND |%a%>%b%, 命令                   // | 前缀 = 浮点数比较
FIND #%a%>%b%, 命令                   // # 前缀 = 整数比较（INT64）
FIND [ $A & $B ], 命令                // 复合 AND
FIND [ $A | $B ], 命令                // 复合 OR（| 分隔）
FIND --pid &var,                     // 返回 5 值：空闲时间 总时间 CPU个数 1秒时钟数 一时钟100ns数
FIND --pid*@ &var,                   // 进程列表（用于 TABL）
FIND --class:Shell_TrayWnd --wid*@ &var  // 按类名过滤窗口列表
FIND --forpid:PID --wid*@ &var       // 按进程 ID 过滤窗口
FIND --sub --wid*@ &var              // 递归子窗口
FIND --menu &var,窗口ID              // 查询窗口的 MENU 句柄
FIND --menu#Index &var,MenuID        // 按索引查询子 MENU
```

### IFEX — 文件测试 / 数值比较

```wcs
IFEX C:\boot.ini, 命令                // 文件/目录存在
IFEX C:\boot.ini,! 命令               // 不存在
IFEX x:\, 命令                        // 盘符存在且有文件系统
IFEX $%val%>=5, 命令                  // $=浮点数比较（#=整数比较，|=字符串比较）
IFEX [ 条件1 & 条件2 ], 命令          // AND 复合条件
IFEX [ 条件1 | 条件2 ], 命令          // OR 复合条件（| 分隔）
IFEX MEMU=?,&var                     // 查询可用内存
IFEX C:\=?,&可用空间                  // 查询磁盘可用空间
```

### LOOP / FORX / TEAM / LOCK

```wcs
SET &&I=0
LOOP #%&I%<=10, { MESS %&I% | CALC &I=%&I% + 1 }

FORX * %&list%,&&item, { MESS %&item% }              // * = 空格分隔
FORX *NL &多行变量,&&line, { ... }                    // *NL = 换行分隔
FORX *NL:| &数据,&&item, { ... }                     // *NL:分隔符 = 自定义分隔符
FORX *L 0 2 10,&&val, { ... }                        // *L = 数值循环（起始 步长 结束）
FORX /S C:\Windows\*.exe,&&file,0 { ... }            // 文件系统枚举
FORX @\Windows,&&winDir,1 { ... }                    // @=仅搜索目录，\=所有盘符

// ⚠ FORX 循环变量引用规则：
// 定义用 &&item，引用值用 %&item%（单&），**不是** %&&item%
// 示例：FORX *NL &list,&&line, { SET &val=%&line% }  // ✅ 正确
//       FORX *NL &list,&&line, { SET &val=%&&line% }  // ❌ 错误

TEAM SET &a=1| SET &b=2| CALC &c=%&a% + %&b%         // 多命令链

// 线程
THREAD* CALL WorkerFunc                             // * = 持久栈（共享变量，保持父子关系）
THREAD CALL WorkerFunc                              // 独立模式（复制变量到子线程）
THREAD& CALL WorkerFunc                             // & = 强制 PE 变量模式（最简多线程）
THREAD+ CALL WorkerFunc                             // + = 抛弃式线程（不等待退出）
THREAD# CALL WorkerFunc                             // # = 代理模式（线程结束则退出）
THREAD $ CALL WorkerFunc                            // $ = 预先解释命令组
THREAD -link CALL WorkerFunc                        // 保持父子关系（类似 *）
THREAD -wait CALL WorkerFunc                        // 等待完成（阻塞消息循环）
THREAD -waitx CALL WorkerFunc                       // 等待完成（不阻塞消息循环）
THREAD -here CALL WorkerFunc                        // 当前栈的孩子（可修改父栈临时PE变量）
THREAD -waitp CALL WorkerFunc                       // 进程结束前等待该线程
THREAD -tid:&tid CALL WorkerFunc                    // 获取线程 ID
THREAD --st:128K CALL WorkerFunc                    // 设置栈大小

{ LOCK #pecmd                                      // 原子作用域
    LOCK --exist #MyLock,&&ret                      // 检查锁是否存在
}
LOCK #MyLock,&&ret2                                 // 创建/获取命名锁
```

完整 EXIT 变体、LAMBDA 语法和 `FIND --class:` 参见 [commands-full.md](references/commands-full.md)。

## $5 GUI 编程

### 控件类型

| 控件 | 命令 | 控件 | 命令 |
|------|------|------|------|
| 按钮 | `ITEM` | 标签 | `LABE` |
| 编辑框 | `EDIT` | 复选框 | `CHEK` |
| 单选框 | `RADI` | 下拉列表 | `LIST` |
| 表格 | `TABL` | 进度条 | `PBAR` |
| 分组框 | `GROU` | 图片 | `IMAG` |
| 多行文本 | `MEMO` | 子窗口 | `SWIN` |
| 定时器 | `TIME` | 托盘/气泡 | `TIPS` / `TIPS*` |
| 选项卡 | `TABS` | 滑块 | `SLID` |
| 微调器 | `SPIN` | 树形视图 | `TREE` |
| 滚动条 | `SBAR` | 热键 | `HKEY` |
| 日期时间 | `DTIM` | IP 地址 | `IPAD` |
| 菜单项 | `MENU` | 屏幕捕捉 | `SCRN` |
| 浏览窗口 | `BROW` | | |

### 消息映射

```wcs
ENVI @控件.MSG=_%&WM_LBUTTONDOWN%: 命令        // _ = 后置系统处理器（控件通知）
ENVI @窗口.MSG=0x0010: CALL OnClose             // 无 _ = 窗口级消息（WM_CLOSE）
ENVI @控件.MSG=_%&msg%::&&wp,&&lp, CALL Handler // 捕获 wParam/lParam
ENVI @控件.POSTMSG=#1                            // 投递自定义消息 #1
ENVI @控件.SENDMSG=消息号;wParam;lParam          // 同步发送
ENVI @控件.MSG=$0x0201: 命令                     // $ = 屏蔽系统响应（可返回结果码）
ENVI @控件.MSG=*msg: 命令                        // * = 捕鼠器 B（鼠标捕获模式）
ENVI @控件.MSG=+msg: 命令                        // + = 超级捕获
```

MSG 前缀说明：无前缀=直接处理后丢弃（不传递给系统），`_`=系统响应后执行（控件通知），`$`=屏蔽系统响应（可返回结果码），`*`=捕鼠器 B（鼠标捕获），`+`=超级捕获。消息号 `*del` 删除映射。`#1`~`#N` 为 PECMD 自定义消息。

### 控件操作

```wcs
ENVI @控件名=新文本                              // 设置文本
ENVI @控件名.Enable=0                            // 禁用（1=启用）
ENVI @控件名.Visible=0                           // 隐藏（1=显示）
ENVI @控件名.POS=左:上:宽:高                     // 移动/调整大小
ENVI @控件名.POS=?;&L:&T:&W:&H                  // 查询位置
ENVI @控件名.Val=?行.列;&var                     // 获取 TABL 单元格
ENVI @控件名.Val=?*;&count                       // 获取 TABL 行数
ENVI @控件名.*del=                               // 销毁控件
```

完整控件语法、全部 25 种控件类型、ENVI @ 属性参考、窗口生命周期和消息映射——参见 [pecmd-gui.md](references/pecmd-gui.md)。

GUI 写法示例（动态控件、选项卡页、自定义标题栏、GDI 绘图、拖放等）——参见 [how-tos/gui.md](references/how-tos/gui.md)。

## $6 DLL 调用

> **实测验证（32-bit / 64-bit 行为一致）：** 以下内容在 PECMD 32-bit (PECMDx86) 和 64-bit (PECMD) 上均经实测确认，行为完全相同。DLL 调用可正常工作，但有重要限制。

### 调用约定

DLL 函数**默认按 PASCAL/stdcall** 调用约定。Windows API（WINAPI 标记）不需要 `--c`。使用 `--c` 切换到 CDECL。最多 20 个参数（C 调用 100 个）。

### 正确语法（逗号分隔 + 类型前缀 + --qd）

```wcs
// ✅ 正确格式：逗号分隔，整数 #，字符串 $，建议加 --qd
CALL $ --qd --ret:&&r user32.dll,GetSystemMetrics,#0             // 整数参数
CALL $ --qd --ret:&&r user32.dll,FindWindowW,$Progman,#0         // 字符串+整数
CALL $ --qd --ret:&&r kernel32.dll,lstrlenW,$Hello World          // 字符串参数
CALL $ --ret:&&hDll ,-LoadLibrary,^user32.dll                     // 加载 DLL（^ = 自动释放）
CALL $ --ret:&&r *%&hDll%,GetSystemMetrics,#0                     // 通过句柄调用
CALL $ ,-FreeLibrary,*%&hDll%                                      // 释放 DLL
CALL $ --bool --ret:&&ret DLL,函数,...                             // --bool：函数返回 BOOL
CALL $ --cd --ret:&&ret DLL,函数,...                               // --cd：执行前切换到目标目录

// Win32 回调函数绑定（用于 EnumResourceNames 等 API）
ENVI^ WndProc1C,WndProc1Addr               // C 调用约定回调
SET^ WndProc1,WndProc1Addr                  // PASCAL/stdcall 回调
// PECMD 自动查找 _SUB OnWndProc1 并包装为 Win32 回调
```

### 关键规则

1. 参数**必须**用逗号分隔——点语法（`Func.#Param`）完全不工作
2. 整数参数**必须**有 `#` 前缀——无前缀返回 0
3. 字符串**必须**用 `$` 前缀
4. **务必加 `--qd`**——否则字符串多传一个 null 终止符，导致 API 结果错误：

| 字符串 | 无 `--qd` | 有 `--qd` | 说明 |
|--------|-----------|-----------|------|
| `$A` | lstrlenW → 2 | lstrlenW → 1 | 无 --qd 含 null |
| `$Test` | lstrlenW → 5 | lstrlenW → 4 | 无 --qd 含 null |
| `$Progman` | FindWindowW → 0 | FindWindowW → 66062 | 无 --qd 找不到窗口 |

### ❌ 不可用的语法

```wcs
// ❌ 点语法完全不工作
CALL $--qd --ret:&&r user32.dll,GetSystemMetrics.#0              // 返回空

// ❌ 整数无前缀返回 0
CALL $ --ret:&&r user32.dll,GetSystemMetrics,0                   // 返回 0

// ⚠ GetProcAddress：无 --qd 时常返回 0x0，加 --qd 后可工作
CALL $ --ret:&&p ,-GetProcAddress,*%&hDll%,$GetSystemMetrics     // 可能返回 0x0
CALL $--qd --ret:&&p ,-GetProcAddress,*%&hDll%,GetSystemMetrics  // 加 --qd 更可靠

// ⚠ 缓冲区输出：无 --qd 时数据可能不回传，加 --qd 后可工作
SET$ &buf=*256 0
CALL $--qd --ret:&&r kernel32.dll,GetWindowsDirectoryW,*&buf,#256  // --qd 模式

// ❌ 无 --qd 时字符串含 null 终止符，FindWindowW 等失败
CALL $ --ret:&&r user32.dll,FindWindowW,$Progman,#0             // 返回 0
```

### 可靠工作的场景

| 场景 | 语法 | 状态 |
|------|------|------|
| 无参整数返回 | `CALL $ --ret:&&r kernel32.dll,GetTickCount` | ✅ |
| 整数参数 | `CALL $ --qd --ret:&&r user32.dll,GetSystemMetrics,#0` | ✅ |
| 字符串输入 | `CALL $ --qd --ret:&&r user32.dll,FindWindowW,$Progman,#0` | ✅ |
| 变量作参数 | `CALL $ --qd --ret:&&r kernel32.dll,lstrlenW,$%&str%` | ✅ |
| LoadLibrary | `CALL $ --ret:&&h ,-LoadLibrary,^user32.dll` | ✅ |
| 通过句柄调用 | `CALL $ --ret:&&r *%&h%,GetSystemMetrics,#0` | ✅ |
| 缓冲区输出 | 加 `--qd` 后可回传到 PE 变量 | ⚠ |
| GetProcAddress | 加 `--qd` 后可工作 | ⚠ |
| 点语法 | Func.#Param 不解析 | ❌ |

### 32-bit vs 64-bit 区别

| 项目 | PECMDx86 (32-bit) | PECMD (64-bit) |
|------|-------------------|----------------|
| 文件标识 | PE32, i386 | PE32+, x86-64 |
| `bX64` 值 | `0`（32 位系统）或 `1`（64 位系统） | `3` |
| 指针大小 | 4 字节（< 4GB） | 8 字节（> 4GB） |
| DLL 调用行为 | 与 64-bit 完全相同 | 与 32-bit 完全相同 |
| LoadLibrary 句柄 | `0x761A0000` | `0x7FFC34B00000` |
| 代码差异 | 无 | 无 |

`bX64` 值含义：`0`=WIN32（32 位系统原生），`1`=WIN64（64 位系统运行 32 位 PECMD），`3`=PECMD64（64 位 PECMD）。

### 参数类型前缀

| 前缀 | 类型 | 描述 | 示例 |
|------|------|-------------|------|
| `#` | integer | 按整数传递 | `#0`、`#256`、`#%&hwnd%` |
| `<` | INT64 | 64 位整数 | `<0x100000000` |
| `$` | string | 按 Unicode 字符串传递（配合 `--qd` 不含 null） | `$CabinetWClass` |
| `@` | ANSI | 按 ANSI 字符串传递 | `@text` |
| `*` | buffer | 传递 PE 变量地址（`*&buf` 形式可回传输出） | `*&buf` |
| `=` | raw | 按原始数据传递 | |
| `~` | deref | 间接解引用（展开变量名再读值） | |

### 缓冲区操作（PE 变量内部）

```wcs
SET$# &buf=*256 0                     // 分配 256 字节原始缓冲区
SET$ &strBuf=*256 0                   // 分配 256 个宽字符
SET-long &buf=value:offset            // 写入 32 位整数到缓冲区偏移
SET?int &buf=&&var:offset             // 从缓冲区偏移读取 32 位整数
```

### DLL 输出数据的替代方案

DLL 缓冲区输出可使用 `*&buf` 形式（PE 变量地址传递，输出回传）。获取系统信息也可用内置命令：
- `FIND --wid*@ &var` — 枚举窗口（返回 HWND、类名、标题）
- `FIND --pid*@ &var` — 枚举进程
- `REGI` — 注册表读取系统信息
- `EXEC* &out=!cmd.exe /c ...` — 捕获外部命令输出
- `IFEX MEMU=?,&var` — 查询可用内存
- `IFEX C:\=?,&var` — 查询磁盘空间

### 平台检测

```wcs
IFEX #%&::bX64%=3, SET &PtrSz=8! SET &PtrSz=4  // 3=PECMD64, 1=WIN64(32位PECMD在64位系统), 0=WIN32
```

## $7 常用范式

### 磁盘与分区

```wcs
FDRV &盘符列表=*:                                        // 枚举所有盘符
FDRV *vol &卷标,&文件系统=C:                             // 获取卷标和文件系统
PART list disk,&&全部磁盘                                 // 枚举所有物理磁盘
PART list disk %&dsk%,&&磁盘信息                          // 获取磁盘信息
PART list part %&dsk%,&&分区列表                          // 列出分区号
PART -hextp -phy# list part %&dsk%#%&pt%,&&分区信息       // 详细分区信息
SHOW * %&dsk%#%&pt%,%&盘符%                               // 分配盘符
SHOW *- %&dsk%#%&pt%,                                     // 移除盘符
```

### 注册表与文件 I/O

```wcs
REGI $HKLM\SOFTWARE\App\Key,&var                  // 读 REG_SZ
REGI #HKLM\SOFTWARE\App\Count,&var                // 读 REG_DWORD
REGI $HKLM\SOFTWARE\App\Key=值                     // 写 REG_SZ
READ %路径%,**,&内容                               // 读取整个文件
WRIT %路径%,+0,新行                                // 追加一行
GETF# %路径%,0#*,&原始数据                         // 按原始字节读取文件
```

### 执行与捕获

```wcs
EXEC* &输出=!cmd.exe /c dir /b                            // 捕获全部输出
EXEC =!"%MyNAME%" TEAM WAIT 1000|LOAD other.ini           // 运行子 PECMD
EXEC* &out=!cmd.exe /c ipconfig                           // 捕获外部命令输出
EXEC* --exe:#101 &out=*embedded.exe                       // 从 PECMD 资源运行 EXE
```

### 单实例互斥体

```wcs
{ LOCK #pecmd
    LOCK --exist #MyAppLock,&&exists
}
IFEX $1=%&exists%,
{
    REGI $HKCU\Software\MyApp\WID,&&wid
    IFEX $%&wid%>0, TEAM ENVI @@Visible=%&wid%:2| ENVI @@POS=%&wid%:::::::1
    EXIT FILE
}
LOCK #MyAppLock,&ret2
```

### 字符串操作

```wcs
MSTR &&a,&&b=<1><3->%&data%                              // 字段1、字段3到末尾（- = 到末尾）
MSTR &&last=<-1>%&data%                                   // 最后字段（负索引）
MSTR &&q=<~5>%&data%                                      // 字段5并剥离外层引号（~前缀）
MSTR -delims:. &&a,&&b,&&c,&&d=<1><2><3><4>%&ip%         // 按自定义分隔符拆分
MSTR* &&a,&&b=<1><2>%&data%                               // *前缀 = TAB 分隔
MSTR$ &&a,&&b=<1><2>%&data%                               // $前缀 = 空格分隔（连续空格视为单个）
SED &&r=0,模式,替换,%&source%                             // 替换所有匹配（正则模式）
SED &&r=1,模式,替换,%&source%                             // 替换第一个匹配
SED &&r=-1,模式,替换,%&source%                            // 替换最后一个匹配（负数=从末尾）
SED -t &&r=0,ABCD,abcd,%&source%                          // -t = 字符集翻译（A→a, B→b, 逐字符映射）
SED -ni &&r=0,模式,替换,%&source%                         // -ni = 不区分大小写
SED &&ext=-1,.*\.,,  ,%&filename%                          // 获取扩展名（.*\. 匹配到最后一个句号）
LPOS &&pos=needle,,%&haystack%                            // 查找首次出现（不区分大小写）
```

> **MSTR 注意事项：** 默认分隔符为空格。`-delims:09` 指定制表符（十六进制 09）。字段索引从 1 开始。`<N>` = 单个字段，`<N->` = 字段N到末尾，`<N*>` = 仅字段N（单字段），`<~N>` = 剥离引号，`<-N>` = 从末尾反向索引。`MSTR*` = TAB分隔，`MSTR$` = 空格分隔（连续空格视为单个）。如果 MSTR 返回空，先用 `WRIT` 打印源变量确认格式，或改用 `FORX *` 按空格逐字段拆分作为替代。
>
> **SED 注意事项：** SED 默认使用正则模式。`.` 匹配任意字符，非字面量句号。匹配字面量句号用 `\.`。使用标志字符 `*` 可切换为字面量模式（不解释正则）。count=0 替换全部匹配，count=N 替换前 N 个，count=-N 替换后 N 个。`0:0` 与 `0` 等价。`-t` 标志启用字符集翻译，`-ni` 标志不区分大小写。模式中的空格作为参数分隔符处理时可能引起问题——用变量间接传递含空格的模式。
>
> **LPOS 注意事项：** 参数顺序为 `LPOS &&pos=needle,,haystack`（查找目标在前，源字符串在后）。返回首次出现的位置索引。加 `,1,` 可区分大小写。`RPOS` 从末尾查找。

含错误处理和边界情况处理的完整展开版本，参见 [how-tos/storage.md](references/how-tos/storage.md)（磁盘/注册表/文件）、[how-tos/system.md](references/how-tos/system.md)（进程/线程/定时器）、[how-tos/net.md](references/how-tos/net.md)（网络/COM）。

### CALC 计算与数学函数

```wcs
CALC &结果=%&a% + %&b%                               // 基本算术：+ - * / % ^
CALC #&结果=%&a% & %&b%                               // # = 整数模式；位运算：& | @
CALC -base=16 #&hex=shl(0x07,16)|0x20                 // 十六进制输出 + 位移
CALC &sz=%&bytes%/1G#3                                // 字节转 GB，3 位小数
CALC -err=0 &r=%&a% / %&b%                            // 出错时返回默认值 0
```

**数学函数（34个）：**

| 类别 | 函数 |
|------|------|
| 三角 | `sin cos tan ctg arcsin arccos arctan arcctg deg rad` |
| 代数 | `abs sqrt ln lg log exp pow pow10 hypot`（⚠ `log()` 需两参数 `log(底数,值)`，单参数返回 0；用 `lg()` 代替 log₁₀） |
| 取整 | `floor ceil round int frac` |
| 位运算 | `shl shr xor not lnot` |
| 其他 | `div mod rand max min` |

常量：`e`、`pi`。尺寸后缀：`K`=1024、`M`=1024²、`G`=1024³、`T`=1024⁴、`S`=512。
多表达式用 `;` 分隔，子变量 `$subName=expr`。

## $8 陷阱与注意事项

1. **CALC 空格**：`CALC &J=1+2` 可以正常执行。右侧以 `%&I%` 等变量开头时，用空格分隔（`CALC &J= %&I%+1`）。PECMD 要求减号后必须有空格（`3 - 2`）。
2. **注释标记**：`//`、`;`、`` ` ``（反引号）均为有效注释符。行尾注释前必须有空格（如 `SET &a=1 // 注释`）。help.txt: "注释符前有一个空字符，该空字符算注释"——所有位置的注释符前都建议加空格。
3. **SET 就是 ENVI &**：`SET var=val` 语义上等价于 `ENVI &var=val`。启用 ForceLocal=1 后，两者都创建局部 PE 变量。
4. **FIND 与 IFEX 前缀交换**：FIND 中 `$` = 字符串比较、`|` = 浮点比较；IFEX 中 `$` = 浮点比较、`|` = 字符串比较。`#` 在两者中均为整数比较。IFEX 中 `|` 在 `[]` 复合条件内为 OR 逻辑运算符。
5. **盘符冒号**：`FDRV`、`FORM`、`FIND C:\=?` 都需要 `:` 后缀。
6. **带空格的路径**：`LOAD "C:\Program Files\a.ini"` 需要引号。
7. **文件编码**：中文脚本首行声明编码（`#code=65001` = UTF-8，`#code=936` = GBK）且文件编码须与声明一致。SDK 示例普遍使用 GBK（936）。
8. **`{` 位置**：文件级和函数级 `{` 必须从第 1 列开始。在 TEAM/LOOP/IFEX 内部，`{` 启动命令组。
9. **行续接**：行首第一个非空格字符为 `\` 时，将该行合并到上一行。
10. **`_SUB`、`WRIT`、`LOOP` 不能内联**：不能在 FIND/IFEX/TEAM 命令内部定义 `_SUB`。`WRIT` 和 `LOOP` 也必须位于单独一行，不能嵌套在 FIND/IFEX/TEAM 内。
11. **空字符串检测**：`FIND $%var%=,` 测试"为空"。`FIND *=var,` 也测试"为空"（惯用法）。`FIND *<>var,` 测试"非空"。
12. **字面 %**：字符串中表示字面 `%` 用 `%%`。
13. **线程安全**：线程创建时，**共享点之后**的 PE 变量复制到子线程。持久栈上下文（窗口、`*` 函数）中的变量是**共享**的（非复制）；非持久上下文（普通函数、`{}` 块）中的变量自动复制一份。真正跨线程通信用 `&::` 变量。
14. **中文变量名**：PECMD 社区的事实标准。为这个生态系统编写脚本时使用中文名称。
15. **OnShutdown.wcs**：PECMD 在关机/重启/注销前自动运行 `%SystemRoot%\System32\OnShutdown.wcs`，格式为 `OnShutdown.wcs <操作码> [脚本参数表]`。操作码：`shutdown`=关机、`reboot`=重启、`logout`=注销、`suspend`=挂起、`hiber`=休眠、`poweroff`=关电、`lock`=锁定计算机、`unknown`=未知。关机菜单支持的子集：`shutdown`、`reboot`、`logout`、`poweroff`、`unknown`。
16. **`^` 预解释**：`^COMMAND` 将变量展开推迟到执行时（循环中必备）。`^^COMMAND` 预解释两次。
17. **MSG 上的 `_` 前缀**：控件通知用 `_msg#`；窗口级消息省略 `_`。搞错这一点是非常常见的错误。
18. **THREAD\* 与 THREAD**：只有持久（窗口）栈中的 THREAD* 共享 PE 变量。在 `{}` 块中，两者都复制。
19. **FIND 展开规则**：FIND 中的裸标识符被视为字面字符串。始终使用 `FIND $%&var%=值` 引用 PE 变量。
20. **`@@Visible` 与 `@Visible`**：跨进程用 `ENVI @@Visible=窗口ID:值`。进程内用 `ENVI @控件.Visible=0|1`。
21. **FORX 变量引用**：`FORX ... &&item, { }` 定义用 `&&item`，循环体内引用值用 `%&item%`（单&）。用 `%&&item%` 会找不到变量。
22. **FIND --wid\*@ 输出格式**：`序号 窗口ID 控件ID 父窗口ID 线程ID 进程ID 类型 标题`，字段用 **TAB** 分隔。配合 `FORX *NL` 按行遍历、`MSTR*` 按 TAB 分字段提取（field 2=窗口ID, field 7=类型, field 8=标题）。
23. **DLL 调用不可用时**：优先用 `FIND --wid*@`（窗口枚举）、`FIND --pid*@`（进程枚举）、`REGI`（注册表）、`EXEC*`（外部命令）替代。
24. **`Visible` 与 `Visable`**：两者均可工作（PECMD 接受两种拼写）。文档统一用 `Visible`，但 SDK 示例普遍用 `Visable`。
25. **FORX *NL: 自定义分隔符**：`FORX *NL:| &data,&&item,` 用 `|` 作为行分隔符（默认为换行）。
26. **ENVI^ ALIAS 包装 DLL**：`ENVI^ ALIAS GetTick=CALL $ --ret:&&r kernel32.dll,GetTickCount` 使 `GetTick` 可作为伪命令直接调用。

## $9 参考文件

当需要超越上述快速参考的详细信息时，查阅以下权威文件：

| 文件 | 何时查阅 |
|------|----------|
| `references/commands-full.md` | 命令语法、全部标志、参数细节 |
| `references/pecmd-gui.md` | GUI 控件、消息、窗口生命周期、ENVI @ 属性 |
| `references/pe-startup.md` | WinPE 启动脚本、PECMD.INI 结构、启动阶段 |
| `references/how-tos/storage.md` | 磁盘、分区、文件、注册表、设备 写法示例 |
| `references/how-tos/system.md` | 进程、线程、定时器、系统信息、加密、工具 |
| `references/how-tos/gui.md` | GUI 模式：动态控件、选项卡、GDI、拖放等 |
| `references/how-tos/net.md` | 网络、SOCK、COM/WMI 写法示例 |
| `references/troubleshooting.md` | 常见错误排查、DLL 调用失败、变量引用问题 |

## $10 输出规范

编写 PECMD 脚本和工具时遵循以下规范：

1. 中文脚本首行 `#code=65001`，随后 `ENVI^ EnviMode=1`，再 `ENVI^ ForceLocal=1`
2. 默认使用中文变量名——PECMD 社区的事实标准
3. 除非明确需要环境变量，始终使用 PE 变量（`&变量名`）
4. 大量使用 `TEAM` 链式组合紧凑的初始化序列
5. 将复杂操作拆分为命名清晰的 `_SUB` 函数
6. GUI 中用 `ENVI @控件.POS=?` 保存初始控件位置，在 resize 事件中恢复
7. 可调大小的窗口务必处理 WM_SIZE（0x0005）以重新计算布局
8. 调用外部程序优先用 `EXEC =!"%MyNAME%" ...` 或 `EXEC* &out=!cmd.exe /c ...`
9. 线程安全：跨线程通信用 `&::` 类 PE 变量
10. 生产代码：用 `{ LOCK #pecmd ... }` 块包装互斥操作
11. 控件消息用 `_` 前缀（`_0x0201`）；窗口级消息省略 `_`（`0x0010`）
12. 用 `SED` 做正则替换（始终正则模式，`.` 匹配任意字符，`\.` 匹配字面量句号），用 `MSTR` 从结构化输出中提取字段——如果 MSTR 不工作，改用 `FORX *` 逐字段拆分


---

GitHub: https://github.com/VirtualHotBar/PECMD-Pro-Max
ClawHub: https://clawhub.ai/virtualhotbar/pecmd-pro-max

