# fsys.res.menu：探查 EXE/DLL 菜单资源文件

## 菜单解码：不用手写 Win32 遍历

`fsys.res.menu(resName[,moduleOrPath[,langId]])` 读取 `RT_MENU`，支持标准 MENU 与扩展 MENUEX。纯数据解析，不创建 HMENU 或窗口；读取字段前检查边界，截断、无效头部或超过 64 层子菜单嵌套返回 `null, err, 13`。

返回包含以下树结构的表对象：

```text
{
    version = 0 或 1;
    helpId = MENUEX 头帮助 ID，标准 MENU 为 0;
    items = [
        {
            id; text; type; state; flags;
            popup; separator;
            // 弹出项还包含：
            helpId; items = [ 子项目... ];
        }
    ];
}
```

- 标准 MENU 只按 WORD 对齐，弹出项没有命令 ID，`id` 为 `null`。不要把 Windows 临时子菜单句柄当成命令 ID。
- MENUEX 项目/帮助 ID 按 DWORD 对齐，保留 32 位命令 ID（包括 `0xFFFFFFFF`）和弹出项 ID。
- `text` 为 UTF-8 编码，保留 `&`、TAB 和 Unicode 代理对；不受固定 512/1024 字符缓冲区限制。
- `flags` 是模板原始选项；`type/state` 为菜单类型和状态位，分隔符按 Windows 语义标记为禁用。
- `items` 保持资源中的原始显示顺序，只在弹出项存在；不能对菜单项排序。
- 这是 EXE/DLL 中的**静态资源**，不反映程序运行后动态增删的菜单或状态变化。
- 可用 `JSON.stringify`、`table.tostring` 或线程参数传递菜单结果，无原生句柄生命周期问题。

枚举并查找“复制、粘贴、全选”命令：

```aardio 
import fsys.res.menu;
var file = "C:\Windows\System32\notepad.exe";

var commands = [];
var ids,err,code = fsys.res.menu.enum(
	function(text,id,position,item,resId,level){
		//在资源文件里仅 Ctrl+C 写作 Ctl+C  
		if(string.find(text,"Ctr?l\+[CVA]")){
				table.push(commands,{resId=resId;lcommandId=id;position=position;text=text;level=level});
				print(text,id,position,resId,level);
		} 
	}
)

return commands;

```

遍历全部菜单资源：

```aardio 
import fsys.res.menu;
var file = "C:\Windows\System32\notepad.exe";

for menu,resId in fsys.res.menu.each(file){
	print(menu,resId) // menu 为 fsys.res.menu 对象
}
```