對象的持久性和文件I/O

發表于:2007-07-14來源:作者:點擊數: 標簽:
內容提要 理解對象的持久性* File Demo應用程序* 創建一個持久類* 直接讀寫文件* 創建自己的CArchive對象* 理解注冊 關鍵詞 文章正文 對象的持久性 程序需要完成的一項最重要的事情是,當數據以某種方式改變之后,程序能夠保存用戶的數據。沒有保存被編輯過
內容提要
      理解對象的持久性* File Demo應用程序* 創建一個持久類* 直接讀寫文件* 創建自己的CArchive對象* 理解注冊
關鍵詞
        
文章正文
   對象的持久性
       程序需要完成的一項最重要的事情是,當數據以某種方式改變之后,程序能夠保存用戶的數據。沒有保存被編輯過的數據的功能,用戶利用應用程序所做的工作只能在應用程序執行時存在,在用戶離開應用程序時就會消失。這不是一種好的工作方式!在很多情況下,尤其在使用AppWizard來創建應用程序時,Visual C++提供許多代碼用于保存和加載數據。然而,在一些情況下—特別是當你創建自己對象類型時,必須做一部分額外的工作來保存用戶文件的更新。
當讀者正在編寫一個應用程序時,要處理大量的不同類型對象。你的數據對象有些可以像整數和字符一樣簡單。其他對象可以是諸如CString類的字符串或你的定制類所創建的對象。當在需要保存和加載文檔的應用程序中使用對象時,需要用某種方式保存和加載這些對象的狀態,這樣才能重新創建它們,如同上一次任務結束時那樣。

一個對象所具有的保存和加載其狀態的能力稱為“持久性”。幾乎所有的MFC類都是持久的,這是因為它們是直接或間接由MFC的CObject類派生的,該類提供了保存和加載對象狀態的基本功能。

File Demo應用程序

你在使用Visual C++的AppWizard來創建程序時,將得到一個可以使用文檔和視圖類來組織、編輯和顯示數據的應用程序。由CDocument類派生的文檔對象,負責在會話期間保存應用程序的數據和在不同時期保存和加載數據,使得文檔從一個會話過程到另一個會話過程時得以保持。

創建一個File Demo應用程序,演示保存和加載由CDocument派生的對象的數據的基本技術。File Demo的文檔是包含一個短消息的單一字符串。

有三個菜單項與該應用程序相關。當程序初次運行時,消息自動設置字符串為Default Message。用戶可以選擇Edit|Change Message來改變這條消息。File|Save菜單項可以存儲文檔,File|Open菜單項則從磁盤加載文檔。

☆  文檔類的回顧

任何編寫過程序的人都保存和打開過文件,從用戶的觀點看這就是對象的持久性。我們將可以學到持久性工作的原理。

當使用Visual C++的AppWizard創建程序時,必須完成以下幾步使得文檔能夠保存和加載其狀態。這些步驟是:

1) 定義保存文檔數據的成員變量。

2) 在文檔類的OnNewDocument( )函數中初始化該成員變量。

3) 在視圖類的成員函數OnDraw( )中顯示當前文檔。

4) 在視圖類中提供成員函數以使得用戶能夠編輯文檔。

5) 在文檔類的成員函數Serialize( )中增加保存和加載構成該文檔的數據所需要的代碼。

當應用程序處理多文檔時,你需要做一點額外的工作來保證使用、修改或保存了正確的文檔。所幸的是大部分工作都由MFC和AppWizard完成了。

☆ 創建File Demo應用程序

要想創建File Demo應用程序,首先利用AppWizard創建一個SDI應用程序。因為要使AppWizard中的所有選項都保持為其默認值,所以可以在第1步選擇了SDI并使Document/View被選中之后就單擊Finish按鈕,以加快速度。

在ClassView中雙擊CfileDemoDoc編輯文檔類的頭文件。在Attributes部分添加一個CString成員變量,變量名為m_message,使得Attributes部分如下所示:

// Attributes

public:

    CString  m_message;

在此例中,存儲的文檔只不過是單一字符串對象。通常,存儲的文檔要更加復雜。然而,用單字符串演示文檔持久性的基本方法已經足夠。對于MFC程序員來說,在文檔中使用公有變量而不用帶有公共訪問函數的私有變量是很常見的。這使得編寫訪問文檔變量的視圖類的代碼簡單了一些。不過,這會使以后加強這個程序的功能時要做的工作增加。

