You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C与Win32 API修改DLL字符串表失败求助

解决Win32 API修改DLL字符串表无效的问题

你遇到的核心问题是RT_STRING资源的存储格式和代码处理方式不匹配:Win32的字符串表并非按单个ID独立存储,而是以16个字符串为一组组成"块",每个块对应一个RT_STRING资源项;同时字符串必须是带长度标记的Unicode格式。你的代码直接写入ANSI字符串,未遵循该结构,导致资源无法被识别为字符串表。

关键问题拆解

  1. RT_STRING的块结构:每个RT_STRING资源项对应16个连续的字符串ID,比如块ID为18时,对应字符串ID 288~303(18×16=288)。你要修改的ID296属于块ID 18(296÷16=18),在块内的索引是8(296%16=8)。
  2. 字符串格式要求:每个字符串以单字节表示字符数(非字节数)开头,后跟UTF-16LE编码的Unicode字符;空字符串用0x00表示长度。整个块是这些字符串的连续序列。
  3. 编码错误:你传入的是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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:07:25