電腦室如何管理自身所存放著的大量的資訊的呢。windows的磁碟管理程式為我們提供了一套嚴密而又高效的資訊組織形式--硬碟上的資訊是以檔案的形式被管理的。 面向儲存的檔案技術 什麼是檔案。電腦中,一篇文章、一幅圖片、一個程式等都是以檔案的形式儲存在磁碟上的,每個檔案都有一個檔案名稱。電腦就是對檔案按名存取的。檔案名稱的格式如下:主檔案名.副檔名。 為什麼要在程式中使用檔案。 通常,程式中的資料在程式運行結束之後,就會從記憶體中清除,再次運行程式時不會自動出現。在編製程式的過程中不可避免的會遇到將某些資料永久儲存的問題,當程式關閉後,依然可以使用這些資料,這時就需要進行檔案操作。 檔案類型 Visual C++處理的檔案通常分為兩種: 文字檔:只可被任意文字編輯器讀取ASCII文本。 二進位檔案: 指對包含任意格式或無格式資料的檔案的統稱。 這裡只介紹文字檔的讀寫,INI檔案也屬於文字檔的範疇,且INI檔案的結構和用途與普通的文字檔不同,所以會單獨介紹。 第一部分:文字檔 文字檔的讀寫 認識CFile類;認識文字檔;能夠正確靈活應用文字檔存取資訊;避免文字檔讀寫的常見誤區。 CFile是MFC的檔案操作基本類,它直接支援無緩衝的二進位磁碟I/O操作,並通過其衍生類別支援文字檔、記憶體檔案和socket檔案。 客戶操作記錄執行個體功能預覽及關鍵知識點 許多系統,出於安全或其他原因,常常要求隨時對鍵盤進行監控,利用Hook(鉤子)技術編寫的應用程式能夠很好地達到這個目的。本軟體就製作了一個客戶操作記錄軟體,即在軟體運行過程中,使用者在鍵盤上的按鍵動作會被記錄下來,這樣對維護軟體的正常運行非常有利。 只要啟動客戶操作記錄軟體後,不管輸入焦點是否在本軟體上,按鍵都會被記錄下來。我們需要的是鍵盤的系統監控,只要本軟體在運行,無論當前電腦在做什麼,都能監測到使用者按鍵的行為並做出反應,這就要用到Hook技術。 Hook技術在很多特殊軟體中廣泛應用,如,金山詞霸的“取詞”功能,就用到了Hook計技術。 鉤子的本質是一段用以處理系統訊息的程式,通過系統調用,將其掛入系統。鉤子的種類很多,每種鉤子可以截獲並處理相應的訊息,每當特定的訊息發出,在到達目的視窗之前,鉤子程式先行截獲該訊息、得到對此訊息的控制權。此時在鉤子函數中就可以對截獲的訊息進行加工處理,甚至可以強制結束訊息的傳遞。 從鉤子的本質來看,可以優先截獲作業系統的各種訊息進行處理,所以它幾乎無所不能,因為windows的應用程式都是基於訊息驅動的,應用程式的操作都依賴於它所得到的訊息的類型及內容。 如果Hook過程在應用程式中實現,若應用程式不是當前視窗時,該Hook就補齊作用;如果Hook在DLL中實現,程式在運行中動態調用它,它能即時對系統進行監控。根據需要,我們採用的是在DLL中實現Hook的方式。 (應用程式exe? 和DLL的區別所在) 文字檔儲存管理 字元被電腦處理時都是以二進位代碼的形式出現的,即一個字元對應一個8位位元,這種二進位碼的集合就是所謂的ASCII碼。 基本的ASCII碼有128個,最高位都是0,對應的十進位數是0-127。鍵盤上的字元,如英文字母、數字和一些常用符號,使用基本ASCII部分。如數字“0”的ASCII碼用位元表示就是00110000(即十進位數48)。 擴充的ASCII碼有128個,最高位是1,對應的十進位數是128-255。一些定位字元和其他符號使用擴充的ASCII碼部分。 為解決漢字的儲存和顯示問題,我國制定了國際GB2312。據此規定,一個漢字由2個擴充的ASCII碼組成,這種高位為1的雙位元組漢字編碼就是漢字的機內碼,簡稱為內碼。例如,漢字“學”的機內碼用位元表示就是11010001 10100111(即十進位數206 和167 ),用十進位表示就是53671(206*256+167)。對於字元,文字檔儲存的是它的ASCII碼,對於漢字,文字檔儲存的是它的內碼,即兩位ASCII碼,如字串“0學0”,在文字檔中儲存的內容是00110000 11010001 10100111 00110000 正確的文字檔讀寫過程 1.定義檔案變數;2.開啟指定的檔案;3.向從文字檔中寫入資訊;4.從文字檔中讀取資訊;5.關閉檔案 1、定義檔案變數 定義檔案變數格式:CStdioFile 檔案變數; 例如,定義一個名稱為f1的檔案變數,語句如下:CStdioFile f1; 2、開啟指定檔案 可以直接通過CStdioFile的建構函式來開啟磁碟檔案,同時可以用標誌位指定開啟檔案(唯讀、唯寫、讀寫等): CStdioFile(LPCTSTR lpszFileName,UINT nOpenFlags); 其中,lpszFileName表示要開啟的檔案名稱,可以是相對路徑或絕對路徑 nOpenFlags設定檔案開啟檔案標誌位,可以指定用“|”串連多個標誌位。下面是常用的開啟標誌: CFile::typeText:以文字檔的形式開啟檔案 CFile::typeBinary:以二進位檔案的形式開啟檔案 CFile::modeCreate:如果指定檔案名稱的檔案不存在,則建立檔案;如果檔案存在並且沒有設定CFile::modeNoTruncate標誌,則清空檔案。 CFile::modeNoTruncate:如果檔案存在,不把它的長度刪除為0(即不清空檔案中的資料)。 CFile::modeRead:以唯讀方式開啟檔案 CFile::modeReadWrite:以可讀可寫方式開啟檔案 CFile::modeWrite:以唯寫方式開啟檔案 CFile::shareDenyNone:檔案開啟後,不禁止其他進程對檔案的讀寫操作 CFile::shareExclusive:檔案開啟後,禁止其他進程對檔案的讀寫操作 CFile::shareDenyRead:檔案開啟後,禁止其他進程對檔案的讀操作 CFile::shareDenyWrite:檔案開啟後,禁止其他進程對檔案的寫操作 此外,可以不在建構函式中開啟檔案,而僅僅調用空的建構函式CStidoFile(),然後用CStdioFile::Open()開啟檔案。Open函數的前兩個參數和非空建構函式的參數相同,其聲明如下: BOOL Open(LPCTSTR lpszFileName,UINT nOpenFlags,CFileException* pError=NULL); 第3個參數與開啟失敗時的異常處理有關。 執行個體1:以唯讀方式開啟一個檔案 步驟: 使用AppWizard建立一個對話方塊應用程式,刪除其自動產生的所有控制項,添加一個Button控制項。雙擊控制項,在相應的函數裡添加代碼: char * pszFileName="C://myfile.txt";CStdioFile myFile;CFileException fileException;if(!myFile.Open(pszFileName,CFile::modeCreate|CFile::typeText|CFile::modeRead),&fileException){ TRACE("Can't open file %s, error = %u/n",pszFileName,fileException.m_cause);} 運行結果:如果C:/下沒有myfile.txt檔案,則新產生該檔案。 3.向從文字檔中寫入資訊 CStdioFile提供了函數WriteString來向文字檔中寫入文本,WriteString函數的格式如下: void WriteString(LPCTSTR lpsz); WriteString的參數lpsz是一個以”/0”字元結束的字串,要把這個字串的內容寫入檔案。 提示:使用WriteString函數時,如果希望每執行一次WriteString,文字檔中的內容就會自動換行一次,那麼就需要在需要換行的地方輸出“/n”: myFile.WriteString(“第1行/n”); 執行個體2:向檔案中寫入文本 建立MFC基於對話方塊的程式,刪除自動添加的所有控制項,添加一個“確定”按鈕,雙擊按鈕,按預設添加事件函數,雙擊按鈕,在相應的函數處添加如下代碼: char* pszFileName="C://myfile.txt";CStdioFile myFile;CFileException fileException;if(myFile.Open(pszFileName,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException){ myFile.WriteString("第1行/n"); CString strOrder; strOrder.Format("%d,%.3f",66,88.88); myFile.WriteString(strOrder);}else{ TRACE("Can't open file %s,error=%u/n",pszFileName,fileException.m_cause);} 程式運行結果:C:/myfile.txt檔案中內容如下: 第1行 66,88.880 4.從文字檔中讀取資訊 CStidoFile提供了函數ReadString來讀取文本,ReadString有兩種形式,一種為: virtual LPTSTR ReadString(LPTSTR lpsz, UINIT nMax); ReadString函數的參數如下: lpsz :是使用者提供的一個指向字串的指標,它用來接受從檔案讀出的文本,以”/0”結束。 nMax是本次所允許讀入的文本字元個數,不計“/0”字元,也就是說最多能讀入nMax-1個文本字元。 ReadString的傳回值是一個LPTSTR類型的指標,它指向從檔案讀出的文本字串,如果到達檔案尾,則返回NULL。 ReadString的另一種形式為: BOOL ReadString(CString& rString); 參數rString用來容納從檔案讀出的文本。 CString版本忽略斷行符號分行符號,傳回值是一個布爾值。如果傳回值為FALSE,表示因到達檔案尾而沒有讀到任何字元。 提示:每執行一次ReadString,就會自動從文字檔中讀取一行資料,同時檔案操作指標會自動跳轉到下一行。 執行個體3:從檔案中讀取文本資訊 步驟:建立基於對話方塊的MFC程式,刪除所有自動添加的控制項,添加按鈕控制項,為按鈕添加事件,並在相應的函數處,添加如下代碼: char* pszFileName="C://myfile.txt";CStdioFile myFile;CFileException fileException;if(myFile.Open(pszFileName,CFile::typeText|CFile::modeReadWrite),&fileException){ myFile.SeekToBegin(); CString str1; myFile.ReadString(str1); CString str2; myFile.ReadString(str2); AfxMessageBox(str1+str2);}else{ TRACE("Can't open file %s,error=%u/n",pszFileName,fileException.m_cause);}myFile.Close(); 程式運行結果:為程式F9設定斷點,然後F5逐步執行,結果如下: 5.關閉檔案 對檔案的操作完成後,使用CloseFile關閉檔案。 函數CStdioFile::Close關閉一個檔案,一般一個檔案使用完畢就應該關閉它: myFile.Close(); 錯誤的文字檔讀寫過程 在讀寫文字檔的時候,最常見的錯誤是---操作檔案不存在。這種錯誤產生的典型原因有: 1.路徑錯誤 char * pszFileName="C://Windows//MyFile.txt";CStdioFile myFile;CFileException fileException;if(!myFile.Open(pszFileName,CFile::modeCreate|CFile::typeText|CFile::modeReadWrite),&fileException){ //檔案作業碼}else{ TRACE("Can't open file %s, error = %u/n",pszFileName,fileException.m_cause);}myFile.Close(); 由於將檔案變數與一個絕對路徑的檔案名稱關聯,而程式的資料通常儲存在相對路徑下,所以一旦相對路徑和相對路徑不一致時,就會出錯。 舉例而言,上一段程式本意是想從windows的安裝目錄下面的MyTextFile.txt檔案中讀取一行資料,但是如果作業系統安裝的路徑不是C:/Windwos,而是C:/Winnt,那麼這段程式就會出錯。 解決方案是在程式中使用相對路徑,改正後的程式如下: //擷取windows路徑LPTSTR lpBuffer=new char[MAX_PATH];::GetWindowsDirectory(lpBuffer,MAX_PATH);strcat(lpBuffer,"//MyFile.txt");CStdioFile myFile;CFileException fileException;if(myFile.Open(lpBuffer,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException){ //檔案作業碼}else{ TRACE("Can't open file %s, error = %u/n",pszFileName,fileException.m_cause);}CString strFileTitle="MyFile.txt";CStdioFile myFile;CFileException fileException;if(myFile.Open(strFileTitle,CFile::typeText|CFile::modeReadWrite),&fileException){ //檔案作業碼 myFile.WriteString("測試。");}else{ TRACE("Can't open file %s, error = %u/n",pszFileName,fileException.m_cause);}myFile.Close(); 2.操作檔案不存在 如果應用程式所有路徑下面不存在MyFile.txt檔案,那麼在WriteString寫入資訊時就會出錯。 解決辦法就是在程式中開啟檔案前要檢查是否存在此檔案。如下程式: CString strFileTitle="MyFile.txt";CFileFind finder;if(finder.FindFile(strFileTitle)){ CStdioFile myFile; CFileException fileException; if(myFile.Open(lpBuffer,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException) { //檔案作業碼 } else { TRACE("Can't open file %s, error = %u/n",pszFileName,fileException.m_cause); }}else{ TRACE("Can't find file %s/n",strFileTitle);}myFile.Close(); 3.超越檔案許可權進行讀寫操作 在開啟檔案的過程中,通過參數指定了檔案的讀寫權限,如果讀寫的操作沒有和相應的許可權對應,就會出現錯誤。 下面的程式就是典型的忽略了檔案操作許可權的例子: CString strFileTitle="MyFile.txt";CStdioFile myFile;CFileException fileException;if(myFile.Open(strFileTitle,CFile::typeText|CFile::modeCreate|CFile::NoTruncate|CFile::modeRead),&fileException){ //檔案作業碼 myFile.WriteString("測試!");}else{ TRACE("Can't open file %s,error=%u/n",strFileTitle,fileException.m_cause);}myFile.Close(); 支招兒: 1.準確定位檔案的路徑 操作檔案的過程中,經常需要將文字檔放在程式自身的目錄中,但是如果僅僅在程式中使用不指定任何路徑資訊的相對路徑,如: myFile.Open("MyFile.txt",CFile::modeCreate|CFile::typeText|CFile::modeReadWrite); 那麼就有可能出現不能正確定位的情況,準確定位檔案位置的方法是獲得可執行程式自身的絕對路徑,如: TCHAR FilePath[MAX_PATH];GetModuleFileName(NULL,FilePath,MAX_PATH);(_tcstchr(FilePath,'//'))[1]=0;lstrcat(FilePath,_T("MyFile.txt"));CStdioFile myFile;CFileException fileException;if(myFile.Open(FilePath,CFile::modeCreate|CFile::typeText|CFile::modeReadWrite),&fileException){ //檔案作業碼}else{ TRACE("Can't open file %s,error=%u/n",FilePath,fileException.m_cause);}myFile.Close(); 2.讀文字檔指定的一行,並得到文字檔的總行數。 讀文字檔指定的一行,並得到文字檔的總行數 要統計文字檔的總行數,可以從頭逐行讀,直到檔案尾,程式: CStdioFile myFile;CFileException fileException;if(myFile.Open("MyFile.txt",CFile::modeCreate|CFile::modeNoTruncate|CFile::typeText|CFile::modeReadWrite),&fileException){ CString strContent; int order=1; while(myFile.ReadString(strContent)) { if(2==order) { AfxMessageBox(strContent); } order=order+1; }}else{ TRACE("Can't open file");}myFile.Close(); 執行個體示範檔案操作過程 客戶操作記錄執行個體 本軟體分為兩個部分,一部分是DLL模組,裡面利用Hook技術完成鍵盤監控和寫入檔案的功能;另一部分是介面部分,調用DLL啟動和停止客戶操作記錄功能。 第1步:建立MFC DLL項目 第2步:建立TestHook.h檔案 第3步:加入全域共用資料變數 第4步:儲存DLL執行個體控制代碼 第5步:類CKeyboradHook的成員函數 第6步:建立鉤子可執行程式 第1步:建立MFC DLL項目 |