C++ 注冊表編程


 

C++ 注冊表編程

1.基礎知識

注冊表的組織方式跟文件目錄比較相似,主要分為根鍵、子鍵和鍵值項三部分,與文件目錄對應的話就是根目錄、子目錄和文件。分別介紹一下這三部分:

1)根鍵。分為5個,分別為

    HKEY_CLASSES_ROOT

    HKEY_CURRENT_USER

    HKEY_LOCAL_MACHINE

    HKEY_USERSHKEY_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(RegOpenKeyEx(HKEY_LOCAL_MACHINEdata0KEY_ALL_ACCESS&key)==ERROR_SUCCESS)

    ...{

        /**//*需要執行的操作...*/

    }

    ::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=0NameSizeNameCntNameMaxLenType;

DWORD KeySizeKeyCntKeyMaxLenDateSizeMaxDateLen;

 

if(RegQueryInfoKey(keyNULLNULLNULL&KeyCnt&KeyMaxLenNULL&NameCnt&NameMaxLen&MaxDateLenNULLNULL)!=ERROR_SUCCESS)

...{

    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(dwIndex=0;dwIndex<KeyCnt;dwIndex++)        //枚舉子鍵

...{

    KeySize=KeyMaxLen+1;            //因為RegQueryInfoKey得到的長度不包括0結束字符,所以應加1

    szKeyName=(char*)malloc(KeySize);

    /**//*參數定義請參照獲取子鍵/鍵值信息部分...*/

    RegEnumKeyEx(hKeydwIndexszKeyName&KeySizeNULLNULLNULLNULL);//枚舉子鍵

    printf(szKeyName);

}

 

最后需要注意的是在每次調用RegEnumKeyEx前必須重新將KeySize的值設為KeyMaxLen緩沖區的大小,因為每次函數返回時KeySize的值會變成返回的鍵值的名稱長度,隨着循環次數這個值會變小,而可能出現無法枚舉所有鍵值項的情況。

 

5.枚舉鍵值信息

 

枚舉鍵值信息的方法與枚舉子鍵信息極為相似,可以用RegEnumValue函數實現,其函數原型如下:

 

RegEnumValue(

        hkey,        //被枚舉的鍵句柄

        dwIndex,    //子鍵索引編號

        lpValueName,    //鍵值名稱

        lpcbValueName,    //鍵值名稱長度

        lpReserved,    //系統保留,指定為0

        lpType,        //鍵值數據類型

        lpDate,        //鍵值數據

        lpcbDate    //鍵值數據長度

        );

其實現代碼如下:

 

for(dwIndex=0;dwIndex<NameCnt;dwIndex++)    //枚舉鍵值

...{

    DateSize=MaxDateLen+1;

    NameSize=NameMaxLen+1;

    szValueName=(char *)malloc(NameSize);

    szValueDate=(LPBYTE)malloc(DateSize);

    /**//*參數定義請參照獲取子鍵/鍵值信息部分...*/

    RegEnumValue(hKeydwIndexszValueName&NameSizeNULL&TypeszValueDate&DateSize);//讀取鍵值

 

    if(Type==REG_SZ)

    ...{

        /**//*判斷鍵值項類型並做其它操作......*/

    }

    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(m_type=="REG_SZ")

    ...{

        if(RegOpenKeyEx(MKEYSubKey0KEY_ALL_ACCESS&key)==ERROR_SUCCESS)   

        ...{

            if(::RegSetValueEx(keym_name0REG_SZ(const unsigned char *)m_dateMAX_PATH)==ERROR_SUCCESS)

            ...{

                MessageBox("創建成功!");

            }

        }

    }

 

    if(m_type=="REG_DWORD")

    ...{

        if(RegOpenKeyEx(MKEYSubKey0KEY_ALL_ACCESS&key)==ERROR_SUCCESS)   

        ...{

            if(::RegSetValueEx(keym_name0REG_DWORD(const unsigned char *)m_date4)==ERROR_SUCCESS)//注意數據長度應該設為4

            ...{

                MessageBox("創建成功!");

            }

        }

    }

 

    /**//*其它類型的設置......*/

}

 

刪除鍵值可以用RegDeleteValue來實現,它的函數原型如下:

 

RegDeleteValue(

        hkey,        //父鍵句柄

        lpValueName    //要刪除的鍵值項名稱

        );

其實例代碼如下:

 

HKEY key;

char value[MAX_PATH]="LengFeng"            //鍵值

LPCTSTR data="SOFTWARE\Microsoft\Windows\CurrentVersion\Run";//路徑

 

RegOpenKeyEx(HKEY_LOCAL_MACHINEdata0KEY_WRITE&key);        //打開

 

if(ERROR_SUCCESS==::RegDeleteValue(keyvalue))            //刪除

...{

    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(NULLSE_BACKUP_NAME&tkp.Privileges[0].Luid);//申請SE_BACKUP_NAME權限

    tkp.PrivilegeCount=1;

    tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;           

    AdjustTokenPrivileges(hTokenFALSE&tkp0(PTOKEN_PRIVILEGES)NULL0);

    //開始備份工作

    szSaveFileName=LPTSTR("D\KeyDate");        //注意文件不可存在否則無法成功

    RegOpenKeyEx(

                            HKEY_CURRENT_USER

                            (LPCTSTR)strKey

                            0

                            KEY_ALL_ACCESS

                            &key);

    RegSaveKey(keyszSaveFileName, NULL);

    RegCloseKey(key);

}

 

上面用到了提升權限的代碼,以前雜志上面有很多可以參考一下來看,這樣備份就完成了。

恢復注冊表可以用函數RegRestoreKey來實現,它的原形如下:

RegRestoreKey(

        hkey,            //要恢復的鍵句柄

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

        dwFlage        //標志是否易失

        );  

此函數的前兩個參數可以與RegSaveKey相同,而參數dwFlageTRUE的話,是暫時恢復注冊表,如果為FALSE則是永久修改注冊表值。同樣需要注意的是使用這個函數需要有SE_RESTORE_NAME權限。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM