一、注册表简介
注册表实际上是一个管理配置系统运行参数的核心数据库,它记录了安装软件与运行程序的关联关系,计算机的硬件配置等信息。可以说计算机上所有针对硬件、软件、网络的操作都是源于注册表的。
可以使用 regedit.exe 这个系统提供的注册表编辑器去管理编辑注册表,他只是一个编辑器不是注册表的文件。
如上图所示,注册表预设有五个根键:
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USER
HKEY_CURRENT_CONFIG
根键展开后可以看到跟键的子健(子项):像 "System, Software等都是HKEY_CURRENT_CONFIG的子健(子项)。
就像文件夹一样,每个键都可以创建子项,同时也可以拥有键值项。
键值项由3部分组成:名称、类型、值。
几个根键的用途说明:
-
HKEY_LOCAL_MACHINE 根键中包含了操作系统、安装软件及硬件的相关信息。如计算机总线类型、系统可用内存、当前装载了哪些设备驱动程序以及启动控制数据等。
实际上,HKEY_LOCAL_MACHINE 根键保存着注册表中的大部分信息,而另外4个根键都是其子键的别名。 -
HKEY_CURRENT_USER 根键包含这当前登录到计算机上的用户的配置文件。其子键包含着环境变量、个人程序组、桌面设置、网络连接、打印机和应用程序首选项等信息。
计算机把当前用户的信息映射到这个根键下,若未激活用户配置,则它指向子键 HKEY_USERS.DEFAULT。 -
HKEY_CLASSES_ROOT 根键记录的是系统中各类文件与其应用程序之间的对应关系,即记录了某类文件和打开该类文件的应用程序之间的相互关联关系。
HKEY_CLASSES_ROOT 根键是 HKEY_LOCAL_MACHINE\SOFTWARE\Classes 的快捷方式,是注册表的一个最大分支,包括了成千上万的与程序、文件相关联的键和值以及ActiveX类的定义等内容。 -
HKEY_USERS 根键下包含了计算机的所有用户的信息。用户根据个人爱好设置的诸如桌面、背景、开始菜单程序项、应用程序快捷键、显示字体、屏幕节电设置等信息均记录在这个跟建中
HKEY_CURRENT_USER 也是 HKEY_USERS 其中的的一个快捷键部分。 -
HKEY_CURRENT_CONFIG 根键包含的主要内容是计算机的当前配置情况,如显示器、打印机等可选外部设备及其设置信息等。
二、常用API介绍
1.RegCreateKeyEx() 创建注册表键
MSDN: RegCreateKeyExA function (winreg.h)
功能:创建或打开注册表键。
函数原型:
LONG RegCreateKeyEx(
HKEY hKey, // 已打开的键的句柄 (RegOpenKeyEx, RegCreateKeyEx 的返回值) 或者 预定义的值( 即根键)。
LPCTSTR lpSubKey, // 指向 要创建或要打开的子健名称。
DWORD Reserved, // 保留值必须为 0,
LPTSTR lpClass, // 字符串指针,指向用户自定义的键类别名称。可以设为NULL。
DWORD dwOptions, // 新创建的键的属性。
REGSAM samDesired, // 键的访问权限。
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 指向 SECURITY_ATTRIBUTES 结构的指针,用于定义返回的句柄是否可以被子进程继承,为 NULL 表示不继承。
PHKEY phkResult, // 指向新创建或打开的键的句柄的指针,保存返回的句柄。
LPDWORD lpdwDisposition // 指明键是被创建还是被打开的
);
参数:
【入参】hKey: 已经打开的注册表键的句柄。
可以是以下的值:
- HKEY_CLASSES_ROOT
- HKEY_CURRENT_CONFIG
- HKEY_CURRENT_USER
- HKEY_LOCAL_MACHINE
- HKEY_USERS
- RegOpenKeyEx或RegCreateKeyEx 返回的phkResult
【入参】dwOptions: 键的属性。
可以是以下的值:
- REG_OPTION_NON_VOLATILE 新创建的键为一个非短暂性的键 (数据信息保存在文件中,当系统重新启动时,数据信息恢复)
- REG_OPTION_VOLATILE 新创建的键为一个短暂性的键(数据信息保存在内存中)。
- REG_OPTION_BACKUP_RESTORE 仅在WINNT中支持,可以提供优先级支持。
【入参】samDesired: 键的访问权限。
- KEY_ALL_ACCESS 包含所有权限。
- KEY_CREATE_LINK 系统保留。
- KEY_CREATE_SUB_KEY 允许创建子键。
- KEY_ENUMERATE_SUB_KEYS 允许枚举子健。
- KEY_EXECUTE 等同于KEY_READ。
- KEY_NOTIFY 允许提供更改通知(详见 RegNotifyChangeKeyValue)
- KEY_QUERY_VALUE 允许查询键值
- KEY_READ 读取权限,包括STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS 和 KEY_NOTIFY 。
- KEY_SET_VALUE 创建、删除、设置键值。
- KEY_WOW64_32KEY 32位注册表/64位注册表路径重定向,设置后将访问32位注册表路径。
- KEY_WOW64_64KEY 32位注册表/64位注册表路径重定向,设置后将访问64位注册表路径。
- KEY_WRITE 写权限包括 STANDARD_RIGHTS_WRITE, KEY_SET_VALUE 和
【出参】phkResult: 创建或打开的键的句柄。
【出参】lpdwDisposition: 指明键是被创建还是被打开的。
REG_CREATED_NEW_KEY 键先前不存在,现在被创建。
REG_OPENED_EXISTING_KEY 键先前已存在,现在被打开。
【返回值】如果函数调用成功,则返回 ERROR_SUCCESS。否则返回非零的错误代码。
关于KEY_WOW64_32KEY/KEY_WOW64_64KEY几点说明
- 只适用于64位系统,以下所说情况都是在64位系统下。
- 在64位系统中,64位程序默认访问64位注册表,32位程序默认访问32位注册表,两者路径是不一样的。
比如 :分别用64位程序和32位程序访问注册表路径 “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft” ,
64位程序访问的真实路径就是“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft”。
而32位程序访问的真实路径是"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft"。 - 如果想用32位程序访问“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft” ,需要在samDesired 添加KEY_WOW64_64KEY。同理64位程序如果想访问Wow6432Node目录,需要使用KEY_WOW64_32KEY。
- 示例
//32位程序访问64位注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft
DWORD lRtn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft", 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &hKey);
//64位程序访问32位注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft
DWORD lRtn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft", 0, KEY_ALL_ACCESS|KEY_WOW64_32KEY, &hKey);
2.RegOpenKeyEx() 打开注册表
MSDN:RegOpenKeyExA function (winreg.h)
功能:打开注册表键。
函数原型:
LSTATUS RegOpenKeyExA(
HKEY hKey, //已打开的键的句柄或根键
LPCSTR lpSubKey, //子健相对路径
DWORD ulOptions, //一般设为0 ,也可以是REG_OPTION_OPEN_LINK表示该键为符号链接(未使用过,待测试)
REGSAM samDesired,//访问需要的权限
PHKEY phkResult //返回的键句柄
);
【返回值】如果函数调用成功,则返回 ERROR_SUCCESS。否则返回非零的错误代码。
3.RegDeleteKeyEx() 删除注册表键
MSDN: RegDeleteKeyExA function (winreg.h)
功能:删除注册表键。
函数原型:
LSTATUS RegDeleteKeyExA(
HKEY hKey, //已打开的键的句柄或根键
LPCSTR lpSubKey, //子健相对路径
REGSAM samDesired, //访问32位路径还是64位注册表路径 使用KEY_WOW64_32KEY 或 KEY_WOW64_64KEY
DWORD Reserved //保留,必须为0
);
【返回值】如果函数调用成功,则返回 ERROR_SUCCESS。否则返回非零的错误代码。
4.RegCloseKey() 关闭注册表句柄
MSDN:RegCloseKey function (winreg.h)
功能:关闭已经打开的注册表句柄。
函数原型:
LSTATUS RegCloseKey(
HKEY hKey //已打开的键的句柄
);
【返回值】如果函数调用成功,则返回 ERROR_SUCCESS。否则返回非零的错误代码。
5.RegSetValueEx() 写注册表
MSDN:RegSetValueExA function (winreg.h)
功能:设置注册表的键值项。
函数原型:
LSTATUS RegSetValueExA(
HKEY hKey, //已打开的键的句柄
LPCSTR lpValueName, //键值名称,如果为NULL或“”,则设置键值名为“默认”的项
DWORD Reserved, //保留,必须为0
DWORD dwType, //设置的键值数据类型
const BYTE *lpData, //指向键值数据的指针
DWORD cbData //设置的键值数据长度
);
【入参】dwType:设置的键值数据类型
可以是以下值:
- REG_BINARY 二进制数据
- REG_DWORD 32字节数据
- REG_DWORD_LITTLE_ENDIAN 小端的4字节数据(windows是基于小端处理器设计的,所以等同于REG_DWORD)
- REG_DWORD_BIG_ENDIAN 大端的4字节数据
- REG_EXPAND_SZ 可扩展的字符串,此类型的键值中存在的%xxx%的部分会被系统解释,而REG_SZ不会
- REG_LINK 指向符号链接的注册表项路径的字符串
- REG_MULTI_SZ 多个字符串,用\0分隔,例如:String1\0String2\0String3\0LastString\0\0
- REG_NONE 未定义的类型
- REG_QWORD 8字节数据
- REG_QWORD_LITTLE_ENDIAN 等同于REG_QWORD
- REG_SZ 字符串
【返回值】如果函数调用成功,则返回 ERROR_SUCCESS。否则返回非零的错误代码。
6.RegQueryValueEx() 读注册表
MSDN: RegQueryValueExA function (winreg.h)
功能:根据名称获取对应注册表项的数据类型和数据。
(要确保返回的任何字符串值(REG_SZ、REG_MULTI_SZ和REG_EXPAND_SZ)都以null结尾,请使用RegGetValue函数。)
函数原型:
LSTATUS RegQueryValueExA(
HKEY hKey, //已打开的键的句柄
LPCSTR lpValueName, //键值名称,如果为NULL或“”,则设置键值名为“默认”的项
LPDWORD lpReserved, //保留,必须为0
LPDWORD lpType, //【出参】,返回数据类型
LPBYTE lpData, //【出参】, 返回数据
LPDWORD lpcbData //【出参】, 返回数据长度
);
【返回值】如果函数调用成功,则返回 ERROR_SUCCESS。否则返回非零的错误代码。
7.RegGetValue() 读注册表
MSDN:RegGetValueA function (winreg.h)
功能:根据名称获取对应注册表项的数据类型和数据。
函数原型:
LSTATUS RegGetValueA(
HKEY hkey, //已打开的键的句柄,一般是根键
LPCSTR lpSubKey, //hKey的子键路径
LPCSTR lpValue, //键值项名称
DWORD dwFlags, //对于返回键值类型的限制,如果不满足,函数会执行失败
LPDWORD pdwType, //【出参】 返回的数据类型
PVOID pvData, //【出参】, 返回数据
LPDWORD pcbData //【出参】, 返回数据长度
);
【入参】 dwFlags:对于返回键值类型的限制,如果不满足,函数会执行失败。
可以是以下值或它们的组合(按位或|):
- RRF_RT_ANY 无任何限制
- RRF_RT_DWORD 允许32位RRF_RT_REG_BINARY或RRF_RT_REG_DWORD类型
- RRF_RT_QWORD 允许64位RRF_RT_REG_BINARY或RRF_RT_REG_QWORD类型
- RRF_RT_REG_BINARY 仅允许REG_BINARY类型
- RRF_RT_REG_DWORD 仅允许REG_DWORD类型
- RRF_RT_REG_EXPAND_SZ 仅允许 REG_EXPAND_SZ类型
- RRF_RT_REG_MULTI_SZ 仅允许 REG_MULTI_SZ类型
- RRF_RT_REG_NONE 仅允许 REG_NONE
- RRF_RT_REG_QWORD 仅允许 REG_QWORD
- RRF_RT_REG_SZ仅允许 REG_SZ
- RRF_NOEXPAND 当数据类型为REG_EXPAND_SZ时,不自动展开环境变量字符串。
- RRF_ZEROONFAILURE 当pvData 不为NULL时,执行失败会把缓冲区全置为0。
- RRF_SUBKEY_WOW6464KEY 访问64位注册表路径。
- RRF_SUBKEY_WOW6432KEY 访问32位注册表路径。
RegGetValue()较于RegQueryValueEx()优点:
1.RegGetValue不需要打开注册表句柄(这个API自动会打开和关闭句柄)
2.RegGetValue可以限制键值的类型,如果注册表中的类型跟自己想要的类型(由dwFlags设置的)不符,函数会返回0x0000065E错误 :“这个类型的数据不受支持。 ” 。值得注意的是,即使此时返回值不是ERROR_SUCCESS ,pdwType 和pvData仍然会返回正确的数据。
3.RegQueryValueEx不保证查询的字符串已NULL结尾,而RegGetValue会进行检查。(经过个人测试,并未发现此类情况,RegQueryValueEx仍然需要包含\0结尾的大小的buffer才能执行成功。只不过当数据类型是REG_MULTI_SZ 时,RegQueryValueEx返回的数据结尾是\0,而RegGetValue返回的数据结尾是\0\0,环境: win7 64)。
8.RegEnumKeyEx() 枚举注册表子健
MSDN:RegEnumKeyExA function (winreg.h)
功能:枚举子健名称。
函数原型:
LSTATUS RegEnumKeyExA(
HKEY hKey, //已打开的键的句柄
DWORD dwIndex, //子健索引
LPSTR lpName, //【出参】子健名称
LPDWORD lpcchName, //【入参/出参】子健名称缓冲区大小/返回的子健名称长度
LPDWORD lpReserved, //保留,必须为0
LPSTR lpClass, //【出参】类名名称,如果不需要,可以传NULL
LPDWORD lpcchClass, //【入参/出参】类名缓冲区大小/返回的类名长度
PFILETIME lpftLastWriteTime // FILETIME指针,返回子健最后的更改日期,可以创NULL
);
【返回值】如果函数调用成功,则返回 ERROR_SUCCESS,如果全部枚举完毕,返回ERROR_NO_MORE_ITEMS,其他情况返回非零的错误代码。
9.RegEnumValue() 枚举键值项
MSDN:RegEnumValueA function (winreg.h)
功能:枚举键值项。
函数原型:
LSTATUS RegEnumValueA(
HKEY hKey, //已打开的键的句柄
DWORD dwIndex, //键值项索引
LPSTR lpValueName, //【出参】键值项名称
LPDWORD lpcchValueName, //【入参/出参】键值项名称缓冲区大小/返回的键值项名称长度
LPDWORD lpReserved, //保留,必须为0
LPDWORD lpType, //【出参】键值项的数据类型
LPBYTE lpData, //【出参】键值项数据
LPDWORD lpcbData //【入参/出参】数据长度
);
【返回值】如果函数调用成功,则返回 ERROR_SUCCESS,如果全部枚举完毕,返回ERROR_NO_MORE_ITEMS,其他情况返回非零的错误代码。
示例代码
#include <iostream>
#include <windows.h>
using namespace std;
int main() {
HKEY hKey = NULL;
LONG lRtn = 0;
DWORD dwDisposition = 0;
//创建注册表键
lRtn = RegCreateKeyEx( HKEY_CURRENT_CONFIG,
"Software\\nibin",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwDisposition );
if (lRtn != ERROR_SUCCESS) {
cout << "RegCreateKeyEx error : 0x" << hex << lRtn << endl;
return -1;
}
if (dwDisposition == REG_CREATED_NEW_KEY) {
cout << "创建新键 HKEY_CURRENT_CONFIG\\Software\\nibin" << endl;
}
else {
cout << "打开已存在的键 HKEY_CURRENT_CONFIG\\Software\\nibin" << endl;
}
RegCloseKey(hKey); //关闭注册表句柄
hKey = NULL;
//打开注册表,权限设为KEY_ALL_ACCESS,以便下一步读写操作
lRtn = RegOpenKeyEx(HKEY_CURRENT_CONFIG, "Software\\nibin", 0, KEY_ALL_ACCESS, &hKey);
if (lRtn != ERROR_SUCCESS) {
cout << "RegOpenKeyEx error : 0x" << hex << lRtn << endl;
return -1;
}
//写注册表
char writeData[] = { "Hello Registry!" };
DWORD cbWriteData = sizeof(writeData);
lRtn = RegSetValueEx(hKey, "RegeditTest", 0, REG_SZ, (BYTE*)writeData, cbWriteData);
if (lRtn != ERROR_SUCCESS) {
cout << "RegSetValueEx error : 0x" << hex << lRtn << endl;
RegCloseKey(hKey); //关闭注册表句柄
hKey = NULL;
return -1;
}
DWORD dwType;
DWORD dataLen = 0;
//读注册表,首先传NULL获取缓冲区需要的大小,如果已知数据的大小,可以直接传入一个足够大数组。
lRtn = RegQueryValueEx(hKey, "RegeditTest", 0, &dwType, NULL, &dataLen);
if (lRtn != ERROR_SUCCESS) {
cout << "RegQueryValueEx error : 0x" << hex << lRtn << endl;
RegCloseKey(hKey); //关闭注册表句柄
hKey = NULL;
return -1;
}
BYTE* readData = new BYTE[dataLen];
//读注册表
lRtn = RegQueryValueEx(hKey, "RegeditTest", 0, &dwType, readData, &dataLen);
if (lRtn != ERROR_SUCCESS) {
cout << "RegQueryValueEx error : 0x" << hex << lRtn << endl;
RegCloseKey(hKey); //关闭注册表句柄
hKey = NULL;
return -1;
}
else {
cout << "use RegQueryValueEx readData : " << (char*)readData << endl;
}
delete[] readData;
readData = NULL;
RegCloseKey(hKey); //关闭注册表句柄
hKey = NULL;
char getData[64] = { 0 };
dataLen = sizeof(getData);
//使用RegGetValue读注册表不需要open和close操作
lRtn = RegGetValue(HKEY_CURRENT_CONFIG, "Software\\nibin", "RegeditTest", RRF_RT_ANY, &dwType, getData, &dataLen);
if (lRtn != ERROR_SUCCESS) {
cout << "RegGetValue error : 0x" << hex << lRtn << endl;
RegCloseKey(hKey); //关闭注册表句柄
hKey = NULL;
return -1;
}
else {
cout << "use RegGetValue readData : " << getData << endl;
}
//打开注册表,权限设为KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,枚举子健,读键值项数据
lRtn = RegOpenKeyEx(HKEY_CURRENT_CONFIG, "Software", 0, KEY_ENUMERATE_SUB_KEYS| KEY_QUERY_VALUE, &hKey);
if (lRtn != ERROR_SUCCESS) {
cout << "RegOpenKeyEx error : 0x" << hex << lRtn << endl;
return -1;
}
//在枚举子健和键值项之前最好先调用RegQueryInfoKey 获取子健和键值项的数目,用来结束枚举循环;
//也可以不调用RegQueryInfoKey,直接判断RegEnumKeyEx和RegEnumValue返回值来结束枚举循环。
DWORD cSubKeys = 0;
DWORD cValues = 0;
lRtn = RegQueryInfoKey(hKey, NULL, NULL, 0, &cSubKeys, NULL, NULL, &cValues, NULL, NULL, NULL, NULL);
if (lRtn != ERROR_SUCCESS) {
cout << "RegQueryInfoKey error : 0x" << hex << lRtn << endl;
RegCloseKey(hKey); //关闭注册表句柄
hKey = NULL;
return -1;
}
else {
cout << "RegQueryInfoKey HKEY_CURRENT_CONFIG\\Software has " << cSubKeys
<< " SubKeys and " << cValues << " Items" << endl;
}
lRtn = ERROR_SUCCESS;
char szSubKeyName[1024] = { 0 };
DWORD cbSubKeyName = sizeof(szSubKeyName);
//枚举HKEY_CURRENT_CONFIG\Software子健
for (int dwIndex = 0; dwIndex < cSubKeys; ++dwIndex) {
lRtn = RegEnumKeyEx(hKey, dwIndex, szSubKeyName, &cbSubKeyName, 0, 0, 0, 0);
if (lRtn == ERROR_SUCCESS) {
cout << "EnumSubKey : " << szSubKeyName << endl;
cbSubKeyName = sizeof(szSubKeyName);//如果是用同一数组保存Name,注意重置cbSubKeyName为szSubKeyName分配的空间大小
}
else {
cout << "RegEnumKeyEx error : 0x" << hex << lRtn << endl;
}
}
lRtn = ERROR_SUCCESS;
char szItemName[1024] = { 0 };
DWORD cbItemName = sizeof(szItemName);
BYTE Data[1024] = { 0 };
DWORD cbData = sizeof(Data);
//枚举HKEY_CURRENT_CONFIG\Software键值项
for (int dwIndex = 0; dwIndex < cValues ; ++dwIndex) {
lRtn = RegEnumValue(hKey, dwIndex, szItemName, &cbItemName, 0, 0, Data, &cbData);
if (lRtn == ERROR_SUCCESS) {
cout << "EnumItem : " << szItemName <<" Data : "<<Data<< endl;
cbData = sizeof(Data); //如果是用同一数组保存Name,注意重置为cbData分配的空间大小
}
else{
cout << "RegEnumValue error : 0x" << hex << lRtn << endl;
}
}
RegCloseKey(hKey); //关闭注册表句柄
hKey = NULL;
return 0;
}
未完待续。。。