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
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 //鍵值項長度
);
使用這個函數的時個有一點需要注意,其中參數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()中指定的。
要做到這一點就必須提升程序的權限,其具體實現如下代碼如示:
#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相同,而參數dwFlage為TRUE的話,是暫時恢復注冊表,如果為FALSE則是永久修改注冊表值。同樣需要注意的是使用這個函數需要有SE_RESTORE_NAME權限。