像所有的文檔數據一樣,這個變量必須要初始化。這個工作在OnNewDocument( )函數中進行。在ClassView中擴展CFileDemoDoc,雙擊OnNewDocument( )編輯它。在其中添加一行初始化串的代碼,如程序清單1所示。在此之前要把TODO注釋刪除掉。

程序清單1   初始化文檔的數據

BOOL CFileDemoDoc::OnNewDocument()

{

    if (!CDocument::OnNewDocument())

        return FALSE;

  

    m_message = 褼efault Message*;

  

    return TRUE;

}

經過對文檔類的數據成員m_message進行初始化,應用程序可以在視圖窗口中顯示該數據。你只須編輯視圖類的OnDraw( )函數(參見程序清單2)。在ClassView中擴展CFileDemoView,并雙擊OnDraw( )函數編輯它。在這里也只要刪除TODO注釋,添加一行代碼。

程序清單2   顯示文檔的數據

void CFileDemoView::OnDraw(CDC* pDC)

{

    CFileDoc* pDoc = GetDocument();

    ASSERT_VALID(pDoc);

  

    pDC->TextOut(20, 20, pDoc->m_message);

}

現在創建File Demo應用程序,確保沒有錯誤,運行這個程序。缺省的消息就顯示在屏幕中。

現在,增加程序的功能允許程序員通過修改串值來編輯應用程序的文檔。理論上說應用程序應該顯示一個對話框,讓用戶輸入字符串。在這兒,我們利用Edit|Change Message菜單項來自動賦給消息串一個不同的值。

單擊Resource選項卡切換到ResourceView,擴展resources,擴展Menus,雙擊IDR_MAINFRAME,編輯它。單擊菜單中的Edit項,下拉出一個子菜單列表。單擊列表末尾的空白項并輸入Change &Message,向菜單中添加了另外一項。

選擇View|ClassWizard使得這個菜單項與代碼相關聯。先使ID_EDIT_ CHANGEMESSAGE 被中,再從右上角的下拉框中選擇CFileDemoView 。在左下角的框中單擊COMMAND,然后單擊Add Function按鈕。單擊對話框的OK按鈕,接受所推薦的函數名稱-OnEditChangeMessage( )。單擊Edit Code,在編輯器中打開這個新函數,按程序清單3編輯它。

程序清單3   修改文檔的數據

void CFileDemoView::OnEditChangemessage()

{

   CTime now = CTime::GetCurrentTime();

   CString changetime = now.Format(褻hanged at %B %d %H:%M:%S*);

   GetDocument()->m_message = changetime;

   GetDocument()->SetModifiedFlag();

   Invalidate();

}

  

  此函數響應應用程序的Edit|Message命令,利用當前的日期和時間創建一個串,并將它傳送給文檔數據成員。對文檔類函數SetModifiedFlag( )的調用通知對象其內容已經改變。在用戶可能修改數據的地方調用SetModifiedFlag( ),就會使程序在用戶試圖不保存修改過的數據而退出時發出警告。最后,代碼調用Invalidate( ),強迫屏幕重繪。

提示:如果m_message是文檔類的私有成員變量,就應該用公共的SetMessage( )函數來調用SetModifiedFlag( )函數。這樣每次m-message改變時,都會調用SetModifiedFlag()函數。這是編寫真正的面向對象的程序的好處。

文檔類的Serialize( )函數處理保存和加載文檔的數據。程序清單4顯示了由AppWizard生成的Serialize( )的空殼。

程序清單4   FILEVIEW.CPP—文檔類的Serialize( )函數

void CFileDoc::Serialize(CArchive& ar)

{

    if (ar.IsStoring())

    {

        // TODO: add storing code here

  

}

    else

    {

        // TODO: add loading code here

}

}

由于Cstring類(m_message是其的一個對象)定義了>>和<<運算符用于向文檔傳遞字符串,因此保存和加載文檔類的數據就是一件簡單的事情。在提醒增加保存代碼的注釋的地方增加以下行:

ar<<m_message;

同樣,在加載代碼的地方增加增加以下行:

ar>>m_message;

《運算符將CSTring m_message傳送給文檔》運算符把文檔賦給m_message。只要所有的文檔成員變量都是如整數或字符那樣的簡單數據類型,利用象CString這樣已經定義了此運算符的MFC類,保存和加載數據將非常簡單,此運算符是為下列這些簡單數據類型定義的:

