1.基礎(chǔ)知識 注冊表的組織方式跟文件目錄比較相似,主要分為根鍵、子鍵和鍵值項三部分,與文件目錄對應(yīng)的話就是根目錄、子目錄和文件。分別介紹一下這三部分: (1)根鍵。分為5個,分別為HKEY_CLASSES_ROOT, HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE,HKEY_USERS和HKEY_CURRENT_CONFIG,把它們理解成磁盤的五個分區(qū)可以了。 (2)子鍵??梢杂卸鄠€子鍵和鍵值項,就像一個目錄中可以有多個子目錄和多個文件一樣。 (3)鍵值項??梢岳斫鉃槲募?,它由三部分組成,分別為 :名稱、類型、數(shù)據(jù)。 類型又分為多種主要包括如下: REG_BINARY 二進(jìn)制數(shù)據(jù) REG_DWORD 32位雙字節(jié)數(shù)據(jù) REG_SZ 以0結(jié)尾的字符串 REG_DWORD_BIG_ENDIAN 高位排在底位的雙字 REG_EXPAND_SZ 擴(kuò)展字符串,可以加入變量如%PATH% REG_LINK UNICODE 符號鏈接 REG_RESOURCE_LIST 設(shè)備驅(qū)動程序資源列表 REG_MULTI_SZ 多字符串 注冊表數(shù)據(jù)項的數(shù)據(jù)類型有8種,但最常用的主要是前3種。 有了這些基礎(chǔ)下面我們討論如何編程實現(xiàn)對注冊表的操作。 2.打開/關(guān)閉注冊表句柄 在對注冊表操作前應(yīng)該先打開指定的鍵,然后通過鍵的句柄進(jìn)行操作,打開鍵句柄可以用API RegOpenKeyEx來實現(xiàn),其原形如下: RegOpenKeyEx( hKey, //父鍵句柄 lpSubKey, //子鍵句柄 dwOptions, //系統(tǒng)保留,指定為0 samDesired, //打開權(quán)限 phkResult, //返回打開句柄 ); 其中打開權(quán)限有多種, 想方便的話可以指定為KEY_ALL_ACCESS ,這樣什么權(quán)限都有了,當(dāng)函數(shù)執(zhí)行成功時返回ERROR_SUCCESS。 其實例代碼如下: HKEY key; LPCTSTR data="SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,data,0,KEY_ALL_ACCESS,&key)==ERROR_SUCCESS) ...{ /**//*需要執(zhí)行的操作...*/ } ::RegCloseKey(key); 要注意的是,在使用后應(yīng)該調(diào)用RegCloseKey()函數(shù)關(guān)閉句柄. 3.獲取子鍵/鍵值信息 在現(xiàn)實的編程操作中我們常常需要獲取子鍵/鍵值的信息比如:子鍵/鍵值的數(shù)量,長度,以及數(shù)據(jù)的最大長度等等,這些信息可以通過RegQueryInfoKey函數(shù)來獲取。 它的原型如下: RegQueryInfoKey( hkey, //要獲取信息的句柄 lpClass, //接受創(chuàng)建健時的Class字符串 lpcbClass, //lpClass的長度 lpReserved, //系統(tǒng)保留,指定為0 lpcSubKeys, //子鍵數(shù)量 lpcbMaxSubKeyLen, //子鍵中最長名稱的長度 lpcbMaxClassLen, //子鍵中最長Class字符串長度 lpcVlaues, //鍵值數(shù)量 lpcbMaxValueNameLen, //鍵值項中最長名稱的長度 lpcbMaxValueLen, //鍵值項數(shù)據(jù)最大長度 lpcbSecurityDescriptor, //安全描述符長度 lpftLastWriteTime, //FILETIME結(jié)構(gòu),最后修改時間 ); 這個函數(shù)的參數(shù)很多,實際使用時,只填寫自己需要的就行了,不需要的可以放個NULL就OK了,還有一點需要注意就是它所返回的長度都不包括結(jié)尾的0字符,所以在使用時應(yīng)該用長度+1。 其實例代碼如下: DWORD dwIndex=0,NameSize,NameCnt,NameMaxLen,Type; DWORD KeySize,KeyCnt,KeyMaxLen,DateSize,MaxDateLen; if(RegQueryInfoKey(key,NULL,NULL,NULL,&KeyCnt,&KeyMaxLen,NULL,&NameCnt,&NameMaxLen,&MaxDateLen,NULL,NULL)!=ERROR_SUCCESS) ...{ printf("RegQueryInfoKey錯誤"); return; } 用的時候套用格式就成了。有了這些信息我們就可以枚舉子鍵和鍵值的信息了。 4.枚舉子鍵信息 枚舉子鍵可以用API函數(shù) RegEnumKeyEx來實現(xiàn),調(diào)用RegEnumKeyEx時將返回子鍵的名稱、長度和一些相關(guān)數(shù)據(jù)。如果想得到一個鍵下的全部子鍵的話應(yīng)該循環(huán)調(diào)用,直到返回ERROR_NO_MORE_ITEMS為至,就說明已經(jīng)枚舉完了所有數(shù)據(jù)。 其函數(shù)原型如下: RegEnumKeyEx( hkey, //被枚舉的鍵句柄 dwIndex, //子鍵索引編號 lpName, //子鍵名稱 lpcbName, //子鍵名稱長度 lpReserved, //系統(tǒng)保留,指定為0 lpClass, //子鍵類名 lpcbClass, //子鍵類名長度 lpftLastWriteTime//最后寫入時間 ); 因為在之前我們已經(jīng)通過RegQueryInfoKey函數(shù)獲取了鍵的有關(guān)數(shù)據(jù),所以在這里不再跟據(jù)ERROR_NO_MORE_ITEMS來實現(xiàn)了。 其實現(xiàn)代碼如下: for(dwIndex=0;dwIndex<KeyCnt;dwIndex++) //枚舉子鍵 ...{ KeySize=KeyMaxLen+1; //因為RegQueryInfoKey得到的長度不包括0結(jié)束字符,所以應(yīng)加1 szKeyName=(char*)malloc(KeySize); /**//*參數(shù)定義請參照獲取子鍵/鍵值信息部分...*/ RegEnumKeyEx(hKey,dwIndex,szKeyName,&KeySize,NULL,NULL,NULL,NULL);//枚舉子鍵 printf(szKeyName); } 最后需要注意的是在每次調(diào)用RegEnumKeyEx前必須重新將KeySize的值設(shè)為KeyMaxLen緩沖區(qū)的大小,因為每次函數(shù)返回時KeySize的值會變成返回的鍵值的名稱長度,隨著循環(huán)次數(shù)這個值會變小,而可能出現(xiàn)無法枚舉所有鍵值項的情況。 5.枚舉鍵值信息 枚舉鍵值信息的方法與枚舉子鍵信息極為相似,可以用RegEnumValue函數(shù)實現(xiàn),其函數(shù)原型如下: RegEnumValue( hkey, //被枚舉的鍵句柄 dwIndex, //子鍵索引編號 lpValueName, //鍵值名稱 lpcbValueName, //鍵值名稱長度 lpReserved, //系統(tǒng)保留,指定為0 lpType, //鍵值數(shù)據(jù)類型 lpDate, //鍵值數(shù)據(jù) lpcbDate //鍵值數(shù)據(jù)長度 ); 其實現(xiàn)代碼如下: for(dwIndex=0;dwIndex<NameCnt;dwIndex++) //枚舉鍵值 ...{ DateSize=MaxDateLen+1; NameSize=NameMaxLen+1; szValueName=(char *)malloc(NameSize); szValueDate=(LPBYTE)malloc(DateSize); /**//*參數(shù)定義請參照獲取子鍵/鍵值信息部分...*/ RegEnumValue(hKey,dwIndex,szValueName,&NameSize,NULL,&Type,szValueDate,&DateSize);//讀取鍵值 if(Type==REG_SZ) ...{ /**//*判斷鍵值項類型并做其它操作......*/ } if(Type==REG_DWORD) ...{ } } 與枚舉子鍵相似,在每次循環(huán)中應(yīng)該重新設(shè)置 數(shù)據(jù)長度DateSize=MaxDateLen+1,鍵值名稱長度NameSize=NameMaxLen+1。 6.創(chuàng)建/刪除子鍵 創(chuàng)建子鍵跟打開子鍵差不多,可以用RegCreateKeyEx函數(shù)來實現(xiàn), 其原型如下: RegCreateKeyEx( hkey, //父鍵句柄 lpSubKey, //子鍵句柄 Reserved, //系統(tǒng)保留,指定為0 lpClass, //定義子鍵類名,通常設(shè)為NULL dwOptions, //創(chuàng)建子鍵時的選項 samDesired, //創(chuàng)建后操作權(quán)限 lpSecurityAttributes, //指向SECURITY_ATTRIBUTES結(jié)構(gòu),指定鍵句柄的繼//承性 phkResult, //返回創(chuàng)建句柄 lpdwDisposition //通常設(shè)為NULL ); 創(chuàng)建子鍵也可以用16位下的API函數(shù)RegCreateKey來實現(xiàn)。 其實例代碼如下: HKEY KEY; if (ERROR_SUCCESS!=RegCreateKey(HKEY_LOCAL_MACHINE,"SOFTWARE\Microsoft\Windows\MyKey",&KEY)) ...{ MessageBox("創(chuàng)建失敗!"); }else ...{ MessageBox("創(chuàng)建成功!"); }; 刪除一個鍵可以用RegDeleteKey()實現(xiàn),它有兩個參數(shù)原型如下: RegDeleteKey( hkey, //主鍵句柄 lpSubKey, //子鍵名稱字符串 ); 如果想刪除上面創(chuàng)建的MyKey子鍵可以用下面的代碼實現(xiàn): if(ERROR_SUCCESS==RegDeleteKey(HKEY_LOCAL_MACHINE,"SOFTWARE\Microsoft\Windows\MyKey")) ...{ AfxMessageBox("刪除成功!"); }else ...{ AfxMessageBox("刪除失敗!"); } 需要注意的是, 在創(chuàng)建子鍵時可以創(chuàng)建多級子鍵,比如: RegCreateKey(HKEY_LOCAL_MACHINE,"SOFTWARE\Microsoft\Windows\MyKey1\MyKey2",&KEY); 如果MyKey1不存在的話,那么它將先創(chuàng)建MyKey1,再創(chuàng)建MyKey2,這一點與文件系統(tǒng)中創(chuàng)建目錄是不同的。但是刪除的時候卻不能刪除多級子鍵。比如想刪除MyKey1,那么必須先刪除MyKey2才可以。不過一個子鍵下面的多個鍵值可以一次刪除。 7.創(chuàng)建/刪除鍵值項 創(chuàng)建鍵值可以用RegSetValueEx函數(shù)來實現(xiàn),它的原型如下: RegSetValueEx( hkey, //鍵句柄,鍵值項將保存在此鍵下 lpValueName, //鍵值項名稱 Reserved, //系統(tǒng)保留,指定為0 dwType, //鍵值項類型 lpDate, //鍵值項數(shù)據(jù) cbDate //鍵值項長度 ); 使用這個函數(shù)的時個有一點需要注意,其中參數(shù)lpDate和cbDate的值要跟據(jù)dwType的值來設(shè)定,按常用設(shè)置我們分三種情況 (1)當(dāng)dwType為REG_SZ時,這時跟通常一樣,lpDate為要設(shè)置的數(shù)據(jù), cbDate為數(shù)據(jù)的長度。 (2)當(dāng)dwType為REG_DWORD 時,cbDate必須設(shè)為4。 (3)當(dāng)dwType為REG_BINARY 時,cbDate也必須設(shè)為4。 如果調(diào)用時,鍵值項名稱已經(jīng)存在,則會覆蓋原有鍵值項。如果沒有就新建一個。 實現(xiàn)功能的實例代碼如下: void CreateValue::OnCreate() ...{ HKEY key; UpdateData(true); if(m_type=="REG_SZ") ...{ if(RegOpenKeyEx(MKEY,SubKey,0,KEY_ALL_ACCESS,&key)==ERROR_SUCCESS) ...{ if(::RegSetValueEx(key,m_name,0,REG_SZ,(const unsigned char *)m_date,MAX_PATH)==ERROR_SUCCESS) ...{ MessageBox("創(chuàng)建成功!"); } } } if(m_type=="REG_DWORD") ...{ if(RegOpenKeyEx(MKEY,SubKey,0,KEY_ALL_ACCESS,&key)==ERROR_SUCCESS) ...{ if(::RegSetValueEx(key,m_name,0,REG_DWORD,(const unsigned char *)m_date,4)==ERROR_SUCCESS)//注意數(shù)據(jù)長度應(yīng)該設(shè)為4 ...{ MessageBox("創(chuàng)建成功!"); } } } /**//*其它類型的設(shè)置......*/ } 刪除鍵值可以用RegDeleteValue來實現(xiàn),它的函數(shù)原型如下: RegDeleteValue( hkey, //父鍵句柄 lpValueName //要刪除的鍵值項名稱 ); 其實例代碼如下: HKEY key; char value[MAX_PATH]="LengFeng" //鍵值 LPCTSTR data="SOFTWARE\Microsoft\Windows\CurrentVersion\Run";//路徑 RegOpenKeyEx(HKEY_LOCAL_MACHINE,data,0,KEY_WRITE,&key); //打開 if(ERROR_SUCCESS==::RegDeleteValue(key,value)) //刪除 ...{ MessageBox("刪除成功!"); } 8.備份/恢復(fù)注冊表 備份和恢復(fù)注冊表相對來說用的不是太多,僅用一個運行在CONSOLE32下的小程序來討論一下它們的實現(xiàn)。 備份注冊表可以用RegSaveKey函來實現(xiàn), 它的原形如下: RegSaveKey( hkey, //要備份的鍵句柄 lpFile, //保存信息的文件名稱 lpSecurityAttributes //文件安全屬性 ); hkey為要備份的鍵句柄,可以是系統(tǒng)預(yù)定義的,也可以是用RegOpenKey()打開或是RegCreateKeyEx()創(chuàng)建的。 lpFile為保存信息的文件名稱,注意這個文件必須是不存在的,而且也不能有擴(kuò)展名(否則RegRestoreKey()函無法讀?。?。 lpSecurityAttributes:在NT系統(tǒng)中用來設(shè)置新文件的安全屬性,通常設(shè)置為NULL。 在使用這個函數(shù)時需要有SE_BACKUP_NAME權(quán)限,而這個權(quán)限是不可以在RegOpenKey()或是RegCreateKeyEx()中指定的。 要做到這一點就必須提升程序的權(quán)限,其具體實現(xiàn)如下代碼如示: #include <windows.h> #include <stdio.h> #include <stdlib.h> void main() ...{ char strKey[]="Software\Microsoft\Internet Explorer"; LPTSTR szSaveFileName; HKEY key; // 申請備份權(quán)限 HANDLE hToken; TOKEN_PRIVILEGES tkp; if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGESTOKEN_QUERY,&hToken)) return; LookupPrivilegeValue(NULL,SE_BACKUP_NAME,&tkp.Privileges[0].Luid);//申請SE_BACKUP_NAME權(quán)限 tkp.PrivilegeCount=1; tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken,F(xiàn)ALSE,&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); } 上面用到了提升權(quán)限的代碼,以前雜志上面有很多可以參考一下來看,這樣備份就完成了。 恢復(fù)注冊表可以用函數(shù)RegRestoreKey來實現(xiàn),它的原形如下: RegRestoreKey( hkey, //要恢復(fù)的鍵句柄 lpFile, //保存信息的文件名稱 dwFlage //標(biāo)志是否易失 ); 此函數(shù)的前兩個參數(shù)可以與RegSaveKey相同,而參數(shù)dwFlage為TRUE的話,是暫時恢復(fù)注冊表,如果為FALSE則是永久修改注冊表值。同樣需要注意的是使用這個函數(shù)需要有SE_RESTORE_NAME權(quán)限。
|
|