首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC 檔案類體系全解析:CFile、CStdioFile、CMemFile 與 CFileFind 核心實現

     編輯:關於VC++

前言

MFC 提供了以 CFile 為根基的檔案操作類別體系,封裝 Win32 API、C 執行期庫、記憶體映射等多種底層機制,並透過多型實現統一介面。本文將系統剖析 CFile 核心結構、三大衍生類實現細節、以及 CFileFind 搜尋機制。

一、CFile 核心架構

列舉型別定義

enum OpenFlags {
    // 存取模式 (bit 0-1)
    modeRead = 0x0000, modeWrite = 0x0001, modeReadWrite = 0x0002,
    // 共享模式 (bit 4-6)
    shareCompat = 0x0000, shareExclusive = 0x0010, shareDenyWrite = 0x0020,
    shareDenyRead = 0x0030, shareDenyNone = 0x0040,
    // 繼承/建立/格式 (bit 7, 12-15)
    modeNoInherit = 0x0080, modeCreate = 0x1000, modeNoTruncate = 0x2000,
    typeText = 0x4000, typeBinary = 0x8000
};

enum Attribute { normal=0x00, readOnly=0x01, hidden=0x02, system=0x04,
                 volume=0x08, directory=0x10, archive=0x20 };

enum SeekPosition { begin=0, current=1, end=2 };
enum { hFileNull = (UINT)-1 };

關鍵資料成員

public:
    UINT m_hFile;          // Win32 檔案控制代碼 (HANDLE)
    operator HFILE() { return (HFILE)m_hFile; } // 隱含轉換
protected:
    BOOL m_bCloseOnDelete; // 解構時是否關閉檔案
    CString m_strFileName; // 檔案路徑

虛擬/靜態函式對應 Win32 API 表

分類CFile 函式Win32 API備註
建立/開啟/關閉OpenCreateFile虛擬,核心進入點
CloseCloseHandle虛擬
AbortCloseHandle虛擬,異常安全關閉
DuplicateDuplicateHandle虛擬,複製控制代碼
讀寫ReadReadFile虛擬
WriteWriteFile虛擬
ReadHuge/WriteHugeReadFile/WriteFile靜態,16位相容
FlushFlushFileBuffers虛擬
定位/長度SeekSetFilePointer虛擬
SeekToBegin/SeekToEndSeek(0,begin/end)內聯
GetLengthSeek(0,end)虛擬
SetLengthSetEndOfFile虛擬
GetPositionSetFilePointer(0,current)虛擬
鎖定LockRangeLockFile虛擬
UnlockRangeUnlockFile虛擬
狀態GetStatus(CFileStatus&)GetFileTime/Size/Attributes虛擬
GetStatus(LPCTSTR, CFileStatus&)FindFirstFile靜態,不開啟檔案
SetStatusSetFileTime/Attributes靜態
重命名/刪除RenameMoveFile靜態
RemoveDeleteFile靜態

Open 核心實現:OpenFlags → CreateFile 參數映射

BOOL CFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError)
{
    // 1. 存取權限映射
    DWORD dwAccess = 0;
    switch (nOpenFlags & 3) {
        case modeRead: dwAccess = GENERIC_READ; break;
        case modeWrite: dwAccess = GENERIC_WRITE; break;
        case modeReadWrite: dwAccess = GENERIC_READ | GENERIC_WRITE; break;
    }

    // 2. 共享模式映射
    DWORD dwShare = 0;
    switch (nOpenFlags & 0x70) {
        case shareExclusive: dwShare = 0; break;
        case shareDenyWrite: dwShare = FILE_SHARE_READ; break;
        case shareDenyRead: dwShare = FILE_SHARE_WRITE; break;
        case shareDenyNone: dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
    }

    // 3. 安全屬性:繼承性
    SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, (nOpenFlags & modeNoInherit) ? FALSE : TRUE };

    // 4. 建立/開啟 disposition
    DWORD dwCreate = OPEN_EXISTING;
    if (nOpenFlags & modeCreate) {
        dwCreate = (nOpenFlags & modeNoTruncate) ? OPEN_ALWAYS : CREATE_ALWAYS;
    }

    // 5. 呼叫 CreateFile
    HANDLE hFile = ::CreateFile(lpszFileName, dwAccess, dwShare, &sa,
                                dwCreate, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) { /* 填入 pError */ return FALSE; }

    m_hFile = (UINT)hFile;
    m_bCloseOnDelete = TRUE;
    m_strFileName = lpszFileName;
    return TRUE;
}

