65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 UpdateResource 更改字符串资源

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.49/5 (7投票s)

2004 年 9 月 21 日

3分钟阅读

viewsIcon

101618

用于更改字符串资源的代码,确实有效!

引言

此例程展示了如何使用 UpdateResource 更改可执行文件中的单个字符串资源。

这一切的开始是因为我正在编写一个国际化的屏幕保护程序,它必须在所有 Windows 平台上工作,并且我需要在安装时能够将屏幕保护程序的名称更改为正确的语言。

对于 Win9x,这很容易,因为 .scr 文件名就是显示设置中显示的名称(要验证,在 9x 系统上,搜索 *.scr 文件),所以我所要做的就是重命名文件。 对于 NT4/2000/XP,情况就不同了。在这些操作系统上,屏幕保护程序的名称位于 .scr 文件的字符串资源 ID=1 中;文件名完全不同。

因此,我必须想办法在运行时更改字符串资源。

我知道 UpdateResource 是解决方案,但它的文档不完善,我发现在新闻组中,很多人在使用它处理字符串资源时遇到了很多问题。 这是一个有趣的 API 例程,即使它不起作用,似乎也永远不会返回错误代码!

好吧,我捅了捅,戳了戳,并在十六进制编辑器和资源查看器的帮助下进行调试,我终于弄清楚了。

让事情顺利工作的关键是

  • 首先将替换字符串转换为 Unicode(这一点有明确的文档记录)。
  • 除非您正在编写字符串 ID=0,否则传递给 UpdateResource 的缓冲区必须以一个或多个前导零的 WCHAR 开头; 您添加的每个 WCHAR 都会将字符串资源 ID 递增 1(因此在下面的代码中,要获取字符串 ID=1,我用一个零的 WCHAR 开始缓冲区)。
  • 在前导零 WCHAR 之后,包括一个包含字符串中字符数的 WCHAR,然后用实际转换的 Unicode 字符填充缓冲区。
  • 缓冲区必须是 WCHAR 对齐的,并且它的大小必须是偶数个 WCHAR(为了保证这一点,我使用 GlobalAlloc)。

我确信我的代码可以更简洁,并且上面的一些假设可能并不完全正确,尽管它们在我尝试代码的有限范围内有效。

PADDINGXX 谜团解决了吗?

我发现 UpdateResource 有一个无害但奇怪的副作用……如果它需要插入填充以正确对齐部分,它会根据需要插入重复的模式 PADDINGXX。 因此,如果您在文件中看到这些字节,则很可能是因为该文件已由 UpdateResource 处理。 如果您正在调试 UpdateResource,则输出文件中存在 PADDINGXX 字节的片段也可能表明您存在对齐或填充问题(但它们也可能无害地存在,例如,如果前面的部分需要填充几个字节)。

注意事项

  • 此代码在字符串表与其他字符串表分开的具有字符串资源 ID=1 的可执行文件(例如,屏幕保护程序)上进行了测试。
  • 在 Windows NT4/2000/XP 上有效(Win9x 不支持 UpdateResource 和相关 API)。

示例代码

// szSSPath = path to .exe file
// szSSName = replacement text
BOOL SetScreenSaverName(LPCTSTR szSSPath, LPCTSTR szSSName)
{
  HANDLE h = ::BeginUpdateResource(szSSPath,FALSE);
  if(!h)
  {
    // BeginUpdateResource failed
    return FALSE;
  }
  CString sNewString = szSSName;
  int iCharCount = sNewString.GetLength() + 1;
  LPWSTR pUnicodeString = new WCHAR[iCharCount];
  if(!pUnicodeString)
  {
    // new failed
    return FALSE;
  }
  DWORD dwUnicodeCharCount = 
     MultiByteToWideChar(CP_ACP, NULL, sNewString.GetBuffer(0), 
                               -1, pUnicodeString, iCharCount);
  HGLOBAL hGlob =
     GlobalAlloc(GHND, (dwUnicodeCharCount + 4) * sizeof(WCHAR) );
  if(!hGlob)
  {
    // GlobalAlloc failed
    delete pUnicodeString;
    return FALSE;
  }
  LPWSTR pBufStart = (LPWSTR)GlobalLock(hGlob);
  if(!pBufStart)
  {
    // GlobalLock failed
    GlobalFree(hGlob);
    delete pUnicodeString;
    return FALSE;
  }
  LPWSTR pBuf = pBufStart;
  pBuf++;
  // offset to make it string ID=1. Increment by more
  // to change to a different string ID

  // next 2 bytes is string length
  *(pBuf++) = (WCHAR)dwUnicodeCharCount-1;
  for(int i=0; i<(int)dwUnicodeCharCount-1; i++)
    // remaining bytes are string in wide chars (2 bytes each)
    *(pBuf++) = pUnicodeString[i];
  delete pUnicodeString;
  if(++dwUnicodeCharCount % 1)
    // make sure we write an even number
    dwUnicodeCharCount++;
  BOOL bSuccess = TRUE;
  if(!::UpdateResource(h, RT_STRING, MAKEINTRESOURCE(1), 
      MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 
      pBufStart, dwUnicodeCharCount * sizeof(WCHAR) ) )
  {
    // UpdateResource failed
    bSuccess = FALSE;
  }

  GlobalUnlock(hGlob);
  GlobalFree(hGlob);
  ::EndUpdateResource(h, FALSE); // write changes 
  return bSuccess; 
}
© . All rights reserved.