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權限。

相關文章
相關標籤/搜索