* BYTE

* WORD

* int

* LONG

* DWORD

* float

* double

創建這個應用程序File Demo,并運行它。選擇Edit|Message命令,就可以看到屏幕上出現了新的串,選擇File|Save并輸入一個易記的文件名。再次修改消息,選擇File|New,用戶會得到應先保存當前修改過的數據的警告。選擇File|Open瀏覽文件,或者在File菜單的底部找到剛剛存儲的文件的文件名,單擊它也可以打開這個文件??梢钥吹紽ile Demo的確可以保存和重新載入一個串。

File Demo用命令修改了串值

用戶不會無意丟失未保存的數據了

創建一個持久類

但是如果讀者已經創建了自己的定制類用于保存文檔的元素該怎么辦呢?如何使得此類的一個對象成為持久呢?

假設讀者現在想改進File Demo應用程序使其能夠在一個稱為CMessages的定制類中包含程序的數據。此時該成員變量稱為m_messages,它是CMessages的一個實例。此類包含三個CString對象,如果要使應用程序正常運行,必須對上述三個對象進行保存和加載。保存和加載它們的一種方法是分別保存和加載每一個字符串,代碼如程序清單5所示:

程序清單5   保存新類的字符串的一種方注

void CFileDoc::Serialize(CArchive& ar)

{

    if (ar.IsStoring())

    {

        ar << m_messages.m_message1;

        ar << m_messages.m_message2;

        ar << m_messages.m_message3;

    }

    else

    {

        ar >> m_messages.m_message1;

        ar >> m_messages.m_message2;

        ar >> m_messages.m_message3;

     }

}

如果在CMessages類中定義這三個成員變量為public,而且知道類是如何實現的,讀者就可以編寫如清單5中所示的代碼。之后,如果類以任何其他方式發生變化,此代碼也必須改變。把存貯和加載工作交付給CMessages類自身去完成更具有面向對象的意義。這需要一些準備,創建一個能串行化的其成員變量的類的基本步驟如下:

1) 由CObject派生此類。

2) 在類定義部分放置DECLAR_SERIAL( )宏。

3) 在類實現部分放置IMPLEMENT_SERIAL( )宏。

4) 重載類中的Serialize( )函數。

5) 提供一個類的空的缺省構造函數。

☆ File Demo 2 應用程序

下一個示例應用程序File Demo 2,演示創建一個可由此創建持久對象的類的步驟。它包含了可以修改三個串的Edit|Change Messages命令。與File Demo程序類似,當用戶選擇File|Save或File|Open時可以保存或加載文檔。

與創建File Demo程序類似,創建一個名為MultiString的SDI應用程序。添加一個成員變量到文檔中,使得MultiStringDoc.h的Attributes部分如下所示:

//Attributes

public:

  CMessages  m_messages;

下一步就是編寫CMessage類。

☆  CMessages類一覽

在理解文檔類如何成功實現保存和加載其內容之前,需要了解CMessages類是如何工作的,文檔類的m_messages數據成員是該類的一個對象。在查看此類時,將看到上面提到的創建持久類所需的五個步驟是如何實現的。

要想創建CMessages類,首先選擇Insert|New Class。修改其類類型為普通類,并命名為CMessages。在屏幕的下部,選擇CObject作為基類名,并將As列設置為public,這樣將創建兩個文件:messages.h作為頭文件,messages.cpp作為代碼文件。同時還有一些簡單的代碼被添加到這些文件中(這時候可能會有一條警告信息,找不到CObject的頭文件,單擊OK忽略它。因為CObject像所有MFC文件一樣,不需要包含額外的處理就是可用的)。

切換到Multistringdoc.h中,將下面的代碼行添加到類定義部分:

#include "Messages.h"

這就保證了編輯器知道CMessages類的存在?,F在切換回Messages.h,在其中添加下面的代碼行:

DECLARE_SERIAL(CMessages)

protected:

   CString m_message1;

   CString m_message2;

   CString m_message3;

public:

   void SetMessage(UINT msgNum, CString msg);

   CString GetMessage(UINT msgNum);

   void Serialize(CArchive& ar);

DECLARE_SERIAL( )宏提供了實現對象持久性所需的附加的函數和成員變量聲明。

