第十三章 Win32 API及Registry 我們將利用Win32 API結合上一節所介紹的元件寫一個類似檔案總管的介面,也將介紹Registry的特性,進而利用Registry讓我們的程式更加有彈性。
大綱 13-1. Win32 API初體驗 13-2. 如何設計檔案總管 13-3. Registry觀念介紹 本章習題
13-1. Win32 API初體驗 Win32 API是開發Windows程式非常重要的一環(API:Application Programming Interface)。雖然常有人都說要用Windows API就用Visual C++來開發比較方便,但是實際上在BCB上面使用Win32 API一樣非常方便,並不會因為開發環境的不同而使得Win32 API的功能打了折扣。 在BCB中也有提供與Windows API相關的On-Line Help可以閱讀,除了可以從BCB的IDE介面中呼叫出說明檔外,我們也可以直接從Windows中的程式集中的BCB內選取我們所需要的說明檔。除了BCB提供的輔助檔外,我們也可以在微軟的MSDN上面找到Windows SDK的資料。
13-1. Win32 API初體驗 範例程式 範例13-1:GetDriverType 主要用來抓取各個磁碟機的型態。從A到Z這二十六個不同的磁碟代碼所表示的是軟碟、硬碟、光碟、或是可移動式磁碟等不同的型態。 範例13-2:GetShellIcon 用來抓取某個檔案的Icon。Icon有Large Icon也有Small Icon,我們該如何抓到某個檔案的這些Icon呢?常常一個檔案內的Icon有好幾個,那代表這個檔案的Icon又是哪一個呢?在這一個範例中將告訴大家該怎麼抓取這些圖示。
13-1. Win32 API初體驗 範例程式 範例13-3:GetSpecialFolder 用來抓取一些系統資料夾的目錄。例如桌面的路徑、網路芳鄰的路徑、我的文件的目錄……etc,有非常多的系統路徑我們都可以透過這個範例來抓取。 範例13-4:MessageBox 訊息視窗的設計。雖然在Borland C++ Builder中已經有ApplicationMessageBox以及ShowMessage可以用,但是Win32 API提供的MessageBox則有著更多的功能。 範例13-5:螢幕解析度 取得目前螢幕的解析度。有人螢幕的解析度是800x600,有人是1024x768,當然也有不少人用更高或是更低的解析度。我們要如何偵測解析度的數值呢?這個範例將帶給大家答案。
13-1. Win32 API初體驗 重要細節 有些程式的最前面需要『#define NO_WIN32_LEAN_AND_MEAN』 記得要Include適當的header file。例如『ShellAPI.h』等。 有的Win32 API有限制一定要在Win95/98 or WinNT/2000的環境下使用,要注意相關訊息。不過絕大部分的Win32 API都是所有的Windows系統皆適用。
13-1. Win32 API初體驗 範例13-1:抓取磁碟機的類型 範例13-1運用很簡單的方式來偵測每一個磁碟機代碼所表示的磁碟種類。在這個範例程式中,我們使用『GetDriveType』這個函式來取得磁碟的種類。關於這個範例的詳細程式碼如下所示。 void __fastcall TForm1::BitBtn1Click(TObject *Sender) { int m; char i; char driver[3]; for (i = 'A' ; i <= 'Z' ; i++) for (m = 0 ; m < 3 ; m++) driver[m] = '\0'; driver[0] = i; driver[1] = ':'; if (GetDriveType(driver) == 0) { Edit1->Text = Edit1->Text + driver; Edit1->Text = Edit1->Text + " "; } if (GetDriveType(driver) == 1) Edit2->Text = Edit2->Text + driver; Edit2->Text = Edit2->Text + " ";
13-1. Win32 API初體驗 範例13-1:抓取磁碟機的類型 if (GetDriveType(driver) == DRIVE_REMOVABLE) { Edit3->Text = Edit3->Text + driver; Edit3->Text = Edit3->Text + " "; } if (GetDriveType(driver) == DRIVE_FIXED) Edit4->Text = Edit4->Text + driver; Edit4->Text = Edit4->Text + " "; if (GetDriveType(driver) == DRIVE_REMOTE) Edit5->Text = Edit5->Text + driver; Edit5->Text = Edit5->Text + " "; if (GetDriveType(driver) == DRIVE_CDROM) { Edit6->Text = Edit6->Text + driver; Edit6->Text = Edit6->Text + " "; } if (GetDriveType(driver) == DRIVE_RAMDISK) Edit7->Text = Edit7->Text + driver; Edit7->Text = Edit7->Text + " ";
13-1. Win32 API初體驗 範例13-1:抓取磁碟機的類型 執行結果
13-1. Win32 API初體驗 範例13-2:抓取檔案的圖示 範例13-2我們利用『SHGetFileInfo』來抓取Icon的ImageHandle,在這個程式範例中,我們放入了一個Image以及一個ImageList來當作顯示Icon的Interface。其中如果要抓取某個磁碟機的Icon的話,在Edit上要輸入『C:\』這樣的符號,如果是要抓取某個資料夾的Icon,則直接輸入資料夾的完整路徑,若是要抓取某個檔案的Icon,則輸入檔案的完整路徑及檔名。首先我們先將SHGetFileInfo的使用方法列在下方,由語法中我們也可以很輕易的看出,我們利用『SHGetFileInfo』來抓取Icon只不過是這個API的其中一個小功能的延伸,這個API可以做到的功能還很多,就等待各位使用者去發掘吧!
13-1. Win32 API初體驗 範例13-2:抓取檔案的圖示 SHGetFileInfo語法 WINSHELLAPI DWORD WINAPI SHGetFileInfo( LPCTSTR pszPath, DWORD dwFileAttributes, SHFILEINFO FAR *psfi, UINT cbFileInfo, UINT uFlag );
13-1. Win32 API初體驗 範例13-2:抓取檔案的圖示 (程式碼) void __fastcall TForm1::BitBtn1Click(TObject *Sender) { SHFILEINFO info; DWORD ImageHandle; if (ComboBox1->ItemIndex == 0) { ImageHandle = SHGetFileInfo(Edit1->Text.c_str(), 0, &info, \ sizeof(info), SHGFI_ICON | SHGFI_SYSICONINDEX); } else { ImageHandle = SHGetFileInfo(Edit1->Text.c_str(), 0, &info, sizeof(info), \ SHGFI_SMALLICON | SHGFI_OPENICON | SHGFI_SYSICONINDEX); if (ImageHandle != 0) { ImageList1->Handle = ImageHandle; ImageList1->ShareImages = true; ImageList1->GetIcon(info.iIcon,Image1->Picture->Icon);
13-1. Win32 API初體驗 範例13-2:抓取檔案的圖示 執行結果
13-1. Win32 API初體驗 範例13-3:抓取系統中的特殊目錄 範例13-3將讓大家知道一些比較特殊的資料夾的位置在哪裡。或許有人會說,我可以去Registry中慢慢找,我一樣可以找到這些特殊位置的路徑,不過這未免也太花時間了。在這個範例中,我們將要教大家一個Win32 API,這個API可以幫我們抓取系統的一些特殊資料夾的位置。 在程式13-3中我們所用的API就是『SHGetSpecialFolderLocation』這一個,但是除了『SHGetSpecialFolderLocation』外,我們也使用到其他的一些API,例如『SHGetMalloc』、以及『SHGetPathFromIDList』這兩個API,在這個範例中,我們在main.cpp檔案中的最前面一定要加入『#define NO_WIN32_LEAN_AND_MEAN』,否則將無法編譯成功。
13-1. Win32 API初體驗 範例13-3:抓取系統中的特殊目錄 SHGetSpecialFolderLocation的語法 hwndOwner -- Handle of the owner window that the client should specify if it displays a dialog box or message box. nFolder -- Value specifying the folder to retrieve the location of. This parameter can be one of the following values. ppidl -- Address that receives a pointer to an item identifier list specifying the folder's location relative to the root of the name space (the desktop). WINSHELLAPI HRESULT WINAPI SHGetSpecialFolderLocation(; HWND hwndOwner, int nFolder, LPITEMIDLIST *ppidl );
13-1. Win32 API初體驗 範例13-3:抓取系統中的特殊目錄 SHGetSpecialFolderLocation中nFolder的各種參數值 CSIDL_BITBUCKET -- 資源回收筒的路徑(Virtual Directory) CSIDL_CONTROLS -- 控制台(Virtual Directory) CSIDL_DESKTOP -- 桌面(Virtual Directory) CSIDL_DESKTOPDIRECTORY -- 桌面的路徑 CSIDL_DRIVES -- 我的電腦(Virtual Directory) CSIDL_FONTS -- 字型的目錄 CSIDL_NETHOOD -- 網路芳鄰的目錄 CSIDL_NETWORK -- 網路芳鄰的目錄(Virtual Directory) CSIDL_PERSONAL -- 我的文件夾目錄
13-1. Win32 API初體驗 範例13-3:抓取系統中的特殊目錄 SHGetSpecialFolderLocation中nFolder的各種參數值 CSIDL_PRINTERS -- 印表機目錄(Virtual Directory) CSIDL_PROGRAMS -- 程式集的目錄 CSIDL_RECENT -- 最近使用的文件的目錄 CSIDL_SENDTO -- 傳送到???的目錄 CSIDL_STARTMENU -- 開始功能表的目錄 CSIDL_STARTUP -- 啟動這個資料夾的目錄 CSIDL_TEMPLATES -- 暫存檔的目錄
13-1. Win32 API初體驗 範例13-3:抓取系統中的特殊目錄 case 8 : CSIDL_STR = CSIDL_PERSONAL ; break; case 9 : CSIDL_STR = CSIDL_PRINTERS ; break; case 10 : CSIDL_STR = CSIDL_PROGRAMS ; break; case 11 : CSIDL_STR = CSIDL_RECENT ; break; case 12 : CSIDL_STR = CSIDL_SENDTO ; break; case 13 : CSIDL_STR = CSIDL_STARTMENU ; break; case 14 : CSIDL_STR = CSIDL_STARTUP ; break; case 15 : CSIDL_STR = CSIDL_TEMPLATES ; break; } if(SUCCEEDED(SHGetMalloc(&pShellMalloc))) { if(SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_STR, &pidl))) if(SHGetPathFromIDList(pidl, szDir)) Edit1->Text = szDir; pShellMalloc->Free(pidl); pShellMalloc->Release(); //--------------------------------------------------------------------- //千萬不要忘記在這個範例要加入這個定義 #define NO_WIN32_LEAN_AND_MEAN void __fastcall TForm1::BitBtn1Click(TObject *Sender) { LPITEMIDLIST pidl; LPMALLOC pShellMalloc; char szDir[MAX_PATH]; int CSIDL_STR; switch (ComboBox1->ItemIndex) case 0 : CSIDL_STR = CSIDL_BITBUCKET ; break; case 1 : CSIDL_STR = CSIDL_CONTROLS ; break; case 2 : CSIDL_STR = CSIDL_DESKTOP ; break; case 3 : CSIDL_STR = CSIDL_DESKTOPDIRECTORY ; break; case 4 : CSIDL_STR = CSIDL_DRIVES ; break; case 5 : CSIDL_STR = CSIDL_FONTS ; break; case 6 : CSIDL_STR = CSIDL_NETHOOD ; break; case 7 : CSIDL_STR = CSIDL_NETWORK ; break;
13-1. Win32 API初體驗 範例13-3:抓取系統中的特殊目錄 執行結果
13-1. Win32 API初體驗 範例13-4: Win32 API所提供的MessageBox 雖然BCB有提供ApplicationMessageBox,也有ShowMessage等函式可以顯示訊息方塊,可是大家一定會覺得,好像總是缺個什麼似的,就是跟Windows出現的訊息方塊有點不一樣。基於以上的原因,我們在範例13-4中將告訴大家如何使用Win32 API來實做MessageBox。 在這個範例中,我們除了使用到MessageBox這個API外,我們也用到了『GetActiveWindow』這個API,這個API主要的功能是抓取目前Active的視窗的Handle,借此Handle程式才知道這個訊息窗是哪個視窗發出來的。 MessageBox的語法 int MessageBox( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box );
13-1. Win32 API初體驗 範例13-4: Win32 API所提供的MessageBox MessageBox的傳回值 數值 說明 IDABORT 按下ABORT的按鈕 IDCANCEL 按下CANCEL的按鈕 IDIGNORE 按下IGNORE的按鈕 IDNO 按下NO的按鈕 IDOK 按下OK的按鈕 IDRETRY 按下RETRY的按鈕 IDYES 按下YES的按鈕
13-1. Win32 API初體驗 範例13-4: Win32 API所提供的MessageBox void __fastcall TForm1::BitBtn1Click(TObject *Sender) { unsigned int Flag; AnsiString TitleStr, BodyStr; switch (ComboBox1->ItemIndex) case 0: Flag = MB_ABORTRETRYIGNORE; break; case 1: Flag = MB_OK; break; case 2: Flag = MB_OKCANCEL; break; case 3: Flag = MB_RETRYCANCEL; break; case 4: Flag = MB_YESNO; break; case 5: Flag = MB_YESNOCANCEL; break; } switch (ComboBox2->ItemIndex) case 0: Flag = Flag | MB_ICONEXCLAMATION; break; case 1: Flag = Flag | MB_ICONWARNING; break; case 2: Flag = Flag | MB_ICONINFORMATION; break; case 3: Flag = Flag | MB_ICONASTERISK; break; case 4: Flag = Flag | MB_ICONQUESTION; break; case 5: Flag = Flag | MB_ICONSTOP; break; case 6: Flag = Flag | MB_ICONERROR; break; case 7: Flag = Flag | MB_ICONHAND; break; } switch (ComboBox3->ItemIndex) { case 0: Flag = Flag | MB_DEFBUTTON1; break; case 1: Flag = Flag | MB_DEFBUTTON2; break; case 2: Flag = Flag | MB_DEFBUTTON3; break; case 3: Flag = Flag | MB_DEFBUTTON4; break; TitleStr = Edit1->Text; BodyStr = Edit2->Text; MessageBox(GetActiveWindow(), BodyStr.c_str(), TitleStr.c_str(), Flag);
13-1. Win32 API初體驗 範例13-4: Win32 API所提供的MessageBox 執行結果
13-1. Win32 API初體驗 範例13-5:抓取目前螢幕解析度 在程式13-5中,我們將告訴各位我們怎麼抓取目前螢幕的解析度。因為這個範例比要簡單,所以我們直接列出程式碼讓各位看看,相信大家一定可以直接從程式碼中了解這個範例的意義。在這個範例中我們只使用『GetSystemMetrics』這個API。 程式碼 & 執行結果 void __fastcall TForm1::Button1Click(TObject *Sender) { int X, Y; AnsiString Content; X = GetSystemMetrics(SM_CXSCREEN); Y = GetSystemMetrics(SM_CYSCREEN); Content = "目前螢幕解析度為:" + IntToStr(X) + "x" + IntToStr(Y); MessageBox(GetActiveWindow(), Content.c_str(), "解析度", MB_OK); }
13-2. 如何設計檔案總管 談到檔案總管時,一定不能忘記介紹『Name Space』這個東西。Name Space簡單的說就是Windows的骨幹,整個Windows其實可以看成一棵樹,從桌面一直向下延伸,所以不管是『我的電腦』、『控制台』、『各個磁碟機』、『我的文件夾』、『網路芳鄰』、『印表機』……等等,都是在這個Name Space中。要弄懂Name Space的API並沒有想像中的難。而且在上一小節中,範例13-2以及13-3也是Name Space的範例之一,看起來是不是就簡單多了呢! 反過來說,要寫出檔案總管的介面,我們也不一定全部都要使用Name Space來寫,可以某部分採用Name Space,某部分採用我們一般的程式設計,雖然這樣看起來可能會跟真正的檔案總管有一點出入,不過至少我們也做到了『大部分』的相似,而且在設計上的難度也大大的降低。 在BCB的Example中,也有一個VirtualListView的範例,就是利用Name Space寫出的檔案瀏覽程式,該範例就很像檔案總管右邊的ListView。接著我們在範例13-6中,將利用簡單的檔案搜尋技巧將整個電腦的TreeView設計出來。
13-2. 如何設計檔案總管 範例13-6:檔案總管的TreeView實作 在這個範例中,我們為了不建立整個Name Space,所以用了許多『Dirty』的方法,當然這樣的程式並不能算是一個相當好的程式,不過在這邊為了讓大家提高對於這邊的學習興趣,所以只好採用這樣的方式建構出類似Name Space的整棵Tree。關於建構整個TreeView的程式碼如下所示,程式的說明將會直接在程式碼中以註解的方式出現!
13-2. 如何設計檔案總管 範例13-6:檔案總管的TreeView實作 程式碼 – main.h class TForm1 : public TForm { __published: // IDE-managed Components TTreeView *TreeView; void __fastcall FormCreate(TObject *Sender); void __fastcall TreeViewExpanding(TObject *Sender, TTreeNode *Node, bool &AllowExpansion); private: // User declarations void __fastcall TreeViewInitial(); void __fastcall CreateNewNode(char *str, int image1, int image2); void __fastcall CreateNewDisk(char *str, int image1, int image2); void __fastcall SearchDisk(); void __fastcall FindFirstLevelTree(char *prifix); void __fastcall FindSecondLevelTree(char *prifix); AnsiString __fastcall TreeViewGetPath(TTreeNode *Node); int __fastcall GetTreeViewShellImage(char *str, bool Open); AnsiString __fastcall GetSpecialFolder(int CSIDL_STR); int __stdcall CompareFunc(LPARAM LParam1, LPARAM LParam2, LPARAM LData); public: // User declarations __fastcall TForm1(TComponent* Owner); };
13-2. 如何設計檔案總管 範例13-6:檔案總管的TreeView實作 (程式碼 – main.cpp) //--------------------------------------------------------------------- //該程式需要定義"NO_WIN32_LEAN_AND_MEAN" #define NO_WIN32_LEAN_AND_MEAN #include <vcl.h> #pragma hdrstop #include "main.h" //將shellapi.h include進來 #include <shellapi.h> #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //宣告兩個TreeNode //*root用來紀錄Root //*node用在動態產生Node TTreeNode *root, *node; __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //----------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) //將Form建立的時候呼叫TreeViewInitial //TreeViewInitial就是整個Tree產生的起點 //在TreeView底下的這將進300行的Code中 //有使用到ShellAPI(NameSpace) //也有使用到最基本的檔案搜尋的API TreeViewInitial();
13-2. 如何設計檔案總管 範例13-6:檔案總管的TreeView實作 (程式碼 – main.cpp) 因為程式碼的部分相當的多,詳細的程式碼請直接參考書上的範例 執行結果
13-2. 如何設計檔案總管 範例13-7:檔案總管的ListView實作 接下來我們要談的就是範例程式13-7,也就是列出File的程式碼,在這部分我們也沒有使用Name Space的方式來抓取檔案,我們採用最簡單的方式,利用FindFirst和FindNext來找出所有的檔案,使用方式就是輸入路徑後讓程式幫我們搜尋出目錄中的所有檔案。在這個程式中,我們有一個Edit可以讓大家輸入路徑,輸入後按下『Find』,程式就會自動在所輸入的目錄內搜尋檔案。
13-2. 如何設計檔案總管 範例13-7:檔案總管的ListView實作 程式碼 void __fastcall TForm1::Button1Click(TObject *Sender) { AnsiString PathStr; TSearchRec sr; int iAttributes = faReadOnly | faDirectory; TListItem *p; if (Edit1->Text.c_str()[Edit1->Text.Length() - 1] == '\\') PathStr = Edit1->Text + "*"; else PathStr = Edit1->Text + "\\*"; ListView1->Items->Clear(); if (FindFirst(PathStr, iAttributes, sr) == 0) do { if ((sr.Attr & iAttributes) == sr.Attr) p = ListView1->Items->Add(); p->Caption = sr.Name; p->SubItems->Add(IntToStr(sr.Size)); p->SubItems->Add(FileDateToDateTime(sr.Time)); } } while (FindNext(sr) == 0); FindClose(sr);
13-2. 如何設計檔案總管 範例13-7:檔案總管的ListView實作 執行結果
13-2. 如何設計檔案總管 範例13-7:檔案總管的ListView實作 在這一節中我們大概只提到Tree的建法以及抓取檔案的方式,其實如果大家能將上面兩個範例的原理弄清楚,要寫一個簡單的檔案總管已經不是問題了,在這邊我們稍微提一下將Tree以及List整合起來有哪些需要注意的事項。 點下左方Tree上面的Node必須也要同時抓到TreeNode所表示的路徑,並且將這個路徑傳給List上搜尋檔案的程式碼。這樣才有辦法將ListView上的檔案資訊更新。 如果有一個ComboBox可以用來記錄目前路徑資料的話,當我們在ComboBox上輸入新的路徑時,並需要同時更新到TreeView以及ListView上。 在ListView上進入另一層目錄時,TreeView必須要同時更新。ComboBox也要更新。 若是按下『上一頁』或是『下一頁』之類的按鈕,TreeView以及ListView要同時更新。
13-3. Registry觀念介紹 Registry的觀念可以說從Win95 OSR2版本(俗稱Win97)之後就大行其道,一直到Win98/NT/2000/ME甚至是現在最新的Windows XP、Windows .NET,都持續的使用Registry,我們可以將Registry想成一個很大的資料庫,也可以想成一個很大的檔案,也可以想成是一堆資訊在裡面的容器,總之,關於這台電腦的所有事情幾乎都可以在Registry中找到。
13-3. Registry觀念介紹 在以前Win 3.1的時代,我們常常看到有許多『.ini』的檔案存在於電腦中,也常常看到一些config類型的檔案等等。其實在Windows 3.1的時候也有Registry的觀念,只是那時候儲存的資訊只有跟OLE相關的物件而已,而現在是不管什麼資料都全部放在Registry裡面了。Registry經過分析大概有以下這些特點: 集中化:不再像過去一樣散亂在各處。 結構化:樹狀目錄。 伸縮性:可用二進位、八進位、十進位、以及十六進位各式各樣的值。 安全性:有存取控制的限制。 隱密性:要了解或是知道放在哪邊並不容易。 不規則:一開始就很大,且很複雜,各項資料只有個大概的規則。 危險性:Registry是即時的、動態的,一改錯就有可能造成問題。
13-3. Registry觀念介紹 在Windows中要開啟Registry,我們可以在開始選單中的『執行』輸入『regedit』後按下『Enter』,這時候就會將Registry的編輯畫面呼叫出來。一般來說,除了有經驗的管理人員外,我們並不建議一般不熟悉Registry的管理者任意修改Registry中的值。除此之外,還要養成隨時備份Registry的習慣。
13-3. Registry觀念介紹 在Registry中主要分成五大部分: HKEY_CLASSES_ROOT HKEY_LOCAL_MACHINE\SOFT WARE\Classes的一個鏡對。也提供和Windows 3.X登陸資料庫的相容系。 HKEY_CURRENT_USER 目前使用者的相關事項及限制。當HKEY_USERS和HKEY_CURRENT_USER有一樣的值時,以HKEY_CURRENT_USER有較高的優先權。 HKEY_LOCAL_MACHINE 每一部份的資料都適合用在此電腦上,並不會因為使用者的不同而有不同的設定。 HKEY_USERS 包含帳戶內所定義的使用者。通常只會包含預設使用者跟目前正在使用的使用者,不然全部使用者都讀出來可能會造成浪費太多記憶體。 HKEY_CURRENT_CONFIG 系統目前的硬體資訊。
13-3. Registry觀念介紹 HKEY_LOCAL_MACHINE主要的子鍵 HARDWARE 在HKEY_LOCAL_MACHINE中最常變動的數值,每次開機都會自動偵測並且更新。在HARDWARE中也有三個子鍵,各是Description、DeviceMap、以及ResourceMap。Description主要的功能是開機時系統會執行自動偵測的程式,並將這程式傳回的資訊寫入Description。DeviceMap是裝置和驅動程式的設定值以及指標。ResourceMap則是記錄著硬體所使用的IRQ、DMA、I/O Port等資源。 SAM SAM就是Security Account Manager,SAM其實就是SECURITY中的一個子鍵。 SECURITY 要修改這個子鍵下的資料必須要呼叫Windows NT Security API才能更改,無法直接從編輯程式中修改。
13-3. Registry觀念介紹 HKEY_LOCAL_MACHINE主要的子鍵 (cont.) SOFTWARE 定義及維護所有32位元的軟體設定值,包括Windows本身也是。在這底下還包含著五個重要子鍵:Classes、Microsoft、Program Groups、Secure、以及Windows 3.1 Migration Status。其中Classes記錄著檔案和OLE之間的關係,這是由Windows 3.1那邊演變而來的。Microsoft內包含了系統上安裝的Microsoft相關軟體,當然也有一些協力廠商的軟體也會在這邊出現。Program Groups記錄著常用的應用程式或是軟體清單。Secure幾乎沒有用處。 SYSTEM 負責維護驅動程式和服務等資訊。在這底下也包含多個子鍵,大部分是ControlSet。其中ControlSet有四種狀態,現在值、預設值、失敗、以及LastKnowGood這四種狀態。而這四種檔案對應到哪個狀態則可以參閱SYSTEM\Select Key。
13-3. Registry觀念介紹 Registry中的五種資料型態 REG_BINARY 二進位資料。 REG_WORD REG_SZ 單純的字串。 REG_MULTI_SZ 多個字串,用空白隔開。 REG_EXPEND_SZ 可擴充的字串,一般都是變數。
13-4. 讀寫Registry 在寫程式之前,我們需要先知道在BCB中有一個物件叫做『TRegistry』,在這個類別中提供了許多屬性及方法可以讓我們使用。我們在程式13-8中的所有做法也都是照著TRegistry所提供的功能做出來的。下面我們先列出一些比較重要的方法讓大家知道: CloseKey, CreateKey, DeleteKey, DeleteValue, GetDateType, GetValueName, KeyExists, LoadKey, MoveKey, OpenKey, OpenKeyReadOnly, ReadBinaryData, ReadBool, ReadCurrency, ReadDate, ReadDateTime, ReadFloat, ReadIntegre, ReadString, ReadTime, RegistryConnect, RenameValue, ReplaceKey, RestoreKey, SaveKey, UnLoadKey, ValueExists, WriteBinaryData, WriteBool, WriteCurrency, WriteDate, WriteDateTime, WriteFloat, WriteIntegre, WriteString, WriteTime. 看到這麼多的Method都是由TRegistry實在是相當的恐怖,所以在這邊我們不在投影片上列出各個Method的說明,請大家自行參閱書上的說明。
13-4. 讀寫Registry 範例13-8:從Registry讀出資料 程式碼 (main.h) 記得要加入Registry的標頭檔 #include <Vcl\Registry.hpp> 也要宣告一個TRegistry的物件 TRegistry *Registry;
13-4. 讀寫Registry 範例13-8:從Registry讀出資料 (程式碼: main.cpp) void __fastcall TForm1::BitBtn1Click(TObject *Sender) { if ((ComboBox1->Text != "") && (Edit1->Text != "") && (Edit2->Text != "")) Registry = new TRegistry; switch (ComboBox1->ItemIndex) case 0 : Registry->RootKey = HKEY_CLASSES_ROOT; break; case 1 : Registry->RootKey = HKEY_CURRENT_USER; break; case 2 : Registry->RootKey = HKEY_LOCAL_MACHINE; break; case 3 : Registry->RootKey = HKEY_USERS; break; case 4 : Registry->RootKey = HKEY_CURRENT_CONFIG; break; } Registry->OpenKey(Edit1->Text.c_str(), false); Edit3->Text = Registry->ReadString(Edit2->Text.c_str()); Registry->Free(); } else MessageBox(GetActiveWindow(), "請輸入完整資訊", "錯誤", MB_OK|MB_ICONSTOP); //--------------------------------------------------------------------------- void __fastcall TForm1::BitBtn2Click(TObject *Sender) { ComboBox1->Text = ""; Edit1->Text = ""; Edit2->Text = ""; Edit3->Text = "";
13-4. 讀寫Registry 範例13-8:從Registry讀出資料 執行結果
13-4. 讀寫Registry 範例13-9:寫入資料到Registry中 程式碼 Main.h也要如上個範例一樣加入所需要的敘述,如標頭檔或是宣告等等 Main.cpp如下所示 case 3 : Registry->RootKey = HKEY_USERS; break; case 4 : Registry->RootKey = HKEY_CURRENT_CONFIG; break; } Edit1->Enabled = true; BitBtn2->Enabled = true; ComboBox1->Enabled = false; BitBtn1->Enabled = false; void __fastcall TForm1::BitBtn1Click(TObject *Sender) { if (ComboBox1->Text != "") Registry = new TRegistry; switch (ComboBox1->ItemIndex) case 0 : Registry->RootKey = HKEY_CLASSES_ROOT; break; case 1 : Registry->RootKey = HKEY_CURRENT_USER; break; case 2 : Registry->RootKey = HKEY_LOCAL_MACHINE; break;
13-4. 讀寫Registry 範例13-9:寫入資料到Registry中(程式碼) void __fastcall TForm1::BitBtn2Click(TObject *Sender) { if (Edit1->Text != "") Registry->OpenKey(Edit1->Text.c_str(), true); Edit2->Enabled = true; Edit3->Enabled = true; ComboBox2->Enabled = true; BitBtn3->Enabled = true; Edit1->Enabled = false; BitBtn2->Enabled = false; } void __fastcall TForm1::BitBtn3Click(TObject *Sender) { if ((Edit2->Text != "") && (Edit3->Text != "") && (ComboBox2->Text != "")) switch (ComboBox2->ItemIndex) case 0 : Registry->WriteString(Edit2->Text, Edit3->Text); break; case 1 : Registry->WriteString(Edit2->Text, Edit3->Text); break; case 2 : Registry->WriteString(Edit2->Text, Edit3->Text); break; case 3 : Registry->WriteInteger(Edit2->Text, Edit3->Text.ToInt()); break; } Edit2->Text = ""; Edit3->Text = ""; ComboBox2->Text = "";
13-4. 讀寫Registry 範例13-9:寫入資料到Registry中(程式碼) void __fastcall TForm1::BitBtn4Click(TObject *Sender) { ComboBox1->Text = ""; ComboBox2->Text = ""; Edit1->Text = ""; Edit2->Text = ""; Edit3->Text = ""; ComboBox1->Enabled = true; BitBtn1->Enabled = true; Edit1->Enabled = false; BitBtn2->Enabled = false; BitBtn3->Enabled = false; Edit2->Enabled = false; Edit3->Enabled = false; ComboBox2->Enabled = false; Registry->Free(); }
13-4. 讀寫Registry 範例13-9:寫入資料到Registry中 執行結果
本章習題 試著找書或是從網頁找到Windows中的Name Space這方面的資料。 試著看懂BCB中VirtualListView這個範例。 嘗試自己利用Name Space寫出完整的檔案總管,介面可以先以範例11-6朱的執行檔為基準。 利用Registry紀錄自己開發的軟體每次結束時的位置,以便於下次啟動同一套軟體時可以在同一個地方出現。 比較使用Visual C++和BCB開發Win32 API程式的差異性。