参考及摘抄自文章:http://www.cnblogs.com/lartely/archive/2011/04/10/2011770.html
http://blog.csdn.net/sunboy_2050/article/details/7753662
基础知识:
注册表的组织方式跟文件目录比较相似,主要分为根键、子键和键值项三部分,与文件目录对应的话就是根目录、子目录和文件。分别介绍一下这三部分:
1.根键:共有5个,分别为HKEY_CLASSES_ROOT,HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE,HKEY_USERS和HKEY_CURRENT_CONFIG,把它们理解成磁盘的五个分区可以了。
2.子键:可以有多个子键和键值项,就像一个目录中可以有多个子目录和多个文件一样。
3.键值项:可以理解为文件,它由三部分组成,分别为:名称、类型、数据。
类型又分为多种主要包括如下:
REG_BINARY 二进制数据
REG_DWORD 32位双字节数据
REG_SZ 以0结尾的字符串
REG_DWORD_BIG_ENDIAN 高位排在底位的双字
REG_EXPAND_SZ 扩展字符串,可以加入变量如%PATH%
REG_LINK UNICODE 符号链接
REG_RESOURCE_LIST 设备驱动程序资源列表
REG_MULTI_SZ 多字符串
注册表数据项的数据类型有8种,但最常用的主要是前3种。
常用API:
1.打开/关闭注册表键
LONG WINAPI RegOpenKeyEx(
_In_ HKEY hKey, // 父键句柄
_In_opt_ LPCTSTR lpSubKey, // 子键的名称
_Reserved_ DWORD ulOptions, // 保留项,传0即可
_In_ REGSAM samDesired, // 访问权限
_Out_ PHKEY phkResult // 返回子键的句柄
);
HKEY hKey------
父键的句柄,可为RegCreateKeyEx或RegOpenKeyEx返回的注册表键句柄
或为预定义的根键HKEY_CLASSES_ROOT,HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE,HKEY_USERS或HKEY_CURRENT_CONFIG
REGSAM samDesired------
访问权限,想方便的话可以指定为KEY_ALL_ACCESS,这样什么权限都有了。其他常用的权限还有KEY_READ,KEY_WRITE等。
成功开启子键则返回ERROR_SUCCESS
LONG WINAPI RegCloseKey(
_In_ HKEY hKey
);
这两个函数需配对使用
// 打开注册表键------
HKEY hKey;
LPCTSTR lpszSubKey = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
int ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpszSubKey, 0, KEY_ALL_ACCESS, &hKey);
if (ret != ERROR_SUCCESS)
{
// 打开失败
......
}
......
// 关闭注册表键------
RegCloseKey(hKey);
2.获取注册表键的子键及键值的信息
每个注册表键下面都包含子键及键值项,RegQueryInfoKey函数用于获取子键的数量,键值项的数量等信息。
LONG WINAPI RegQueryInfoKey(
_In_ HKEY hKey, // 要获取信息的注册表键句柄
_Out_opt_ LPTSTR lpClass, // 一般传NULL
_Inout_opt_ LPDWORD lpcClass, // 一般传NULL
_Reserved_ LPDWORD lpReserved, // NULL
_Out_opt_ LPDWORD lpcSubKeys, // hKey下的子键数量
_Out_opt_ LPDWORD lpcMaxSubKeyLen, // hKey下的子键名称的最大长度(不包含结尾的null字符)
_Out_opt_ LPDWORD lpcMaxClassLen, // 一般传NULL
_Out_opt_ LPDWORD lpcValues, // hKey下的键值的数量
_Out_opt_ LPDWORD lpcMaxValueNameLen, // hKey下键值Name的最大长度(不包含结尾的null字符)
_Out_opt_ LPDWORD lpcMaxValueLen, // hKey下的键值Data的最大长度(in bytes)
_Out_opt_ LPDWORD lpcbSecurityDescriptor, // 安全描述符长度,一般传NULL
_Out_opt_ PFILETIME lpftLastWriteTime // 最后修改时间,一般传NULL
);
通过RegQueryInfoKey获取了子键及键值项的信息后,这才知道子键的数量,键值项的数量等信息,后面就可以通过RegEnumKeyEx枚举子键信息,通过RegEnumValue枚举键值项信息。
DWORD dwSubKeyCnt; // 子键的数量
DWORD dwSubKeyNameMaxLen; // 子键名称的最大长度(不包含结尾的null字符)
DWORD dwKeyValueCnt; // 键值项的数量
DWORD dwKeyValueNameMaxLen; // 键值项名称的最大长度(不包含结尾的null字符)
DWORD dwKeyValueDataMaxLen; // 键值项数据的最大长度(in bytes)
int ret = RegQueryInfoKey(
hKey,
NULL,
NULL,
NULL,
&dwSubKeyCnt,
&dwSubKeyNameMaxLen,
NULL,
&dwKeyValueCnt,
&dwKeyValueNameMaxLen,
&dwKeyValueDataMaxLen,
NULL,
NULL);
if (ret != ERROR_SUCCESS) // Error
{
......
}
3.枚举子键信息
LONG WINAPI RegEnumKeyEx(
_In_ HKEY hKey,
_In_ DWORD dwIndex, // 索引,从0开始
_Out_ LPTSTR lpName, // 接收子键的名称
_Inout_ LPDWORD lpcName, // lpName的大小(in characters,包含null)
_Reserved_ LPDWORD lpReserved, // NULL
_Inout_ LPTSTR lpClass, // 一般传NULL
_Inout_opt_ LPDWORD lpcClass, // 一般传NULL
_Out_opt_ PFILETIME lpftLastWriteTime // 一般传NULL
);
前面通过RegQueryInfoKey获取了hKey下子键的数量以及子键名称的最大长度,那么接下来通过RegEnumKeyEx就可以获取每个子键的名称了。为什么要获取子键的名称,因为获取了这个名称之后,就可以通过RegOpenKeyEx开启子键获得其句柄。
整个流程差不多是这样子的:RegQueryInfoKey-->获得某注册表键下的子键的数量及子键名称的最大长度-->RegEnumKeyEx-->枚举获取每个子键的名称-->RegOpenKeyEx-->开启子键句柄-->做你想要的操作。
lpName------
前面通过RegQueryInfoKey获取了子键名称的最大长度(没有包含结尾的null字符),所有可以通过定义一个[最大长度+1]大小的Buffer来接收
lpcName------
RegQueryInfoKey获取的子键名称的最大长度 + 1
//
// 以下代码中的变量dwSubKeyNameMaxLen&dwSubKeyCnt由
// RegQueryInfoKey获取
//
LPTSTR lpszSubKeyName = new TCHAR[dwSubKeyNameMaxLen+1];
for (int index = 0; index < dwSubKeyCnt; ++index)
{
memset(lpszSubKeyName, 0, sizeof(TCHAR)*(dwSubKeyNameMaxLen+1));
DWORD dwNameCnt = dwSubKeyNameMaxLen + 1;
int ret = RegEnumKeyEx(
hKey,
index,
lpszSubKeyName,
&dwNameCnt,
NULL,
NULL,
NULL,
NULL);
if (ret != ERROR_SUCCESS)
{
......
}
}
delete[] lpszSubKeyName;
4.枚举键值项信息
LONG WINAPI RegEnumValue(
_In_ HKEY hKey,
_In_ DWORD dwIndex, // 索引,从0开始
_Out_ LPTSTR lpValueName, // 接收键值项的名称
_Inout_ LPDWORD lpcchValueName,// lpValueName的大小,包含null character
_Reserved_ LPDWORD lpReserved, // NULL
_Out_opt_ LPDWORD lpType, // 接收键值项的类型
_Out_opt_ LPBYTE lpData, // 接收键值项的数据
_Inout_opt_ LPDWORD lpcbData // lpData的Buffer大小(in bytes)
);
前面通过RegQueryInfoKey获取了hKey下键值项的数量以及键值项名称的最大长度,键值项数据的最大长度,那么接下来通过RegEnumValue就可以获取每个键值项的名称、类型、数据。
//
// 以下代码中变量:dwKeyValueCnt & dwKeyValueNameMaxLen &
// dwKeyValueDataMaxLen
// 均为通过RegQueryInfoKey获取的
//
for (unsigned int index = 0; index < dwKeyValueCnt; ++index)
{
LPTSTR lpszKeyValueName = new TCHAR[dwKeyValueNameMaxLen + 1];
memset(lpszKeyValueName, 0, sizeof(TCHAR)*(dwKeyValueNameMaxLen + 1));
DWORD dwNameCnt = dwKeyValueNameMaxLen + 1;
DWORD dwKeyValueType;
LPBYTE lpbKeyValueData = new BYTE[dwKeyValueDataMaxLen];
DWORD dwKeyValueDataLen;
int ret = RegEnumValue(
hKey,
index,
lpszKeyValueName,
&dwNameCnt,
NULL,
&dwKeyValueType,
lpbKeyValueData,
&dwKeyValueDataLen);
if (ret != ERROR_SUCCESS)
{
......
}
delete[] lpszKeyValueName;
delete[] lpbKeyValueData;
}
5.由键值项名称获取键值项的类型及键值项数据
LONG WINAPI RegQueryValueEx(
_In_ HKEY hKey,
_In_opt_ LPCTSTR lpValueName, // 键值项名称
_Reserved_ LPDWORD lpReserved, // NULL
_Out_opt_ LPDWORD lpType, // 接收键值项类型
_Out_opt_ LPBYTE lpData, // 接收键值项数据
_Inout_opt_ LPDWORD lpcbData // lpData的Buffer大小(in bytes)
);
有时候,我们知道某注册表键下的键值项的名称,而想获取键值项的类型及键值项数据,就可以通过该函数获取。
比如:我们知道HKEY_CURRENT_USER\Environment注册表键下存在名为"Path"的键值项,通过该函数就可以获取其值。
通常情况下,虽然我们知道键值项的名称,却不知道键值项数据的大小,也即lpData该如何定义?可以分两步实现:一、调用RegQueryValueEx,但传lpData为NULL,函数执行成功后,lpcbData会返回键值项数据的大小。二、根据上一步获取的键值项数据的大小,new一个对应大小的Buffer,然后再调用RegQueryValueEx,传递lpData为新开辟的Buffer,这样就可以了。
// 获取键值项Data的大小
LPCTSTR lpszKeyValueName = TEXT("???");
DWORD dwKeyValueType;
DWORD dwKeyValueDataSize;
int ret = RegQueryValueEx(hKey, lpszKeyValueName, NULL, &dwKeyValueType, NULL, &dwKeyValueDataSize);
if (ret != ERROR_SUCCESS)
{
......
}
// 获取键值项Data
LPBYTE lpbKeyValueData = new BYTE[dwKeyValueDataSize];
ret = RegQueryValueEx(hKey, lpszKeyValueName, NULL, &dwKeyValueType, lpbKeyValueData, &dwKeyValueDataSize);
if (ret != ERROR_SUCCESS)
{
......
}
delete[] lpbKeyValueData;
6.设置键值项的值/新建键值项
LONG RegSetValueEx(
HKEY hKey,
LPCWSTR lpValueName,
DWORD Reserved,
DWORD dwType,
const BYTE* lpData,
DWORD cbData
);
该函数也可以新建键值项,当lpValueName指定名称的键值项不存在时,会新建键值项。
7.删除键值项
LONG RegDeleteValue(
HKEY hKey,
LPCWSTR lpValueName
);
8.创建、删除子键
LONG RegCreateKeyEx(
HKEY hKey,
LPCWSTR lpSubKey,
DWORD Reserved,
LPWSTR lpClass,
DWORD dwOptions,
REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
PHKEY phkResult,
LPDWORD lpdwDisposition
);
LONG WINAPI RegDeleteKey(
_In_ HKEY hKey,
_In_ LPCTSTR lpSubKey
);
示例程序一:读取注册表获取计算机上已安装程序的信息
Windows 系统中,安装程序都可以在注册表 HKEY_LOCAL_MACHINE\SoftWare\Microsoft\Windows\CurrentVersion\Uninstall 获取,并且xp、vista、win7、win8都一样
以下示例程序中:
结构体ApplicationInfoA用于记录每个安装程序的具体信息,至于为何在名称后面加A,主要是为了表明其下的信息全是用string记录的。
函数GetAllInstalledAppInfoA用于获取计算机上已安装程序的全部信息,它接受vector<ApplicationInfoA>引用类型的参数,并将获取的全部信息存放在该vector中。该程序执行成功返回0,执行失败则返回-1。
main()函数中演示了怎么使用:
vector<ApplicationInfoA> vAppInfo;
GetAllInstalledAppInfoA(vAppInfo);
在获取了安装程序的信息后,输出到D盘下的InstalledAppInfo.txt文件中。
#include <windows.h>
#include <iostream>
#include <TCHAR.H>
#include <vector>
using namespace std;
//
// 用于记录安装软件信息的结构体
//
struct ApplicationInfoA
{
string strName; // 软件名
string strDisplayName; // 显示的软件名
string strPublisher; // 发布者
string strVersion; // 版本
string strDisplayVersion; // 显示的版本
string strInstallLocation; // 安装的位置
};
//
// 获取具体的程序的键值Data
// hKey [in]
// --- 指向HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall的子键句柄
// lpszKeyValueName [in]
// --- hKey下面的子键名称
// lpszKeyValueName [in]
// --- 正如其名,键值的名称
// strKeyValue [out]
// --- 键值的Data
//
int _GetAppInfoA_(HKEY hKey, LPCSTR lpszAppName, LPCSTR lpszKeyValueName, string& strKeyValue)
{
int ret;
// 打开已安装软件的注册表键------------------------------------------------
HKEY hInstallAppKey;
ret = RegOpenKeyEx(hKey, lpszAppName, 0, KEY_ALL_ACCESS, &hInstallAppKey);
if (ret != ERROR_SUCCESS)
{
return -1;
}
// 获取已安装软件的注册表键的键值------------------------------------------
// 1.获取字符串大小(默认为字符串即REG_SZ)
DWORD dwKeyValueType = REG_SZ;
DWORD dwKeyValueDataSize = 0;
ret = RegQueryValueExA(
hInstallAppKey,
lpszKeyValueName,
NULL,
&dwKeyValueType,
NULL,
&dwKeyValueDataSize);
if (ret == ERROR_FILE_NOT_FOUND)
{
RegCloseKey(hInstallAppKey);
return 0;
}
else if (ret != ERROR_SUCCESS)
{
RegCloseKey(hInstallAppKey);
return -1;
}
// 2.获取字符串值
if (dwKeyValueType != REG_SZ) // 如果不是字符串类型则返回,有的安装程序此项不为字符串而为其他类型,忽略
{
RegCloseKey(hInstallAppKey);
return 0;
}
LPSTR lpszKeyValueData = new char[dwKeyValueDataSize + 1];
memset(lpszKeyValueData, 0, dwKeyValueDataSize + 1);
ret = RegQueryValueExA(
hInstallAppKey,
lpszKeyValueName,
NULL,
&dwKeyValueType,
(LPBYTE)lpszKeyValueData,
&dwKeyValueDataSize);
if (ret != ERROR_SUCCESS)
{
delete[] lpszKeyValueData;
RegCloseKey(hInstallAppKey);
return -1;
}
strKeyValue = lpszKeyValueData;
delete[] lpszKeyValueData;
// 关闭注册表键------------------------------------------------------------
RegCloseKey(hInstallAppKey);
return 0;
}
//
// 获取系统安装的程序信息并存储于参数vector中
// 成功执行返回0
// 执行失败则返回-1
//
int GetAllInstalledAppInfoA(vector<ApplicationInfoA>& vAppInfo)
{
int ret;
// 打开注册表键------------------------------------------------------------
HKEY hKey;
LPCSTR lpszSubKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpszSubKey, 0, KEY_ALL_ACCESS, &hKey);
if (ret != ERROR_SUCCESS)
{
return -1;
}
// 获取子键&键值信息-------------------------------------------------------
DWORD dwSubKeysCnt; // 子键数量
DWORD dwMaxSubKeyNameLen; // 子键名字的最大长度(not including the terminating null character)
DWORD dwKeyValueCnt; // 键值的数量
DWORD dwMaxKeyValueNameLen; // 键值名字的最大长度(not including the terminating null character)
DWORD dwMaxKeyValueDataLen; // 键值数据的最大长度(in Bytes)
ret = RegQueryInfoKey(
hKey,
NULL,
NULL,
NULL,
&dwSubKeysCnt,
&dwMaxSubKeyNameLen,
NULL,
&dwKeyValueCnt,
&dwMaxKeyValueNameLen,
&dwMaxKeyValueDataLen,
NULL,
NULL);
if (ret != ERROR_SUCCESS)
{
RegCloseKey(hKey);
return -1;
}
// 枚举子键信息------------------------------------------------------------
DWORD dwIndex;
LPSTR lpszSubKeyName = new char[dwMaxSubKeyNameLen + 1];
DWORD dwNameLen = dwMaxSubKeyNameLen + 1;
for (dwIndex = 0; dwIndex < dwSubKeysCnt; ++dwIndex)
{
dwNameLen = dwMaxSubKeyNameLen + 1;
memset(lpszSubKeyName, 0, dwMaxSubKeyNameLen + 1);
ret = RegEnumKeyEx(
hKey,
dwIndex,
lpszSubKeyName,
&dwNameLen,
NULL,
NULL,
NULL,
NULL);
if (ret != ERROR_SUCCESS)
{
RegCloseKey(hKey);
delete[] lpszSubKeyName;
return -1;
}
//************获取具体的程序的安装信息BEG*************
ApplicationInfoA appInfo;
appInfo.strName = lpszSubKeyName;
_GetAppInfoA_(hKey, lpszSubKeyName, "DisplayName", appInfo.strDisplayName);
_GetAppInfoA_(hKey, lpszSubKeyName, "Publisher", appInfo.strPublisher);
_GetAppInfoA_(hKey, lpszSubKeyName, "Version", appInfo.strVersion);
_GetAppInfoA_(hKey, lpszSubKeyName, "DisplayVersion", appInfo.strDisplayVersion);
_GetAppInfoA_(hKey, lpszSubKeyName, "InstallLocation", appInfo.strInstallLocation);
vAppInfo.push_back(appInfo);
//************获取具体的程序的安装信息END*************
}
delete[] lpszSubKeyName;
// 关闭注册表键------------------------------------------------------------
RegCloseKey(hKey);
return 0;
}
int main()
{
cout << "Reg Demo Test" << endl;
vector<ApplicationInfoA> vAppInfo;
cout << GetAllInstalledAppInfoA(vAppInfo) << endl;
//输出到文件
vector<ApplicationInfoA>::iterator iter = vAppInfo.begin();
FILE *fp = fopen("D:\\InstalledAppInfo.txt", "a");
while (iter != vAppInfo.end())
{
fprintf(fp, "----------------\n");
fprintf(fp, "Name: %s\n DisplayName: %s\n Publisher: %s\n Version: %s\n DisplayVersion: %s\n InstallLocation: %s\n",
iter->strName.c_str(), iter->strDisplayName.c_str(),
iter->strPublisher.c_str(), iter->strVersion.c_str(),
iter->strDisplayVersion.c_str(), iter->strInstallLocation.c_str());
fprintf(fp, "----------------\n\n");
++iter;
}
fclose(fp);
return 0;
}
示例程序二:增加Path环境变量
我们常常需要手工添加环境变量,如下图所示:
那么怎样用程序实现呢?环境变量的配置存储在注册表当中,可以通过读写注册表来实现读写环境变量。
系统变量存储在注册表的如下位置:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
而用户变量则存储在注册表的如下位置:HKEY_CURRENT_USER\Environment
下面示例程序用于向系统变量中的Path环境变量中增加内容。
main函数中调用AddPathEnvValue(";HelloKitty")就用于向Path环境变量后面添加;HelloKitty。当然程序中有防呆机制,如果系统变量下刚开始没有Path环境变量则先新增Path环境变量。程序执行成功返回0,执行失败返回-1。
代码如下:
#include <windows.h>
#include <iostream>
using namespace std;
//
// 为系统变量下的Path环境变量增加内容lpszPathValue
// 成功则返回0
// 失败则返回-1
// 若刚开始Path环境变量为"D:\\123"
// 则调用AddPathEnvValue(";HelloKitty")后为"D:\\123;HelloKitty"
//
int AddPathEnvValue(LPCSTR lpszPathValue)
{
int ret;
// 打开注册表键----------------------------------------
HKEY hKey;
LPCSTR lpszSubKey = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpszSubKey, 0, KEY_ALL_ACCESS, &hKey);
if (ERROR_SUCCESS != ret)
{
cout << "RegOpenKeyExA():Error" << endl;
return -1;
}
// 读取注册表键的键值"Path"----------------------------
// 1.获取KeyValueData即字符串的大小
LPCSTR lpszKeyValueName = "Path";
DWORD dwKeyValueType = REG_EXPAND_SZ;
DWORD dwKeyValueDataSize = 0;
ret = RegQueryValueExA(hKey, lpszKeyValueName, NULL, &dwKeyValueType, NULL, &dwKeyValueDataSize);
if (ret == ERROR_FILE_NOT_FOUND)
{
//不存在Path环境变量则新增一个Path环境变量
ret = RegSetValueExA(hKey, lpszKeyValueName, 0, REG_EXPAND_SZ, (const BYTE*)"", 1);
if (ret != ERROR_SUCCESS)
{
cout << "RegSetValueExA():Error" << endl;
RegCloseKey(hKey);
return -1;
}
}
else if (ret != ERROR_SUCCESS)
{
cout << "RegQueryValueExA():Error" << endl;
RegCloseKey(hKey);
return -1;
}
else if (dwKeyValueType != REG_EXPAND_SZ)
{
cout << "It is impossible" << endl;
cout << dwKeyValueType << endl;
RegCloseKey(hKey);
return -1;
}
// 2.获取KeyValueData即字符串的值
CHAR *lpszKeyValueData = new CHAR[dwKeyValueDataSize + 1];
memset(lpszKeyValueData, 0, dwKeyValueDataSize + 1);
ret = RegQueryValueExA(hKey, lpszKeyValueName, NULL, &dwKeyValueType, (LPBYTE)lpszKeyValueData, &dwKeyValueDataSize);
if (ret != ERROR_SUCCESS)
{
cout << "RegQueryValueExA():Error" << endl;
RegCloseKey(hKey);
delete[] lpszKeyValueData;
return -1;
}
// 在原注册表键值的基础上添加新的值
unsigned int nLen = strlen(lpszPathValue);
nLen += strlen(lpszKeyValueData);
CHAR *lpszKeyValueData_New = new CHAR[nLen + 1];
memset(lpszKeyValueData_New, 0, nLen + 1);
sprintf(lpszKeyValueData_New, "%s%s", lpszKeyValueData, lpszPathValue);
ret = RegSetValueExA(hKey, lpszKeyValueName, 0, REG_EXPAND_SZ, (const BYTE*)lpszKeyValueData_New, strlen(lpszKeyValueData_New) + 1);
if (ret != ERROR_SUCCESS)
{
cout << "RegSetValueExA:Error" << endl;
RegCloseKey(hKey);
delete[] lpszKeyValueData;
delete[] lpszKeyValueData_New;
return -1;
}
delete[] lpszKeyValueData;
delete[] lpszKeyValueData_New;
// 关闭注册表键----------------------------------------
RegCloseKey(hKey);
return 0;
}
int main()
{
cout << AddPathEnvValue(";HelloKitty") << endl;
return 0;
}