下一步是類的數據成員,它們是Cstring類的三個對象。請注意它們現在是protected性質的成員變量。其后是public類型的成員函數。SetMessage( )函數的變元為需設置的字符串的索引號和字符串的新值,該函數使得程序能夠改變一個數據成員。GetMessage( )是一個實現函數,它使得程序能夠檢索任何字符串的當前值。它的唯一參量是要檢索的字符串的索引號。

最后,該類重載Serialize( )函數,在此所有的數據得以保存和加載,Serialize( )函數是一個持久對象的核心,每個持久類以不同方式實現。程序清單6顯示了這些新成員函數的代碼,將它添加到messages.cpp中。

程序清單6   MESSAGE.CPP—CMessage類的實現文件

void CMessages::SetMessage(UINT msgNum, CString msg)

{

     switch (msgNum)

     {

     case 1:

          m_message1 = msg;

          break;

     case 2:

        m_message2 = msg;

          break;

  

     case 3:

        m_message3 = msg;

          break;

     }

   SetModifiedFlag();

}

CString CMessages::GetMessage(UINT msgNum)

{

   switch (msgNum)

   {

      case 1:

         return m_message1;

      case 2:

         return m_message2;

      case 3:

         return m_message3;

      default:

         return  " ";

   }

}

void CMessages::Serialize(CArchive& ar)

{

    CObject::Serialize(ar);

    if (ar.IsStoring())

    {

        ar << m_message1 << m_message2 << m_message3;

    }

    else

    {

        ar >> m_message1 >> m_message2 >> m_message3;

    }

}

在SetMessage( )和GetMessage( )函數中沒有任何技巧,它們精確地完成所給予的任務。不過在Serialize( )函數中可能會產生一些問題。首先,注意函數體的第一行調用了基類的Serialize( )函數。這是一個標準的基類函數的重載函數。在本例中,對CObject::Serialize( )的函數調用沒做多少事情,這是因為CObject類的Serialize( )函數為空。盡管如此,調用基類的Serialize( )函數是一個需要養成的好習慣,因為你建立的持久類不總是直接由CObject類派生的。

在調用的此函數基類版本后,Serialize( )用與文檔對象相同的方式進行數據的保存和加載。因為必須串行化的數據成員是CString對象。程序可以使用>>和<<運算符將字符串寫入磁盤。

在messages.cpp的頂部,include語句之后,添加下面的代碼行:

IMPLEMENT_SERIAL(CMessages,CObject,0)

IMPLEMENT_SERIAL宏與DECLARE_SLRIAL( )組成一對,它提供了使類具有持續能力的函數的實現。此宏的三個變元是類名、直接基類名和方案號。方案號類似一個版本號。在多數情況下,可使用0或1作為方案號。

☆  在程序中使用CMessages類

現在CMessages已經被定義和實現了,MultiString的文檔和視圖的成員函數也已經可以執行了。首先擴展CMultiStringDoc,雙擊OnNewDocument( )編輯它。將下面的代碼添加到TODO注釋處:

m_messages.SetMessage(1, "Default Message 1");

m_messages.SetMessage(2, "Default Message 2");

m_messages.SetMessage(3, "Default Message 3");

由于文檔類不能直接訪問數據對象的數據成員,它必須通過調用CMessages類的SetMessage( )成員函數來初始化每一個字符串。

擴展CMultiStringView,雙擊OnDraw( )函數,按下面的代碼行來編輯它:

void CMultiStringView::OnDraw(CDC* pDC)

{

        CMultiStringDoc* pDoc = GetDocument();

        ASSERT_VALID(pDoc);

    pDC->TextOut(20, 20, pDoc->m_messages.GetMessage(1));

    pDC->TextOut(20, 40, pDoc->m_messages.GetMessage(2));

    pDC->TextOut(20, 60, pDoc->m_messages.GetMessage(3));

}

與在創建File Demo時所作的類似,添加“Change Message”項目到Edit菜單中。將它與名為OnEditChangemessages的函數相關聯。這個函數將會通過調用CMessages對象的成員函數來修改數據,如程序清單7所示。視圖類函數OnDraw( )同樣調用GetMessage( )成員函數,以訪問CMessages類的串。

程序清單7   編輯數據串

void CMultiStringView::OnEditChangemessages()

