C++ 注册表编程

C++ 注册表编程编程

1.基础知识

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

1)根键。分为5个,分别为windows

    HKEY_CLASSES_ROOT安全

    HKEY_CURRENT_USER函数

    HKEY_LOCAL_MACHINEui

    HKEY_USERSHKEY_CURRENT_CONFIGspa

    把它们理解成磁盘的五个分区能够了。code

2)子键。能够有多个子键和键值项,就像一个目录中能够有多个子目录和多个文件同样。htm

3)键值项。能够理解为文件,它由三部分组成,分别为 :名称、类型、数据。blog

 类型又分为多种主要包括以下:

    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结构,最后修改时间

        );

这个函数的参数不少,实际使用时,只填写本身须要的就好了,不须要的能够放个NULLOK了,还有一点须要注意就是它所返回的长度都不包括结尾的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结束字符,因此应加1
    szKeyName = (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        //键值项长度

        );

使用这个函数的时个有一点须要注意,其中参数lpDatecbDate的值要跟据dwType的值来设定,按经常使用设置咱们分三种状况

 1)当dwTypeREG_SZ时,这时跟一般同样,lpDate为要设置的数据, cbDate为数据的长度。

2)当dwTypeREG_DWORD 时,cbDate必须设为4

3)当dwTypeREG_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()中指定的。

要作到这一点就必须提高程序的权限,其具体实现以下代码如示:

 

#include <windows.h>             
#include <stdio.h>
#include <stdlib.h>

void main()
{
    char strKey[] = "Software\Microsoft\Internet Explorer";
    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("D:\KeyDate");        //注意文件不可存在不然没法成功
    RegOpenKeyEx(HKEY_CURRENT_USER, (LPCTSTR)strKey, 0, KEY_ALL_ACCESS, &key);
    RegSaveKey(key, szSaveFileName, NULL);
    RegCloseKey(key);
}

 

上面用到了提高权限的代码,之前杂志上面有不少能够参考一下来看,这样备份就完成了。

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

RegRestoreKey(

        hkey,            //要恢复的键句柄

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

        dwFlage        //标志是否易失

        );  

此函数的前两个参数能够与RegSaveKey相同,而参数dwFlageTRUE的话,是暂时恢复注册表,若是为FALSE则是永久修改注册表值。一样须要注意的是使用这个函数须要有SE_RESTORE_NAME权限。

相关文章
相关标签/搜索