昨天在用 C++ 操作 Windows 注册表(Registry)的时候,犯了一个非常低级的错误,现在把有关注册表的一些相关知识写下来,以免再犯。

  1. 注册表的基本概念

Windows 的注册表中包含了 Windows 的系统配置、PC机的硬件配置、Win32应用程序和其他设置信息。注册表和 INI 文件不同,它是多层次的树状数据结构,具有六个分支(根键),每个分支又由许多的键(key)和值(value)组成,每个键则代表一个特定的配置项目。在注册表中,键可以包括子键(sub key)和值(value)。我们可以对它们做个比喻,键就是目录,而子键和值可以看成子目录和文件,这样比喻虽然不准确但是很形象。

  1. 注册表里值(value)的数据类型

注册表里的值(value)可以是字符串、多字符串或是二进制值等,详细的解释可以看看MSDN上的相关解释

  1. 写入多字符串类型数据时容易犯的错误

我在注册表编辑器里,在一个键下面直接新建了一个多字符串(REG_MULTI_SZ)的值,编辑该值的时候,输入完一个字符串就用回车来分隔,输了几行,就成了下面的情形:

REG_MULTI_SZ_editCopyright.png

测试完我该输入的东西后,我就开始用C++编码来实现这个步骤、让它自动化。

在编码需要一次性插入到多字符串(REG_MULTI_SZ)值里的多个字符串时,我就想当然地以为是用回车来分隔这若干个字符串(因为前面我在编辑器里就是用回车来分隔的嘛),于是在代码里把”\r\n”放在两个字符串之间。

但是,在测试代码的时候,用”\r\n”写出来的字符串,与之前手动编辑时添加回车的结果完全不一样。我找了很久原因,最后通过对比注册表值的二进制编码,才发现了原因。

REG_MULTI_SZ_edit2Copyright.png REG_MULTI_SZ_edit3Copyright.png

**其实,注册表里的多字符串(REG_MULTI_SZ)值是用NULL来分割的。在注册表编辑器里手动编辑后,有”\r\n”的地方自动被更替换成了””。因为规定了注册表里的字符串(REG_SZ)只能以一个空的字符结束,多字符串则需要以两个空字符串结束。**当然,真正的规则稍微复杂一些,看MSDN里有关多字符串的说明:

A sequence of null-terminated strings, terminated by an empty string ().

The following is an example:

String1String2String3LastString

The first terminates the first string, the second to the last terminates the last string, and the final terminates the sequence. Note that the final terminator must be factored into the length of the string.

The values are separated by null characters and terminated by an empty string.

  1. 注册表里值的大小是有限制的

放到注册表里的值不能超过2048 bytes,如果超过这个大小,最好存放在文件里,然后让注册表去读这个文件:

Value sizes are limited by available memory. Long values (more than 2048 bytes) should be stored as files with the file names stored in the registry. This helps the registry perform efficiently. Application elements such as icons, bitmaps, and executable files should be stored as files and not be placed in the registry.

  1. MoveFileEx()并且带上MOVEFILE_DELAY_UNTIL_REBOOT的参数,也可以实现通过Windows注册表来实现开机时文件替换

这个页面里也有更多有关重启系统替换文件的知识。

  1. 常用的注册表API函数有RegOpenKeyExRegQueryValueExRegSetValueEx等。

要判断一个Value是否存在于某个键下面,可以使用RegQueryValueEx函数,如果返回的是ERROR_FILE_NOT_FOUND,则表明该值不存在。

  1. Windows 的 API 并不提供在一个值后面追加值的方法,如果你要在原来的值后面追加一个值,则你只能把这个值读出来、追加,然后再写入。