{

   CMultiStringDoc* pDoc = GetDocument();

   CTime now = CTime::GetCurrentTime();

   CString changetime = now.Format(褻hanged at %B %d %H:%M:%S*);

   pDoc->m_messages.SetMessage(1, CString(襍tring 1 *) + changetime);

   pDoc->m_messages.SetMessage(2, CString(襍tring 2 *) + changetime);

   pDoc->m_messages.SetMessage(3, CString(襍tring 3 *) + changetime);

   pDoc->SetModifiedFlag();

   Invalidate();

}

剩下的工作就是編寫文檔類的Serialize( )函數,這個函數將m_message數據對象存儲到磁盤中。如程序清單8所示:

程序清單8   串行化數據對象

void CMultiStringDoc::Serialize(CArchive& ar)

{

    m_messages.Serialize(ar);

        if (ar.IsStoring())

        {

        }

        else

        {

        }

}

讀者可以看出,在串行化m_messages數據對象后,在文檔類的Serialize( )函數中已經沒什么需要做的了。請注意對m_messages的調用。Serialize( )把檔案對象作為唯一的參量進行了傳遞?,F在創建MultiString并測試它,它將會完成所期望的功能。

直接讀寫文件

盡管使用MFC內建的串行化功能是保存和加載數據的便捷方式,但是某些時候需要對文件處理過程有更多的控制權。例如,可能需要對用戶的文件進行非連續的操作,某些需要I/O流、而Serialize( )函數和它相關的CArchive對象又不能進行處理的操作。在這種情況下,你可以幾乎與在非Windows程序中完全一樣來處理文件,直接對文件進行創建、讀取和寫入。即使需要深入到這個水平來進行文件處理,MFC也提供了幫助,尤其是可以使用CFile類和其派生類來直接處理文件。

CFile類

MFC的CFile類封裝了所有在處理任意類型的文件時所需的函數。無論是希望進行通常的順序的數據保存和加載還是希望構造一個隨機訪問文件,CFile都將支持。使用CFi1e類和老式的C方式的文件處理非常相似,但該類隱藏了一些繁雜工作的細節以使得此項工作快捷而又方便。例如,僅需一行代碼就可以創建一個文件用作讀取。表1列出了CFile類的成員函數和對它們的說明。

表1  CFile類的成員函數

  函    數                說      明

Constructor    創建CFile對象。如果傳遞的是一個文件名,就打開該文件

Destructor    釋放超出范圍的CFile對象。如果文件已打開,則關閉它

Abort( )        立即關閉文件而不管是否出錯

Close( )        關閉文件

Duplicate( )    創建一個可復制的文件對象

Flush( )        清除流中數據

GetFileName( )    獲取文件名

GetFilePath( )    獲取文件的全路徑

GetFileTitle( )    獲取文件標題(不帶擴展名的文件名)

GetLength( )    獲取文件長度

GetPosition( )    獲取文件當前的位置

GetStatus( )    獲取文件的狀態

LockRange( )    鎖定文件的一部分

Open( ) 打開文件

Read( ) 從文件中讀取數據

Remove( )    刪除文件

Rename( )    重命名文件

Seek( )          設置文件中位置

SeekToBegin( )    設置至文件頭的位置

SeekToEnd( )    設置至文件尾的位置

SetFilePath( )    設置文件路徑

SetLength( )    設置文件長度

SetStatus( )    設置文件狀態

UnlockRange( )    對文件的一部分進行解鎖

Write( )         向文件中寫數據

讀者可以從表中看出,CFile類提供大量的文件處理能力。下面演示了如何調用CFile類的一些成員函數。然而,其他函數的絕大部分也是非常容易使用的。

下面是一個簡單的代碼片段,它創建和打開一個文件,向文件中寫入一個串,然后收集一些關于該文件的信息:

    // Create the file.

    CFile file(襎ESTFILE.TXT*, CFile::modeCreate || CFile::modeWrite);

  

    // Write data to the file.

    CString message(襀ello file!*);

    int length = message.GetLength();

    file.Write((LPCTSTR)message, length);

  

    // Obtain information about the file.

    CString filePath = file.GetFilePath();

    Int fileLength = file.GetLength();

注意,在給Constructor函數(其參數是文件名和打開狀態標志)傳遞文件名時,并不需要顯式地打開文件。一次可以使用多個狀態標志,可以簡單地將它們“或”在一起。這些狀態標志描述了如何打開文件、何種操作是有效的。它們作為CFile類的一部分來定義,其含義如下表:

表2  文件狀態標志

    標    記              說      明

CFile::modeCreate    創建新的文件或把已存在的文件長度截為0