二、CStdioFile:文字檔專用,封裝 C Runtime

class CStdioFile : public CFile {
    FILE* m_pStream; // C Runtime FILE*

    // Open 時:_open_osfhandle(m_hFile, ...) → _fdopen → m_pStream
    // Read/Write:_fread_nolock / _fwrite_nolock (避免鎖開銷)
    // 新增:ReadString / WriteString (逐行處理)
};

關鍵差異:

  • 使用 _fread_nolock/_fwrite_nolock 提升單執行緒效能。
  • 支援 typeText 模式:自動處理 \n ↔ \r\n 轉換。
  • 不支援:Duplicate、LockRange、UnlockRange、Seek 精確控制(文字模式下位元組位移不可預測)。

三、CMemFile:記憶體即檔案

class CMemFile : public CFile {
    BYTE* m_lpBuffer;   // 記憶體緩衝區
    UINT m_nPosition;   // 當前位置
    UINT m_nBufferSize; // 目前分配大小
    UINT m_nGrowBytes;  // 成長增量

    // 無 Open/Close,改用 Attach/Detach 管理外部緩衝區
    // Alloc/Free 虛擬函式可覆寫自訂分配策略 (預設 new/delete)
    // Read/Write:memcpy + 自動成長 (寫入時)
};

應用場景:

  • 剪貼簿資料傳輸(COleDataSource 內部使用)。
  • 網路封包組裝/解析(CSocketFile 可配合)。
  • 單元測試模擬檔案 I/O,零磁碟存取。

四、CFileFind:Win32 搜尋 API 的物件導向封裝

class CFileFind : public CObject {
    HANDLE m_hContext;           // FindFirstFile 返回的搜尋控制代碼
    WIN32_FIND_DATA m_findData;  // 當前檔案資訊
    BOOL m_bWorking;             // 是否有效

    // 核心函式
    BOOL FindFile(LPCTSTR pstrName);  // FindFirstFile
    BOOL FindNextFile();              // FindNextFile
    // 豐富的屬性存取:GetLength, GetCreationTime, IsDirectory, IsDots, ...
};

CFileStatus 結構

struct CFileStatus {
    CTime m_ctime;   // 建立時間
    CTime m_mtime;   // 修改時間
    CTime m_atime;   // 存取時間
    LONG m_size;     // 檔案大小
    BYTE m_attribute; // 屬性位元組
    TCHAR m_szFullName[_MAX_PATH]; // 完整路徑
};

CFile::GetStatus(CFileStatus&) 填充此結構,CFileFind 直接對應 WIN32_FIND_DATA 欄位。

五、實務指南與常見坑點

  • 例外處理:CFile 方法失敗拋出 CFileException*,需用 TRY/CATCH 或 try/catch(CFileException*) 捕獲,記得 DELETE_EXCEPTION。
  • 大檔案支援:32 位 MFC 中 CFile::GetLength 返回 ULONGLONG (64 位),Seek 參數為 LONGLONG,需定義 _WIN32_WINNT >= 0x0500。
  • 文字/二進位模式:CStdioFile 預設文字模式會轉換換行符,二進位資料務必指定 typeBinary。
  • 共享違規:shareDenyWrite 會阻擋其他程式寫入(如記事本另存),日誌檔建議用 shareDenyNone。
  • 路徑長度:_MAX_PATH (260) 限制,長路徑需加 \\?\ 前綴並使用 Unicode API,MFC 8.0+ 部分支援。

總結

MFC 檔案類別體系展現了經典的「策略模式」與「適配器模式」:

  1. CFile 定義統一介面,適配 Win32 CreateFile 族。
  2. CStdioFile 適配 C Runtime FILE*,專攻文字處理。
  3. CMemFile 適配記憶體緩衝區,實現零拷貝高速 I/O。
  4. CFileFind 封裝 FindFirstFile/Next,提供型別安全的搜尋介面。

選擇正確的類別,能在效能、相容性、開發效率間取得最佳平衡。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved