使用C与Win32 API修改DLL字符串表失败求助
解决Win32 API修改DLL字符串表无效的问题
你遇到的核心问题是RT_STRING资源的存储格式和代码处理方式不匹配:Win32的字符串表并非按单个ID独立存储,而是以16个字符串为一组组成"块",每个块对应一个RT_STRING资源项;同时字符串必须是带长度标记的Unicode格式。你的代码直接写入ANSI字符串,未遵循该结构,导致资源无法被识别为字符串表。
关键问题拆解
- RT_STRING的块结构:每个RT_STRING资源项对应16个连续的字符串ID,比如块ID为18时,对应字符串ID 288~303(18×16=288)。你要修改的ID296属于块ID 18(296÷16=18),在块内的索引是8(296%16=8)。
- 字符串格式要求:每个字符串以单字节表示字符数(非字节数)开头,后跟UTF-16LE编码的Unicode字符;空字符串用0x00表示长度。整个块是这些字符串的连续序列。
- 编码错误:你传入的是ANSI字符串,但RT_STRING要求必须使用Unicode格式。
修正后的代码
#include <windows.h> #include <wchar.h> BOOL UpdateStringResource(LPCWSTR dllPath, UINT stringID, LPCWSTR newText) { // 计算目标字符串所在的块ID和块内索引 UINT blockID = stringID / 16; UINT indexInBlock = stringID % 16; HMODULE hModule = LoadLibraryExW(dllPath, NULL, LOAD_LIBRARY_AS_DATAFILE); if (!hModule) return FALSE; // 加载原有字符串块资源 HRSRC hRes = FindResourceW(hModule, MAKEINTRESOURCE(blockID), RT_STRING); HGLOBAL hGlobal = NULL; LPBYTE originalBlock = NULL; DWORD blockSize = 0; if (hRes) { hGlobal = LoadResource(hModule, hRes); originalBlock = (LPBYTE)LockResource(hGlobal); blockSize = SizeofResource(hModule, hRes); } // 确定新块的初始大小:沿用原有块大小或空块最小尺寸(16个空字符串占16字节) DWORD newBlockSize = blockSize ? blockSize : 16; LPBYTE newBlock = (LPBYTE)LocalAlloc(LPTR, newBlockSize); if (!newBlock) { FreeLibrary(hModule); return FALSE; } // 复制原有块数据(保留其他字符串) if (originalBlock && blockSize > 0) memcpy(newBlock, originalBlock, blockSize); // 定位到目标字符串在块中的位置 LPBYTE currentPos = newBlock; for (UINT i = 0; i < indexInBlock; i++) { if (currentPos >= newBlock + newBlockSize) break; // 超出原有块范围,视为空字符串 BYTE len = *currentPos; currentPos += 1 + len * 2; } // 限制字符串长度(单字节最多表示255个字符) BYTE newLen = (BYTE)wcslen(newText); if (newLen > 255) newLen = 255; // 按需扩容内存 DWORD neededSize = (currentPos - newBlock) + 1 + newLen * 2; if (neededSize > newBlockSize) { LPBYTE tempBlock = (LPBYTE)LocalReAlloc(newBlock, neededSize, LMEM_MOVEABLE | LMEM_ZEROINIT); if (!tempBlock) { LocalFree(newBlock); FreeLibrary(hModule); return FALSE; } newBlock = tempBlock; newBlockSize = neededSize; currentPos = newBlock + (currentPos - (LPBYTE)originalBlock); } // 写入新字符串的长度和Unicode内容 *currentPos = newLen; memcpy(currentPos + 1, newText, newLen * 2); // 更新DLL资源 HANDLE hUpdate = BeginUpdateResourceW(dllPath, FALSE); BOOL result = FALSE; if (hUpdate) { result = UpdateResourceW(hUpdate, RT_STRING, MAKEINTRESOURCE(blockID), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), newBlock, newBlockSize); EndUpdateResourceW(hUpdate, !result); } // 清理资源 LocalFree(newBlock); FreeLibrary(hModule); return result; } int main() { const wchar_t* dllPath = L"path/to/your_dll.dll"; const int resourceID = 296; const wchar_t* newText = L"Hello World"; if (!UpdateStringResource(dllPath, resourceID, newText)) { // 可调用GetLastError()获取错误码排查问题 return 1; } return 0; }
代码核心说明
- 块定位:通过整数运算快速找到目标字符串所属的块和位置,避免破坏其他字符串
- 资源保留:加载原有块数据,只修改目标位置的字符串,其余内容保持不变
- Unicode兼容:全程使用宽字符操作,严格匹配RT_STRING的编码要求
- 内存适配:动态调整块大小,确保新字符串有足够存储空间,同时兼容原有块结构
修改完成后,用Resource Tuner查看时,ID296的字符串会出现在对应字符串表中,且能被依赖该DLL的软件正确识别调用。
内容的提问来源于stack exchange,提问作者Cezar Catarau
相关产品推荐
相关产品推荐

