aardio 文档

fsys.res:资源枚举、加载与保存

import fsys.res;

主要用于枚举、加载和保存 aardio EXE 中的内嵌文件及库资源:enum/loadRes/saveRes。默认类型为字符串 "RES",枚举内嵌库可指定 "LIB"。

也支持其它 EXE/DLL 的 PE 资源;names/types/languages/list 提供普通数组查询。

使用 fsys.res.menu 探查菜单资源

快速上手

import fsys.res;

// 枚举当前 EXE 的内嵌文件;回调不需要返回值。
var ok,err,code = fsys.res.enum(function(module,resType,resName){
    var data = fsys.res.loadRes(resName,resType,module);
    print(resName,#data);
});
if(!ok) return null,err,code;

// 枚举当前 EXE 的内嵌库。
fsys.res.enum(function(module,resType,resName){
    print(resType,resName);
},"LIB");

通常读取工程内嵌文件可直接使用 string.load("/res/file.txt"),它能同时处理开发期硬盘文件和发布后的内嵌资源。enum 用于需要列出或批量处理资源的情况;默认模块在开发期是 aardio EXE,发布后是自己的 EXE。

函数 返回值
enum(proc, resType="RES", module) 对每个名称调用 proc(module,resType,resName);成功返回 true
types(module) 全部类型的纯数组:数字 ID 或文本类型名
names(resType="RES", module) 指定类型下的资源名称纯数组:数字 ID 或文本名称
languages(resName, resType="RES", module) 真实存在的 LANGID 数值纯数组,可能包含 0
list(resType, module) {type;name;lang;size} 项目的纯数组,每个语言版本一项
loadRes(resName, resType="RES", module, lang) 二进制字符串,保留所有字节,包括 NUL
loadMenu(resName, module, lang) {version;helpId;items} 菜单数据表
decodeMenu(data) 不访问文件,直接解码 MENU/MENUEX 字节串

名称与类型:数字不等于文本

Windows 的数值资源 ID 是 16 位无符号整数,可取 0..65535。数值 7 与文本 "7" 保持区分。原生 API 的 "#7" 数值名称约定仍按 Windows 语义处理。

"MENU"、"ICON" 等字符串不是数字类型的隐式别名,可能是真实的自定义类型名。Windows API 常量不作运行时声明,代码使用 4/*_RT_MENU*/、3/*_RT_ICON*/ 或直接写数值。库中的常量条目仅供编辑器自动完成,将名称替换成数值,不依赖运行时变量。

import fsys.res;
var file = "C:\Windows\System32\notepad.exe";
var ids,err,code = fsys.res.names(4/*_RT_MENU*/,file);
if(!ids) return null,err,code;
for i,id in ids {
    print(id); // number 或 string,没有原生资源指针
}

常用类型值:菜单 4、对话框 5、字符串表 6、快捷键表 9、原始数据 10、图标组 14、版本 16、HTML 23、清单 24。类型库直接使用字符串 "TYPELIB"。

调用原生 API 时,makeIntResource(value) 负责转成原生参数;getName(value) 反向转换。普通查询无需使用它们。getName 将空原生指针解释为资源 ID 0,将低位指针(包含 65535)转成整数,将有效的 UTF-16 名称指针转成 UTF-8。不要给 getName 传任意内存地址。

精确语言选择

import fsys.res;
var file = "C:\path\resources.dll";
var langs,err,code = fsys.res.languages(100,10/*_RT_RCDATA*/,file);
if(!langs) return null,err,code;
for i,lang in langs {
    var bytes,err,code = fsys.res.loadRes(100,10/*_RT_RCDATA*/,file,lang);
    if(bytes===null) return null,err,code;
    print(lang,#bytes);
}

LANGID 是整数,不是 "zh-CN" 等字符串。常见值为 0(中性)、1033(英语/美国)、2052(中文/中国)。

enum 回调

调用方式为 enum(proc,resType="RES",module),回调参数顺序为 module,resType,resName。

import fsys.res;

fsys.res.enum(function(module,resType,resName){
    var data = fsys.res.loadRes(resName,resType,module);
    print(resName,#data);
    // 不返回值即可继续;如果只要第一个资源,明确 return false。
},"RES","C:\path\app.exe");

手工加载模块

load(path, flags) 默认标志 2,只映射数据、不执行代码。若显式指定其它标志,则按 LoadLibraryExW 语义执行,特别是 0 可执行 DLL 入口。

只对自己从 load 获得的模块调用 free。free(null) 安全忽略,但它不能识别已释放的非空句柄,不要重复释放或释放 _HINSTANCE。

更新资源

import fsys.res;
var updater,err,code = fsys.res.open("C:\path\copy.dll");
if(!updater) return null,err,code;

var ok,err,code = updater.update(10/*_RT_RCDATA*/,100,'abc\0def',2052);
if(!ok){
    updater.close(true); // 丢弃暂存修改
    return null,err,code;
}
return updater.close(); // 提交

当前 EXE 的 aardio 内嵌文件

aardio 内嵌目录使用文本类型 "RES" 和大写虚拟路径名称,因此 names/loadRes 的默认类型为 "RES"。

saveRes(srcDir="/", target="/") 和 saveAppData(resDir="/", target) 用于导出当前程序内嵌资源目录,发布期实现直接使用 names/loadRes。一般读取工程文件无需使用本库,直接 string.load("/res/file.txt") 就能兼容开发期硬盘文件与发布后的 EXE 内嵌资源。

常见错误码

code 含义
2 / 3 文件或路径不存在
6 更新器已关闭/无效句柄
13 损坏或不支持的菜单模板
87 参数类型、范围或 NUL 错误
1812 PE 没有资源数据
1813 资源类型不存在
1814 资源名称不存在
1815 指定语言资源不存在

没有资源的 PE 可能完全没有资源目录,也可能保留空目录;Windows 会相应返回 1812 或 1813,库保留实际错误码。

检查第一个返回值或 ===null 即可,通常不需要 try/catch;不要在释放模块后才取 lasterr(),函数已经返回操作时的错误文本和错误码。

Markdown 格式