Delphi中如何枚举BPL包RC文件STRINGTABLE的所有字符串条目
STRINGTABLE资源枚举不到的核心原因
Windows系统对STRINGTABLE资源的存储、枚举规则和窗体、位图等常规资源完全不同:
- 常规资源以单个资源为单位存储,
EnumResourceNames回调会直接返回单个资源的ID/名称 - STRINGTABLE不会按单个字符串拆分存储,而是固定每16个字符串划分为1个资源块,每个块的资源ID为
字符串ID div 16,枚举回调返回的是块ID,不是单个字符串的ID - 常规枚举逻辑拿到RT_STRING类型的回调后,直接把块ID当字符串ID处理,自然拿不到自定义RC里定义的字符串内容
无ID前置依赖的枚举实现方案
不需要提前知道任何字符串ID,按以下两步即可枚举BPL中所有STRINGTABLE资源的有效字符串:
1. 枚举RT_STRING类型的所有资源块
调用EnumResourceNames时指定枚举类型为RT_STRING(系统定义值为6),回调中拿到的整数型资源名就是字符串块ID,注意不要把非整数型的资源名当成有效块ID处理。
2. 遍历块内所有ID读取有效字符串
每个块对应16个连续的字符串ID,起始ID为块ID * 16,结束ID为起始ID+15,逐个调用LoadStringAPI传入BPL模块句柄尝试读取字符串,返回长度大于0的就是存在的有效字符串,返回0的ID位置为空直接跳过即可。
可直接复用的核心代码
// 老版本Delphi没有IS_INTRESOURCE宏的话自行加这个定义 function IS_INTRESOURCE(p: Pointer): Boolean; begin Result := (NativeUInt(p) shr 16) = 0; end; function EnumStringBlockProc(hModule: HMODULE; lpszType: PChar; lpszName: PChar; lParam: LPARAM): BOOL; stdcall; var BlockID: Word; StartID, StrID: Word; Buf: array[0..1023] of Char; StrLen: Integer; OutputList: TStrings; begin Result := True; OutputList := TStrings(lParam); // 校验当前枚举的是STRINGTABLE块 if not (IS_INTRESOURCE(lpszType) and (NativeUInt(lpszType) = RT_STRING)) then Exit; if not IS_INTRESOURCE(lpszName) then Exit; BlockID := Word(NativeUInt(lpszName)); StartID := BlockID * 16; // 遍历块内16个字符串ID for StrID := StartID to StartID + 15 do begin StrLen := LoadString(hModule, StrID, Buf, Length(Buf)); if StrLen > 0 then begin // 记录有效字符串的ID和内容 OutputList.Add(Format('字符串ID: %d, 内容: %s', [StrID, Buf])); end; end; end; // 调用示例 procedure EnumBplStrings(const BplPath: string; OutputList: TStrings); var hPkg: HMODULE; begin hPkg := LoadPackage(PChar(BplPath)); if hPkg = 0 then RaiseLastOSError; try // 直接枚举STRINGTABLE类型资源,不需要枚举所有资源类型 EnumResourceNames(hPkg, RT_STRING, @EnumStringBlockProc, LPARAM(OutputList)); finally // 不需要保留包句柄的话在这里卸载,需要后续用字符串就延后卸载 UnloadPackage(hPkg); end; end;
常见踩坑说明
- 不要用Delphi自带的
LoadStr函数读取动态加载BPL里的字符串,该函数内部默认从EXE或静态加载的模块句柄查找资源,不会传入动态加载的BPL句柄,必须直接调用LoadStringAPI并传入LoadPackage返回的BPL句柄 - 块ID计算规则是
字符串ID div 16,不要错用mod运算,比如你定义的ID=1000的字符串,对应块ID是62,枚举到ID为62的块时就能读到该字符串 - 如果完全收不到RT_STRING类型的枚举回调,先拿资源编辑器打开编译生成的BPL文件,确认RC文件有没有被正确编译进BPL,常见问题是dpk里添加RC文件时路径错误,导致RC没有参与编译
- 不要尝试跳过块遍历逻辑直接拿单个字符串ID,Windows内核就是按块存储STRINGTABLE,没有任何API可以直接返回所有字符串ID,所有公开的STRINGTABLE枚举实现都是按块遍历实现的
内容的提问来源于stack exchange,提问作者Capslock
相关产品推荐
相关产品推荐