CFile::modeNoInherit 不允許子過程繼承

CFile::modeNoTruncate    創建文件時,如果文件已經存在則不對文件截斷

CFile::modeRead  只允許讀操作

CFile::modeReadWrite 允許讀和寫操作

CFile::modeWrite 只允許寫操作

CFile::shareCompat   允許其他過程打開文件

CFile::shareDenyNone 允許其他過程對文件進行讀寫

CFile::shareDenyRead 不允許其他過程對文檔進行讀操作

CFile::shareDenyWrite    不允許其他過程對文檔進行寫操作

CFile::shareExclusive    不允許其他過程訪問

CFile::typeBinary    設置文件為二進制模式

CFile::typeText  設置文件為文本模式

CFile::Write( )需要一個指向存放數據的緩沖區的指針和要寫的字節數。注意在調用Write ( )時,LPCTSTR是強制轉換運算符。它是由CString類定義的,它提取類中的字符串。

此外,這里沒有調用Close( ),當文件超出范圍之外時,CFile的析構函數自動關閉文件。

從文件中讀并不比向文件中寫更難:

    // Open the file.

    CFile file(襎ESTFILE.TXT*, CFile::modeRead);

    // Read data from the file.

    char s[81];

    int bytesRead = file.Read(s, 80);

    s[bytesRead] = 0;

    CString message = s;

當帶著CFile::modeRead狀態標記打開文件時,這種方式打開的文件僅用于讀操作。其后,程序創建一個字符緩沖區并調用文件對象的Read( )成員函數來向緩沖區讀入數據。Read( )函數的兩個參量為緩沖區的地址和讀入字節的個數。此函數返回實際讀入的字節個數,在該例中它幾乎總是小于80。使用所讀取的字節個數,程序可在字符數據的末尾加0來創建一個標準的C類型字符串,該字符串可用于設置CString變量。

上面的代碼片段用了個比較難的文件名。要想讓你的用戶提供文件名,在聯機幫助中查看MFC類CFileDialog,你可以很容易增加這個功能。要給程序添加一些特色是非常簡單的。

創建自己的CArchive對象

盡管可以使用CFile對象處理文件,但可以更進一步并且創建自己的CArchive對象,就像在Serialize( )函數中使用的CArchive對象一樣,讀者完全可以使用自己創建的CArchive對象。這使得讀者可以利用為其他對象編寫的Serialize( )函數,向它們傳遞對自己的檔案對象的引用。

為創建一個檔案文件,需創建一個CFile對象并將其傳遞給CArchive的構造函數。例如,如果想將對象通過檔案文件寫入到一文件中去,可創建如下的檔案文件:

CFile file("FILENAME.EXT", CFile::modeWrite);

CArchive ar(&file, CArchive::store);

在創建檔案文件對象后,可以如MFC所提供的檔案文件對象一樣使用該檔案文件。由于采用CArchive::store標記進行檔案文件對象的創建,對IsStoring( )的調用返回TRUE,而且把對象轉儲到檔案文件的代碼開始執行。當完成檔案文件對象后,可按如下方式關閉檔案文件:

ar.Close( );

file.Close( );

在讀者已經完成它們后,如果對象超出應用范圍,可以安全地忽略對Close( )的調用,這是因為CArchive和CFile已經在析構函數中調用了Close( )。

理解注冊

在Windows編程的早期,應用程序在初始化文件(.ini文件)中保存設置和選項。使用巨大的WIN.INI文件或無數的私有INI文件的日子已經過去了—當一個應用程序希望存儲私有信息時,它使用集中的系統“注冊”。而且,盡管“注冊”很容易使得進程間共享信息,但對于程序員而言,則使情況更加令人迷惑。下面,將揭開“注冊”的神秘面紗和學習如何在你的應用程序中控制它。

注冊是如何設置的

不同于可以用任何文本編輯器進行編輯的純文本文件的INI文件,“注冊(Registry)”包含的二進制和AscII信息只能用Registry Editor進行編輯,或者是用為管理Registry專門創建的特殊API函數調用來進行編輯。如果讀者曾經使用過Registry Editor瀏覽系統的注冊,就會知道它包含了大量的組織成樹形結構的信息。首次運行Registry Editor時的注冊,可以在Windows文件夾中找到稱為REGEDIT.EXE的Registry Editor,或者可以在Start菜單的Run命令中鍵入regedit然后單擊OK。

