C++ 注册表编程

1.基础知识

注册表的组织方式跟文件目录比较相似,主要分为根键、子键和键值项三部分,与文件目录对应的话就是根目录、子目录和文件。分别介绍一下这三部分:

(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种。

有了这些基础下面我们讨论如何编程实现对注册表的操作。

2.打开/关闭注册表句柄

在对注册表操作前应该先打开指定的键,然后通过键的句柄进行操作,打开键句柄可以用API  RegOpenKeyEx来实现,其原形如下:

RegOpenKeyEx(

hKey,        //父键句柄

lpSubKey,    //子键句柄

dwOptions,    //系统保留,指定为0

samDesired,    //打开权限

phkResult,    //返回打开句柄

);

其中打开权限有多种, 想方便的话可以指定为KEY_ALL_ACCESS ,这样什么权限都有了,当函数执行成功时返回ERROR_SUCCESS。

  KEY_CREATE_LINK 许可创建一个符号连接

  KEY_CREATE_SUB_KEY 许可创建子键

  KEY_ENUMERATE_SUB_KEYS 许可列举子键

  KEY_EXECUTE 许可读访问

  KEY_NOTIFY 许可提供更改通知

  KEY_QUERY_VALUE 许可查询子键数据

  KEY_SET_VALUE 许可设置子键数据

  KEY_ALL_ACCESS 联合了 KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS,

  KEY_NOTIFY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK,

  KEY_SET_VALUE 访问权限并且加上所有的标准访问权限

  除了SYNCHRONIZE

  KEY_READ 联合了 STANDARD_RIGHTS_READ, KEY_QUERY_VALUE,

  KEY_ENUMERATE_SUB_KEYS,KEY_NOTIFY 访问权限

  KEY_WOW64_64KEY Windows XP: 使64位或者32位应用程序打开64位键

  KEY_WOW64_32KEY Windows XP: 使64位或者32位应用程序打开32位键

  KEY_WRITE 联合 STANDARD_RIGHTS_WRITE, KEY_SET_VALUE,

  KEY_CREATE_SUB_KEY访问权限

其实例代码如下:

HKEY key;LPCTSTR data = "SOFTWARE\Microsoft\Windows\CurrentVersion\Run";if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, data, 0, KEY_ALL_ACCESS,&key)){//需要执行的操作...}::RegCloseKey(key);

要注意的是,在使用后应该调用RegCloseKey()函数关闭句柄.

3.获取子键/键值信息

在现实的编程操作中我们常常需要获取子键/键值的信息比如:子键/键值的数量,长度,以及数据的最大长度等等,这些信息可以通过RegQueryInfoKey函数来获取。

它的原型如下:

RegQueryInfoKey(

hkey,            //要获取信息的句柄

lpClass,        //接受创建健时的Class字符串

lpcbClass,        //lpClass的长度

lpReserved,        //系统保留,指定为0

lpcSubKeys,        //子键数量

lpcbMaxSubKeyLen,    //子键中最长名称的长度

lpcbMaxClassLen,    //子键中最长Class字符串长度

lpcVlaues,        //键值数量

lpcbMaxValueNameLen,    //键值项中最长名称的长度

lpcbMaxValueLen,    //键值项数据最大长度

lpcbSecurityDescriptor,    //安全描述符长度

lpftLastWriteTime,    //FILETIME结构,最后修改时间

);

这个函数的参数很多,实际使用时,只填写自己需要的就行了,不需要的可以放个NULL就OK了,还有一点需要注意就是它所返回的长度都不包括结尾的0字符,所以在使用时应该用长度+1。

其实例代码如下:

DWORD dwIndex=0, NameSize, NameCnt, NameMaxLen, Type;
DWORD KeySize, KeyCnt,KeyMaxLen,DateSize,MaxDateLen;if(ERROR_SUCCESS != RegQueryInfoKey(key, NULL, NULL, NULL, &KeyCnt, &KeyMaxLen, NULL, &NameCnt, &NameMaxLen, &MaxDateLen, NULL, NULL))
{printf("RegQueryInfoKey错误");return;
}

用的时候套用格式就成了。有了这些信息我们就可以枚举子键和键值的信息了。

3.1获取一个项的设置值

RegQueryValueEx

  RegQueryValueEx

  VC声明

  LONG RegQueryValueEx(  

HKEY hKey,   //一个已打开项的句柄,或者指定一个标准项名   

LPCTSTR lpValueName, // 要获取值的名字

LPDWORD lpReserved, //  未用,设为零

     LPDWORD lpType, // 用于装载取回数据类型的一个变量

LPBYTE lpData,   // 用于装载指定值的一个缓冲区

      LPDWORD lpcbData //用于装载lpData缓冲区长度的一个变量。一旦返回,它会设为实际装载到缓冲区的字节数

 );

  返回值 

     Long,零(ERROR_SUCCESS)表示成功。其他任何值都代表一个错误代码

  lpValueName 指向要查询值的名字的字符串(以空字符结束)。 如果lpValueName是NULL或一个空字符串(""),这个函数找回这个键的未命名或默认值的类型和数据。 Windows 95和Windows 98: 每个键有一个默认值(最初的不包含数据)。在Windows 95,这人默认值类型总是REG_SZ。在Windows 98,默认键的类型最初是REG_SZ,但可以通过RegSetValueEx指定一个默认值为不同的类型。 Windows NT: 键不能自动拥有一个未命名或默认的值,未命名的值可以是任何类型。

  lpReserved 保留,必须是NULL.

4.枚举子键信息

枚举子键可以用API函数 RegEnumKeyEx来实现,调用RegEnumKeyEx时将返回子键的名称、长度和一些相关数据。如果想得到一个键下的全部子键的话应该循环调用,直到返回ERROR_NO_MORE_ITEMS为至,就说明已经枚举完了所有数据。

其函数原型如下:

RegEnumKeyEx(

hkey,        //被枚举的键句柄

dwIndex,    //子键索引编号

lpName,        //子键名称

lpcbName,    //子键名称长度

lpReserved,    //系统保留,指定为0

lpClass,    //子键类名

lpcbClass,    //子键类名长度

lpftLastWriteTime//最后写入时间

);

因为在之前我们已经通过RegQueryInfoKey函数获取了键的有关数据,所以在这里不再跟据ERROR_NO_MORE_ITEMS来实现了。

其实现代码如下:

for(DWORD dwIndex=0; dwIndex<KeyCnt; dwIndex++)        //枚举子键
{KeySize = KeyMaxLen+1;            //因为RegQueryInfoKey得到的长度不包括0结束字符,所以应加1szKeyName = (char*)malloc(KeySize);//参数定义请参照获取子键/键值信息部分...RegEnumKeyEx(hKey, dwIndex, szKeyName, &KeySize, NULL, NULL, NULL, NULL);//枚举子键printf(szKeyName);
}

最后需要注意的是在每次调用RegEnumKeyEx前必须重新将KeySize的值设为KeyMaxLen缓冲区的大小,因为每次函数返回时KeySize的值会变成返回的键值的名称长度,随着循环次数这个值会变小,而可能出现无法枚举所有键值项的情况。

5.枚举键值信息

枚举键值信息的方法与枚举子键信息极为相似,可以用RegEnumValue函数实现,其函数原型如下:

RegEnumValue(

hkey,        //被枚举的键句柄

dwIndex,    //子键索引编号

lpValueName,    //键值名称

lpcbValueName,    //键值名称长度

lpReserved,    //系统保留,指定为0

lpType,        //键值数据类型

lpDate,        //键值数据

lpcbDate    //键值数据长度

);

其实现代码如下:

for(DWORD dwIndex=0; dwIndex<NameCnt; dwIndex++)    //枚举键值
{DateSize = MaxDateLen+1;NameSize = NameMaxLen+1;szValueName = (char *)malloc(NameSize);szValueDate = (LPBYTE)malloc(DateSize);//参数定义请参照获取子键/键值信息部分...RegEnumValue(hKey, dwIndex, szValueName, &NameSize, NULL, &Type, szValueDate, &DateSize);//读取键值if(REG_SZ == Type){//判断键值项类型并做其它操作......}if(Type==REG_DWORD){}
}

与枚举子键相似,在每次循环中应该重新设置 数据长度DateSize = MaxDateLen+1,键值名称长度NameSize = NameMaxLen+1。

6.创建/删除子键

创建子键跟打开子键差不多,可以用RegCreateKeyEx函数来实现,

其原型如下:

RegCreateKeyEx(

hkey,            //父键句柄

lpSubKey,        //子键句柄

Reserved,        //系统保留,指定为0

lpClass,        //定义子键类名,通常设为NULL

dwOptions,        //创建子键时的选项

samDesired,        //创建后操作权限

lpSecurityAttributes,    //指向SECURITY_ATTRIBUTES结构,指定键句柄的继//承性

phkResult,        //返回创建句柄

lpdwDisposition        //通常设为NULL

);

创建子键也可以用16位下的API函数RegCreateKey来实现。

其实例代码如下:

HKEY KEY;if (ERROR_SUCCESS != RegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\MyKey", &KEY))
{MessageBox("创建失败!");
}
else
{MessageBox("创建成功!");
}

删除一个键可以用RegDeleteKey()实现,它有两个参数原型如下:

RegDeleteKey(

hkey,        //主键句柄

lpSubKey,    //子键名称字符串

);

如果想删除上面创建的MyKey子键可以用下面的代码实现:

if(ERROR_SUCCESS == RegDeleteKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\MyKey"))
{AfxMessageBox("删除成功!");
}
else
{AfxMessageBox("删除失败!");
}

需要注意的是, 在创建子键时可以创建多级子键,比如:

RegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\MyKey1\MyKey2", &KEY);

如果MyKey1不存在的话,那么它将先创建MyKey1,再创建MyKey2,这一点与文件系统中创建目录是不同的。但是删除的时候却不能删除多级子键。比如想删除MyKey1,那么必须先删除MyKey2才可以。不过一个子键下面的多个键值可以一次删除。

7.创建/删除键值项

创建键值可以用RegSetValueEx函数来实现,它的原型如下:

RegSetValueEx(

hkey,        //键句柄,键值项将保存在此键下

lpValueName,    //键值项名称

Reserved,    //系统保留,指定为0

dwType,        //键值项类型

lpDate,        //键值项数据

cbDate        //键值项长度

);

使用这个函数的时个有一点需要注意,其中参数lpDate和cbDate的值要跟据dwType的值来设定,按常用设置我们分三种情况

(1)当dwType为REG_SZ时,这时跟通常一样,lpDate为要设置的数据, cbDate为数据的长度。

(2)当dwType为REG_DWORD 时,cbDate必须设为4。

(3)当dwType为REG_BINARY 时,cbDate也必须设为4。

如果调用时,键值项名称已经存在,则会覆盖原有键值项。如果没有就新建一个。

实现功能的实例代码如下:

void CreateValue::OnCreate()
{HKEY key;UpdateData(true);if("REG_SZ" == m_type){if(ERROR_SUCCESS == RegOpenKeyEx(MKEY, SubKey, 0, KEY_ALL_ACCESS, &key))   {if(ERROR_SUCCESS == ::RegSetValueEx(key, m_name, 0, REG_SZ, (const unsigned char *)m_date, MAX_PATH)){MessageBox("创建成功!");}}}if("REG_DWORD" == m_type){if(ERROR_SUCCESS == RegOpenKeyEx(MKEY, SubKey, 0, KEY_ALL_ACCESS, &key)){if(ERROR_SUCCESS == ::RegSetValueEx(key, m_name, 0, REG_DWORD, (const unsigned char *)m_date, 4))//注意数据长度应该设为4{MessageBox("创建成功!");}}}//其它类型的设置......
}

删除键值可以用RegDeleteValue来实现,它的函数原型如下:

RegDeleteValue(

hkey,        //父键句柄

lpValueName    //要删除的键值项名称

);

其实例代码如下:

HKEY key;
char value[MAX_PATH] = "HuangYifan"            //键值
LPCTSTR data = "SOFTWARE\Microsoft\Windows\CurrentVersion\Run";//路径
RegOpenKeyEx(HKEY_LOCAL_MACHINE, data, 0, KEY_WRITE,&key);        //打开if(ERROR_SUCCESS == ::RegDeleteValue(key, value))            //删除
{MessageBox("删除成功!");
}

8.备份/恢复注册表

备份和恢复注册表相对来说用的不是太多,仅用一个运行在CONSOLE32下的小程序来讨论一下它们的实现。

备份注册表可以用RegSaveKey函来实现, 它的原形如下:

RegSaveKey(

hkey,            //要备份的键句柄

lpFile,        //保存信息的文件名称

lpSecurityAttributes    //文件安全属性

);

hkey为要备份的键句柄,可以是系统预定义的,也可以是用RegOpenKey()打开或是RegCreateKeyEx()创建的。

lpFile为保存信息的文件名称,注意这个文件必须是不存在的,而且也不能有扩展名(否则RegRestoreKey()函无法读取)。

lpSecurityAttributes:在NT系统中用来设置新文件的安全属性,通常设置为NULL。

在使用这个函数时需要有SE_BACKUP_NAME权限,而这个权限是不可以在RegOpenKey()或是RegCreateKeyEx()中指定的。

要做到这一点就必须提升程序的权限,备份代码如下:

void CDo_RegDlg::backup()
{// TODO: 在此添加控件通知处理程序代码char strKey[] = "SOFTWARE\\11111";   //这样写,备份的是11111项所包含的一些子项及值 测试时记住自己建立一个子项作为测试项,不然注册表会被搞乱LPTSTR szSaveFileName;HKEY key;// 申请备份权限HANDLE hToken;TOKEN_PRIVILEGES tkp;if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))return;LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tkp.Privileges[0].Luid);//申请SE_BACKUP_NAME权限 也就是备份注册表权限tkp.PrivilegeCount = 1;tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);//开始备份工作szSaveFileName = LPTSTR("H:\\KeyData");        //注意文件不可存在否则无法成功,备份文件无需指定文件类型RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)strKey, 0, KEY_ALL_ACCESS, &key);RegSaveKey(key, szSaveFileName, NULL);RegCloseKey(key);}

恢复注册表可以用函数RegRestoreKey来实现,它的原形如下:

RegRestoreKey(

hkey,            //要恢复的键句柄

lpFile,        //保存信息的文件名称

dwFlage        //标志是否易失

);

恢复注册表代码如下:

void CDo_RegDlg::onRecover()
{// TODO: 在此添加控件通知处理程序代码int valueone;int valuetwo;char strKey[] = "SOFTWARE\\11111";      //这样写,恢复的是11111项所包含的一些子项及值LPTSTR szSaveFileName;HKEY key;// 申请恢复权限HANDLE hToken;TOKEN_PRIVILEGES tkp;if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))return;LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &tkp.Privileges[0].Luid);//申请SE_RESTORE_NAME权限 也就是恢复注册表权限权限tkp.PrivilegeCount = 1;tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);//开始恢复工作szSaveFileName = LPTSTR("H:\\KeyData");   valuetwo=RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)strKey, 0, KEY_ALL_ACCESS, &key);if (valuetwo== ERROR_SUCCESS){MessageBox("成功");}else {char two[10];sprintf(two, "%d", valuetwo);MessageBox(two);}valueone=RegRestoreKey(key, szSaveFileName,REG_FORCE_RESTORE);//REG_FORCE_RESTORE 要指定这个属性(常规恢复,就是永久恢复)  REG_WHOLE_HIVE_VOLATILE这个属性恢复时测试无效if (valueone== ERROR_SUCCESS){MessageBox("恢复成功");}else{char one[10];sprintf(one, "%d", valueone);MessageBox(one);}RegCloseKey(key);
}

C++ 注册表编程(包含权限的提升)相关推荐

  1. 注册表编程,程序记忆功能

    BCG注册表清除: 第一次在CXXXApp::ExitInstance() 加入CleanState(), 运行一次 接着注释掉,再在OnInitInstance加入m_bSaveState=FALS ...

  2. 修改注册表设置管理员权限

    创建一个记事本将后缀修改成 ".reg" 在记事本中写入 Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Softw ...

  3. 猿创征文|【C#编程指南】 文件系统和注册表编程指南与实战

  4. 计算机更改用户名修改注册表出问题,改注册表没权限?无论什么账户这么做就好...

    在昨天的<除旧迎新过小年 1分钟速清电脑>留言中,有好多小伙伴说无法运行,权限不够.如果是运行BAT文件的话,用鼠标右键点击选择"以管理员身份运行"就行了. 但如果是在 ...

  5. Win7系统没有注册表操作权限的解决方法

    最近有部分用户反馈自己没有注册表权限,无法对注册表进行修改怎么办?其实很简单,下面小编就给大家带来Win7系统没有注册表操作权限的解决方法. 详细如下: 1.点击开始-运行-输入"gpedi ...

  6. 注册表包含了计算机哪些信息,注册表大概主要有哪些内容,分别有什么用?MSDN能找到其内容吗?...

    Windows 注册表 Windows 将其配置信息存储在一个称为注册表的数据库中.(随 Windows 一起提供的注册表编辑器是 regedit.exe.)注册表包含计算机中每个用户的配置文件.有关 ...

  7. 通过注册表修改IE的Internet选项

    Internet Explorer 安全区域设置存储在以下注册表子项下面: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\I ...

  8. 修改注册表来修改IE的设置---资料汇总

    原文链接: http://blog.csdn.net/wangqiulin123456/article/details/17068649 附带批处理执行脚本: @echo off &title ...

  9. 注册表的解释和一些简单的修改

    注册表的解释和一些简单的修改 一. HKEY_CLASSES_ROOT根键 此根键中主要记录着Windows 95/98中所有的文件类型,包括安装操作系统时约定注册的和由于以后安装软件而新加载的各种文 ...

  10. 10#Windows注册表的那些事儿

    引言 用了多年的Windows系统,其实并没有对Windows系统进行过深入的了解,也正是由于Windows系统不用深入了解就可以简单上手所以才有这么多人去使用.笔者是做软件开发的,使用的基本都是Wi ...

最新文章

  1. ACMNO.42 C语言-第几天 定义一个结构体变量(包括年、月、日)。计算该日在本年中是第几天,注意闰年问题。利用结构体的在最下面
  2. 【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
  3. jdk1.7 String switch的实现
  4. freemark循环map_freemarker中循环map根据key值得value 报错
  5. java 递归 求一个数的阶乘
  6. docker 在window 10 专业版的安装 .net core 在docker的部署
  7. 矩池云上缺少curand.h、cublas_v2.h、cusolverDn.h头文件解决方法
  8. 【7.17总结】 匈牙利算法(二分图最大匹配)
  9. iOS开发-使用Storyboard进行界面跳转及传值
  10. 超实用Mac苹果电脑终端命令
  11. docker阿里云加速器
  12. 移动平台课程设计--日记本
  13. 惠普计算机图标不在桌面,我的电脑图标没了怎么办
  14. Unity Hub和Unity安装教程
  15. 什么表示计算机的存储容量,存储容量
  16. Java开发16个经典面试问题
  17. selinux基本概念 | 开启selinux策略 | 安全上下文的临时修改 | 安全上下文的永久修改 | 如何修复selinux | selinux对服务功能的影响 | 系统自动排错
  18. 笔记本界面怎么显示服务器界面,电脑桌面显示工作方案(共8篇) .docx
  19. php一句话木马调用cmd命令,一句话木马(webshell)是如何执行命令的
  20. ChinaSoft 论坛巡礼 | 可信AI软件系统工程技术

热门文章

  1. Vue3中导入项目Eslint和TS语法检测问题解决方案
  2. java 解析xml saxreader_Java中使用DOM和SAX解析XML文件的方法示例
  3. python做出代码结构图_数据结构之图的代码实现(使用Python实现)
  4. 宝塔apache配置ssl_BT面板安装ssl数字证书引起网站打不开另类解决方案
  5. 动态规划实战6 leetcode-139. Word Break
  6. php环境下cache失效,cache缓存失效高并发读数据库的问题
  7. yii 获取当前域名_yii2 在域名后面加一个路径作为首页
  8. 【问题10】使用Redis SETNX 命令实现分布式锁
  9. 分享超级表格用户在知乎上与我们的对话
  10. Centos 搭建DNS服务器