MFC 提供了以 CFile 為根基的檔案操作類別體系,封裝 Win32 API、C 執行期庫、記憶體映射等多種底層機制,並透過多型實現統一介面。本文將系統剖析 CFile 核心結構、三大衍生類實現細節、以及 CFileFind 搜尋機制。
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; // 檔案路徑
| 分類 | CFile 函式 | Win32 API | 備註 |
|---|---|---|---|
| 建立/開啟/關閉 | Open | CreateFile | 虛擬,核心進入點 |
| Close | CloseHandle | 虛擬 | |
| Abort | CloseHandle | 虛擬,異常安全關閉 | |
| Duplicate | DuplicateHandle | 虛擬,複製控制代碼 | |
| 讀寫 | Read | ReadFile | 虛擬 |
| Write | WriteFile | 虛擬 | |
| ReadHuge/WriteHuge | ReadFile/WriteFile | 靜態,16位相容 | |
| Flush | FlushFileBuffers | 虛擬 | |
| 定位/長度 | Seek | SetFilePointer | 虛擬 |
| SeekToBegin/SeekToEnd | Seek(0,begin/end) | 內聯 | |
| GetLength | Seek(0,end) | 虛擬 | |
| SetLength | SetEndOfFile | 虛擬 | |
| GetPosition | SetFilePointer(0,current) | 虛擬 | |
| 鎖定 | LockRange | LockFile | 虛擬 |
| UnlockRange | UnlockFile | 虛擬 | |
| 狀態 | GetStatus(CFileStatus&) | GetFileTime/Size/Attributes | 虛擬 |
| GetStatus(LPCTSTR, CFileStatus&) | FindFirstFile | 靜態,不開啟檔案 | |
| SetStatus | SetFileTime/Attributes | 靜態 | |
| 重命名/刪除 | Rename | MoveFile | 靜態 |
| Remove | DeleteFile | 靜態 |
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;
}
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 精確控制(文字模式下位元組位移不可預測)。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 可配合)。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, ...
};
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。CFile::GetLength 返回 ULONGLONG (64 位),Seek 參數為 LONGLONG,需定義 _WIN32_WINNT >= 0x0500。CStdioFile 預設文字模式會轉換換行符,二進位資料務必指定 typeBinary。shareDenyWrite 會阻擋其他程式寫入(如記事本另存),日誌檔建議用 shareDenyNone。_MAX_PATH (260) 限制,長路徑需加 \\?\ 前綴並使用 Unicode API,MFC 8.0+ 部分支援。MFC 檔案類別體系展現了經典的「策略模式」與「適配器模式」:
CFile 定義統一介面,適配 Win32 CreateFile 族。CStdioFile 適配 C Runtime FILE*,專攻文字處理。CMemFile 適配記憶體緩衝區,實現零拷貝高速 I/O。CFileFind 封裝 FindFirstFile/Next,提供型別安全的搜尋介面。選擇正確的類別,能在效能、相容性、開發效率間取得最佳平衡。