在左邊的窗口中列出了Registry的預定義鍵。在鍵旁的加號表示可以打開此鍵來查看更多與之相關的信息。鍵可以有子鍵,子鍵可以有自己的子鍵。一個鍵或子鍵可以有也可以沒有與之相關聯的一個值。如果在層次中探尋得比較深入的活,可以看見在右邊窗口中的一列數值。要想親眼看到這些值,可以從HKEY_CURRENT_USER到Control Panel到Appearance到Schemes進行瀏覽,可以看到在系統上所安裝的桌面系統。

預定義的鍵

為了知道在Registry中所存儲的的東西在哪里,就需要知道預定義鍵和它們所代表的意思。

* HKEY_CLASSES_ROOT

* HKEY_CURRENT_USER

* HKEY_LOCAL_MACHINE

* KEY_USERS

* KEY_CURRENT_CONFIG

* KEY_DYN_DATA

HKEY_CLASSES_ROOT鍵保存了文檔類型和屬性,以及有關安裝在機器上的各種應用程序的分類信息。例如,如果在系統中單擊此鍵,可能可以找到文件擴展名為.DOC的一個條目,在其下可以發現能處理此類文檔的應用程序的條目。

HKEY_CURRENT_USER鍵包含當前用戶的所有系統設置,包括配色方案、打印機和程序組。HKEY_LOCAL_MACHINE鍵保存了計算機的狀態信息,KEY_USERS鍵組織了系統中的每一個用戶的信息及系統的缺省設置,KEY_CURRENT_CONFIG鍵保存了硬件配置信息。KEY_DYN_DATA鍵保存了有關動態注冊的數據信息,這些數據是會頻繁變動的。(在讀者的系統中,不總是可以看這個鍵。)

在MFC應用程序中使用“注冊”

現在讀者了解了一些關于注冊的知識,但完整的解釋如何訪問和使用注冊需要一本書來進行闡述。正如讀者可以想象的那樣,Win32 API有許多操作注冊的函數。但是,如果想使用這些函數,必須有把握!無效的注冊設置可能破壞系統,使得它不能啟動,也可能會使你不得不重新安裝Windows。然而,通過MFC應用程序可以很容易的使用注冊來存儲讀者的應用程序不同部分所需的信息。為了盡可能簡化,MFC提供了帶有SetRegistrykey ( )成員函數的CWinApp類,它在注冊中創建(或打開)一個應用程序的按鍵條目,所需做的僅是提供一個鍵名(通常是一個公司名)給函數使用,如下所示:

SetRegistryKey("MyCoolCompany");

應該在應用程序類的InitInstance( )成員函數中調用SetRegistrykey( ),該成員函數將在程序啟動時立即調用。

在調用過SetRegistrykey( )之后,讀者的應用程序可以通過調用兩個函數中的一個來創建一個子鍵及其所需數值,WriteProfileString( )函數往注冊中增加一個字符串值,WriteProfileInt( )函數注冊中增加一個整數值。要想從注冊取得數據,可以使用GetProfileString( )和GetProfileInt( )函數(也可以使用RegSetValueEx( ) 和RegQueryValueEx( )來設置和檢索注冊中的值)。

注意   當第一次寫入時,WriteProfileString( )、WriteProfileInt( )以及GetProfileString( )和GetProfileInt( )函數向(和從)INI文件中進行數據傳遞。單獨使用時,它們仍然起作用。但是,當首先調用SetRegistrykey( )時,MFC重新調整這些函數對注冊的訪問,這樣使用注冊成了一個簡單的過程。

簡單的應用程序

讀者已經建立了一個使用注冊的應用程序。這兒有一段從CMultiStringApp :: InitInstance( )中摘錄的代碼,它是由AppWizard生成的,它同樣也存在于CFileDemoApp: : InitInstance( )之中。

// Change the registry key under which our settings are stored.

// You should modify this string to be something appropriate

// such as the name of your company or organization.

SetRegistryKey(_T(襆ocal AppWizard-Generated Applications*));

LoadStdProfileSettings();  // Load standard INI file options (including MRU)

MRU代表Most Recently Used(最近使用),指的是用應用程序打開文件后出現在File菜單中的文件列表。Registry Editor顯示保存此信息的鍵:HKEY_CURRENT_USER\Software\Local AppWizard-Generated Applications\MultiString\Recent File List。在前臺,MultiString的File菜單在MRU列表中顯示一個條目。

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97