傳統檔案 I/O (ReadFile/WriteFile) 需要:
GlobalAlloc/HeapAlloc)痛點:
MMF 解決方案: 讓作業系統將檔案直接映射到進程虛擬位址空間。您只需操作指標 (像操作記憶體陣列),OS 自動處理分頁讀寫、快取、預讀。甚至可作為進程間共享記憶體 (需同步機制)。
CreateFile:開啟來源檔案,取得 hFile。CreateFileMapping:建立檔案映射物件 (hMapFile),指定保護屬性 (如 PAGE_READONLY) 與大小 (0=全檔)。MapViewOfFile:將映射物件的某範圍 (或全部) 映射到位址空間,返回基址指標 pMemory。mov eax, [pMemory], rep movsb, WriteFile(hDest, pMemory, size, ...) 等。UnmapViewOfFile:解除映射視圖。CloseHandle(hMapFile):關閉映射物件代碼。CloseHandle(hFile):關閉檔案代碼。功能:選擇來源檔 -> MMF 映射 -> 選擇目標檔 -> WriteFile 直接寫入映射記憶體指標 -> 完成。全程零 GlobalAlloc/ReadFile。
.data
hMapFile HANDLE 0 ; 映射物件代碼,0 也作為旗標判斷是否已建立
hFileRead HANDLE ? ; 來源檔代碼
hFileWrite HANDLE ? ; 目標檔代碼
pMemory DWORD ? ; 映射視圖基址指標
SizeWritten DWORD ? ; WriteFile 實際寫入位元組數
; ... 視窗建立、選單建立略 ...
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg == WM_COMMAND
mov eax, wParam
.if lParam == 0
.if ax == IDM_OPEN
; --- 開啟檔案並建立 MMF ---
mov ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
.if eax == TRUE
invoke CreateFile, ADDR buffer, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL
mov hFileRead, eax
invoke CreateFileMapping, hFileRead, NULL, PAGE_READONLY, 0, 0, NULL
mov hMapFile, eax
; 設定視窗標題為檔名 (利用 ofn.nFileOffset 取純檔名)
mov eax, OFFSET buffer
movzx edx, ofn.nFileOffset
add eax, edx
invoke SetWindowText, hWnd, eax
invoke EnableMenuItem, hMenu, IDM_OPEN, MF_GRAYED
invoke EnableMenuItem, hMenu, IDM_SAVE, MF_ENABLED
.endif
.elseif ax == IDM_SAVE
; --- 另存新檔:建立目標檔 -> 映射視圖 -> WriteFile ---
mov ofn.Flags, OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetSaveFileName, ADDR ofn
.if eax == TRUE
invoke CreateFile, ADDR buffer, GENERIC_READ or GENERIC_WRITE, \
FILE_SHARE_READ or FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_ARCHIVE, NULL
mov hFileWrite, eax
invoke MapViewOfFile, hMapFile, FILE_MAP_READ, 0, 0, 0
mov pMemory, eax
invoke GetFileSize, hFileRead, NULL
invoke WriteFile, hFileWrite, pMemory, eax, ADDR SizeWritten, NULL
invoke UnmapViewOfFile, pMemory
call CloseMapFile ; 關閉 hMapFile, hFileRead
invoke CloseHandle, hFileWrite
invoke SetWindowText, hWnd, ADDR AppName
invoke EnableMenuItem, hMenu, IDM_OPEN, MF_ENABLED
invoke EnableMenuItem, hMenu, IDM_SAVE, MF_GRAYED
.endif
.endif
.endif
.ELSEIF uMsg == WM_DESTROY
.if hMapFile != 0
call CloseMapFile
.endif
invoke PostQuitMessage, NULL
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
CloseMapFile PROC
invoke CloseHandle, hMapFile
mov hMapFile, 0
invoke CloseHandle, hFileRead
ret
CloseMapFile endp
CreateFileMapping proto hFile:DWORD, \
lpFileMappingAttributes:DWORD, \
flProtect:DWORD, \
dwMaximumSizeHigh:DWORD, \
dwMaximumSizeLow:DWORD, \
lpName:DWORD
flProtect:PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY (複製時寫入,不影響原檔)。必須與 CreateFile 的存取權限相容 (如 GENERIC_READ 不能配 PAGE_READWRITE)。dwMaximumSizeHigh/Low:映射大小。皆為 0 表示映射整個檔案。若指定大於檔案大小,檔案會擴充至該大小。lpName:命名映射物件,供跨進程共享使用 (需 OpenFileMapping 開啟)。本例為單進程使用,傳 NULL。MapViewOfFile proto hFileMappingObject:DWORD, \
dwDesiredAccess:DWORD, \
dwFileOffsetHigh:DWORD, \
dwFileOffsetLow:DWORD, \
dwNumberOfBytesToMap:DWORD
dwDesiredAccess:FILE_MAP_READ, FILE_MAP_WRITE, FILE_MAP_COPY。須與 flProtect 相容。dwFileOffsetHigh/Low:檔案起始偏移 (64-bit)。必須是系統配置粒度倍數 (通常 64KB),否則失敗。映射整檔傳 0,0。dwNumberOfBytesToMap:映射位元組數。0 = 從偏移處映射到檔案結尾。FlushViewOfFile)。; 進程 A (建立者)
invoke CreateFileMapping, INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, "MySharedMem"
invoke MapViewOfFile, eax, FILE_MAP_WRITE, 0, 0, 0
; 寫入資料...
; 需用 Mutex 保護寫入
; 進程 B (開啟者)
invoke OpenFileMapping, FILE_MAP_READ, FALSE, "MySharedMem"
invoke MapViewOfFile, eax, FILE_MAP_READ, 0, 0, 0
; 讀取資料...
警告:共享記憶體必須處理同步 (Mutex, Semaphore, Event),否則會讀到髒資料、導致競爭條件甚至崩潰。