参考及摘抄自文章: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; }