相信很多人在第一次针对一个字符串资源使用FindResource的时候,都会这么调用:

FindResource(hinst, MAKEINTRESOURCE(IDS_STRINGID), RT_STRING);

然后基本上你就会发现明明这个字符串是存在的,却就是找不到!是的,系统没问题,编译器没问题,你的眼睛也没有问题。事实上,正确的调用方法应该是:

FindResource(hinst, MAKEINTRESOURCE(IDS_STRINGID/16+1), RT_STRING);

这是由字符串资源特殊的组织方式决定的。

字符串和Section

让我们先来看一下MSDN中是怎么说的:

String resources are stored in sections of up to 16 strings per section. The strings in each section are stored as a sequence of counted (not null-terminated) Unicode strings.

每 16个连续的字符串组成一个section,而FindResource只会找到相应的section,而不是精确到你要找的字符串,因此需要做一个String ID到Section ID的转换:

SectionID = StringID/16 + 1

0-15是第一个section, 16到31是第二个section。一个section中只要有一个StringID是存在的,那么这个section就是有效的。所以当你调用FindResource时发现即使传入一个不存在的String ID时也能找到resource,不要觉得奇怪。(更多内容可以参考The format of string resources)

我们来看个例子,假设一个rc文件中定义了如下字符串资源:

IDS_STRING96 ""
IDS_STRING97 "97"
IDS_STRING99 "99"
IDS_STRING111 "111"

我们可以通过一个工具来查看其内容:  

字符串ID为96, 其SectionID为96/16+1=7,这里我们可以看到在第7个block中所有的字符串,其中未定义的字符串全部为空。

但同时我们也注意到另外一个问题,即定义为空的字符串(如96)和未定义的字符串(如98)在这里都表示为空,也就是我们无法分辨一个空字符串与不存在的字符串,事实如此吗?

内存模型

我们可以通过rc文件编译出来的res中间文件来观察。以二进制的形式打开res文件并搜索字符串 "111":

我们看中间的红框,前两个字节"02 00"表示此字符串长度为2,"39 00"即为16进制0x39,是字符9的ASCII码,同理"37 00"表示字符7,即红框表示的正是字符串:"97"。于是我们可以推出两个绿框表示的内容:两个字节的"00 00"表示的就是字符串长度为0,所以空的字符串(96)和未定义的字符串(98)在目标文件中表示是一样的。因为最终的DLL或Exe是由链接此res中间文件产生,所以可以肯定最终运行时的内存模型中,这两者表示也是毫无二致的。

运行如下代码:

HRSRC hRsrc = FindResource(NULL,MAKEINTRESOURCE(96/16+1),RT_STRING);

HGLOBAL hg = LoadResource(NULL, hRsrc);

观察hg指向的内存地址:

和res文件中观察到的内容是一样的,也就是说,空串在编译的时候就已经被删除掉了。实质上,空字符串就是未定义字符串。

建议

这种把空串与未定义串混为一谈的作法在大多数情况下还是可以接受的,但在做本地化的时候就会遇到一些问题:
一个字符串在中文版中要显示相应的内容,而由于产品设计上的考虑,在英文版中需要显示为空字符串。这种本地化工作我们一般用替换资源DLL来完成的,真正LoadString的代码是同一份的,当我LoadString返回一个空串时,我该认为是正确的还是错误的呢?
如果认为是正确的,那么在中文版下字符串真的出错的情况就判断不出来了。
如果认为是错误的,那么在英文版下空字符串就永远是错的。

很显然,陷入这样的困境原因在于我们无法区分空串与未定义串,而其根本原因在于字符串内存模型的设计缺陷(可以理解其如此设计是可以节省内存空间和字符串加载时的速度)。

或许,我们可以对其稍加修改以解决这个问题:
空串:01 00 00 00
未定义串:00 00

这里,我们把空串解释为长度为1,内容为"00 00"(即"\0")的字符串,因为"\0"在字符串资源中本来就已经做了特殊处理,因此这样的修改不会引起什么冲突。如此,我们就能区分空串与未定义串了。而且,因为空串出现的几率比较小,对内存空间的消耗并不会增加多少。

为使这个方案能够工作,至少需要修改:
Windows API中的LoadString函数
    Load不存在的字符串时报错(SetLastError)
VS中的Resource Compiler
    把空串编译成 "01 00 00 00"

