升级资源字符串到Unicode时,别忘了指定L前缀
摘要
解释:当在Windows资源字符串中使用Unicode转义序列(如\x2019)时,必须在字符串前加上L前缀使其成为宽字符字符串;否则转义序列会被误解为8位序列。
<p>不久前,我讨论了<a title="资源编译器默认使用CP_ACP,即使文件是UTF-8的微妙提示" href="https://devblogs.microsoft.com/oldnewthing/20190607-00/?p=102569">资源编译器默认使用CP_ACP,即使文件是UTF-8的微妙提示</a>。</p>
<p>在又一次<a title="确保Microsoft Visual C++编译器选择正确的源编码" href="https://devblogs.microsoft.com/oldnewthing/20241231-00/?p=110696">Visual Studio悄悄将文件编码从1252改为UTF-8</a>并破坏所有非ASCII字符串,再加上<a title="各种git diff查看器在拉取请求中如何表示文件编码更改" href="https://devblogs.microsoft.com/oldnewthing/20241230-00/?p=110692">Azure DevOps和Visual Studio在显示差异时忽略编码更改</a>之后,一位同事决定使用显式的Unicode转义<tt>\x####</tt>来表示非ASCII字符,从而一劳永逸地解决这个问题。这样,无论文件编码是1252还是UTF-8都无关紧要,因为这两种代码页在共同的ASCII子集上是一致的。</p>
<p>之前是</p>
<pre>IDS_AWESOME "That's great!"
</pre>
<p>改成了</p>
<pre>IDS_AWESOME "That\x2019s great!"
</pre>
<p>不幸的是,屏幕上显示的字符串是</p>
<blockquote class="q"><p>That 19s great!</p></blockquote>
<p>哪里出了问题?</p>
<p>如果要在字符串中编码Unicode,必须给引号字符串加上<code>L</code>前缀。否则,<tt>\xABCD</tt>序列会被解释为8位<tt>\xAB</tt>转义序列,后面跟着两个文字字符<tt>CD</tt>。在这种情况下,<tt>\x2019</tt>被解释为<tt>\x20</tt>(代表空格)后跟文字字符<tt>19</tt>,结果字符串为<tt>That␣19s great!</tt>。</p>
<p>正确的转换包括<code>L</code>前缀。</p>
<pre>IDS_AWESOME <span style="border: solid 1px currentcolor;">L</span>"That\x2019s great!"
</pre>
<p>本文<a href="https://devblogs.microsoft.com/oldnewthing/20260507-00/?p=112307">升级资源字符串到Unicode时,别忘了指定L前缀</a>最初发表在<a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>。</p>
查看缓存全文
缓存时间: 2026/05/16 03:32
# 当你将资源字符串升级为Unicode时,别忘了指定L前缀 - 《老调重弹》
来源:https://devblogs.microsoft.com/oldnewthing/20260507-00?p=112307
2026年5月7日
🎯 头脑风暴 3 条回应
不久前,我讨论过[资源编译器默认使用 CP_ACP,即使面对文件是 UTF-8 的微妙提示也是如此](https://devblogs.microsoft.com/oldnewthing/20190607-00/?p=102569)。
在又一次遭遇 [Visual Studio 偷偷将文件编码从 1252 改为 UTF-8](https://devblogs.microsoft.com/oldnewthing/20241231-00/?p=110696) 并破坏了所有非 ASCII 字符串,再结合 [Azure DevOps 和 Visual Studio 在显示差异时完全无视编码更改](https://devblogs.microsoft.com/oldnewthing/20241230-00/?p=110692) 之后,一位同事决定一劳永逸地解决问题——使用显式的 Unicode 转义序列 `\x####` 来表示非 ASCII 字符。这样一来,无论文件编码是 1252 还是 UTF-8 都无关紧要,因为这两种代码页在共同的 ASCII 子集上是一致的。
原来是这样写的:
```
IDS_AWESOME "That's great!"
```
被改成了:
```
IDS_AWESOME "That\x2019s great!"
```
不幸的是,最终屏幕上显示的字符串却是:
> That 19s great!
哪里出了问题?
如果你要将 Unicode 编码到字符串中,必须在带引号的字符串前面加上 `L` 前缀。否则,`\xABCD` 序列会被解释为一个 8 位的 `\xAB` 转义序列,后面跟着两个原义字符 `CD`。在这个例子中,`\x2019` 被解释为 `\x20`(编码为空格),后面跟着原文字符 `19`,结果字符串变成了 `That 19s great!`。
正确的转换应该包含 `L` 前缀:
```
IDS_AWESOME L"That\x2019s great!"
```
---
### 分类
### 主题
---
## 作者
**Raymond Chen**
Raymond 参与 Windows 的演变已超过 30 年。2003 年,他创办了名为《老调重弹》的网站,其受欢迎程度远超他最大胆的想象——这一发展仍让他不寒而栗。该网站衍生出一本书,巧合的是书名也是《老调重弹》(Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账号上,讲述一些毫无实际帮助的故事。
---
## 继续阅读
## 保持关注
当新文章发布时获取通知。
关注此博客
- https://twitter.com/ChenCravat
- YouTube (https://www.youtube.com/playlist?list=PLlrxD0HtieHge3_8Dm48C0Ns61I6bHThc)
- https://github.com/oldnewthing
- https://devblogs.microsoft.com/oldnewthing/feed/
相似文章
Unicode 字符串的等价性很奇怪 (2016)
Unicode 字符串等价性很复杂,尤其是涉及校对规则时,会导致意外的结果,例如删除控制字符和非确定性分组。作者讨论了在数据库系统中正确实现 Unicode 支持所面临的挑战。
遗产计算时代来临了吗?
本文追踪并报告了终端模拟器对Unicode的‘遗产计算符号’区块的支持情况,强调了在不同终端上显示遗产图形字符的兼容性。
使用特殊 Unicode 字符的域名创意拼写(2018)
一篇博客文章,探讨如何借助浏览器端依据 RFC5895 进行的 IDNA2008 规范化,在域名中使用不常见的 Unicode 类字母符号(如 ℡ 和 №)及样式化字母,并介绍其在 dig 等工具和社交平台上的局限性。
UTF-8000:无限制的UTF-8
UTF-8000 是对UTF-8的一个提议扩展,允许任意大的编码单元,同时保留UTF-8的特性,作为一个独立项目呈现,并附有参考实现。
关于滥用Windows窗口类额外字节的兼容性说明
Raymond Chen 讨论了一个历史性的 Windows 兼容性问题,其中一些 16 位程序滥用窗口类额外字节来存储私有数据,以及微软如何在保持向后兼容性的同时,对 32 位和 64 位程序堵住了这个漏洞。