字符串资源的内部格式相关推荐

  1. java内部格式_详解java内部类的访问格式和规则

    详解java内部类的访问格式和规则 1.内部类的定义 定义一个类来描述事物,但是这个事物其中可能还有事物,这时候在类中再定义类来描述. 2.内部类访问规则 ①内部类可以直接访问外部类中的成员,包括私有 ...

  2. TGA文件内部格式详解(转)

    来自:http://blog.sina.com.cn/s/blog_5ff6097b0100xtvw.html 继位图之后,我们来看看Tga图片的格式,以及程序实现. 一.  文件格式 Tga常见的格 ...

  3. 从pdf和字体内部格式简单分析pdf复制提取文字乱码的原理

    wxleasyland@139.com 2021.12 参考别人<PDF内嵌字体分析 - 提取的文字是乱码原因分析>. 一般PDF文件中都是有嵌入字体的,这样阅读器可以正常显示. &quo ...

  4. HBase HFile与Prefix Compression内部实现全解–KeyValue格式

    1.引子 HFile(HBaseFile)是HBase使用的一种文件存储格式的抽象, 目前存在两种版本的HFile:HFileV1和HFileV2 HBase0.92之前的版本仅支持HFileV1, ...

  5. SAP搜索帮助内部错误:表格格式

    导语:最近发现有些同事的SAP的标准搜索帮助有bug,会弹出内部错误,如下图,以为是SAP安装的问题,就不断的重新安装,最后找到了BASIS提供了解决方案,这其实是一个BUG,需要设置成另外一中形式, ...

  6. python整数格式显示_[python之路]格式化显示

    格式化显示 以下整理自 python字符串格式化 *输出结果的空格在md预览中没效果(用代码块三个撇号就可以保留格式了) 一.使用格式化符来格式化字符串: Python支持的所有格式化符: 格式化符 ...

  7. mysql空间是什么格式_MySQL数据类型 - 空间数据类型 (6)

    获取空间数据 存储在表中的几何值可以用内部格式获取,也可以将其转换为WKT或WKB格式. ●获取内部格式的空间数据: 在表到表传输中,使用内部格式获取几何值非常有用: ●获取WKT格式的空间数据: S ...

  8. Android应用开发(15)---字符串资源

    字符串资源 字符串资源为您的应用提供具有可选文本样式和格式设置的文本字符串. 共有三种类型的资源可为您的应用提供字符串: String 提供单个字符串的 XML 资源. String Array 提供 ...

  9. 脚本_批量修改md为hexo标准post格式[博]

    原创博客地址:脚本_批量修改md为hexo标准post格式[博] 动机,原有md希望快捷发布 本来原来有笔记,已经为md格式,希望批量发布.而不是一个个hexo new page xxx,然后复制过去 ...

  10. mov格式怎么转换成mp4?

    mov格式怎么转换成mp4?之前都是在看mp4格式的视频,直到遇到Apple公司开发的mov格式,我才知道mov格式是一种音频和视频文件格式,而这类的格式的文件是无法被其他设备支持播放的,通常都是把m ...

最新文章

  1. as3 android白屏,Android 8.0中一些坑以及对应的解决方法
  2. Lua和C++交互总结(很详细)
  3. java 快排非递归_C++ 中快排的递归和非递归实现
  4. Python中的Number(数字)
  5. 监听手指是否离开屏幕android_Flutter事件监听
  6. flowable实战(八)flowable核心数据库表详细表字段说明
  7. hibernate annotation注解方式来处理映射关系
  8. Exceptions Errors - 异常与错误
  9. Vuforia3D模型上传
  10. HTML 参考手册 - 浏览器支持
  11. c语言日程报告闹钟,可以闹钟提醒的日程表,日程闹钟提醒怎么弄
  12. 银联扫码支付java,银联商务扫码支付-被扫业务
  13. 牛牛卡牌游戏 javascript
  14. 四分位数(定义、位置、数值)
  15. 14款开源或免费的GIS软件
  16. Java基础教程:dubbo源码解析-服务暴露与发现
  17. 7-6 厘米换算英尺英寸 (15 分)
  18. c语言指针一步错步步错,一步错步步错,可是到底错在哪里
  19. 用DIV+CSS技术设计的游戏企业网页(网页制作课作业)
  20. 最值得入手的五款骨传导耳机,几款高畅销的骨传导耳机

热门文章

  1. mysql replicatedodb_MySQL存储引擎MyISAM与InnoDB的区别
  2. vue 异步更新队列 Vue.nextTick(callback)
  3. React antD-Pro 添加函数防抖
  4. 力扣-1046 最后一块石头的重量
  5. 在串口输入input keyevent发送按键值给机器
  6. shiro框架学习(一)
  7. oracle操作字符串:拼接、替换、截取、查找、长度、判断
  8. luogu p1799 数列_NOI导刊2010提高(06)
  9. CMake语法及CMakeList.txt简单使用
  10. Cocos2d-xV3.17.2 win32平台模板源码详注