#ifndef MEMFILEMAPHELPER_H #define MEMFILEMAPHELPER_H //文件內存映射類,創建內存映射文件;創建后,可以按照內存操作方式操作文件 /* 文件映射問題 內存映射文件並不是簡單的文件I/O操作,實際用到了Windows的核心編程技術--內存管理。 所以,如果想對內存映射文件有更深刻的認識,必須對Windows操作系統的內存管理機制有清楚的認識, 內存管理的相關知識非常復雜,超出了本文的討論范疇,在此就不再贅述,感興趣的讀者可以參閱其他相關書籍。 下面給出使用內存映射文件的一般方法: 首先要通過CreateFile()函數來創建或打開一個文件內核對象,這個對象標識了磁盤上將要用作內存映射文件的文件。 在用CreateFile()將文件映像在物理存儲器的位置通告給操作系統后,只指定了映像文件的路徑,映像的長度還沒有指定。 為了指定文件映射對象需要多大的物理存儲空間還需要通過CreateFileMapping()函數來創建一個文件映射內核對象以告訴 系統文件的尺寸以及訪問文件的方式。在創建了文件映射對象后,還必須為文件數據保留一個地址空間區域,並把文件數據 作為映射到該區域的物理存儲器進行提交。由MapViewOfFile()函數負責通過系統的管理而將文件映射對象的全部或部分 映射到進程地址空間。此時,對內存映射文件的使用和處理同通常加載到內存中的文件數據的處理方式基本一樣, 在完成了對內存映射文件的使用時,還要通過一系列的操作完成對其的清除和使用過資源的釋放。 這部分相對比較簡單,可以通過UnmapViewOfFile()完成從進程的地址空間撤消文件數據的映像、 通過CloseHandle()關閉前面創建的文件映射對象和文件對象。
*/ #include <Windows.h> #include <WinBase.h> #include <string> #include <iostream> using namespace std; //typedef unsigned char byte; //typedef unsigned long DWORD; //typedef void* HANDLE; class CMemFileMapHelper{ public: enum MemFileType{SEQ_READ=0,SEQ_WRITE,RANDOM_READ,RANDOM_WRITE,SEQ_READ_WRITE,RANDOM_READ_WRITE}; protected: HANDLE m_FileHandler;//原始文件句柄 HANDLE m_FileMemMapHandler;//內存映射文件句柄 unsigned __int64 m_FileSize; unsigned __int64 m_CurOffset; size_t m_FileChunkSize;//文件分割塊大小,當文件太大時,將大文件分割成多個文件塊 size_t m_CurChunkSize;//當前文件塊大小 DWORD m_MapChunkSize;//當前操作系統的分配粒度,內存映射大小 byte* m_BaseAddr;//內存映射文件首地址,m_BaseAddr+n =>訪問當前文件塊第n字節處的位置 bool m_FileMapped;//是否映射 DWORD m_ViewAccess; public: CMemFileMapHelper(){ m_FileMapped = false; m_FileHandler = NULL; m_FileMemMapHandler = NULL; m_BaseAddr = NULL; m_FileSize = 0; m_FileChunkSize = 0; m_CurChunkSize = 0; m_MapChunkSize = 0; m_CurOffset = 0; m_ViewAccess=0; } ~CMemFileMapHelper(){ if(m_FileMapped) ReleaseFileMapping(); } void ShowError(char* errmsg){ cout<<errmsg<<endl; } //將文件加載到內存映射 bool BuildFileMapping(const char* fileName,MemFileType type = SEQ_READ, unsigned __int64 view_size=0){ DWORD err_code; char err_msg[100]; string shared_name = GetLastFileName(fileName); //存取模式//GENERIC_READ | GENERIC_WRITE DWORD access_mode; //共享模式// FILE_SHARE_READ | FILE_SHARE_WRITE DWORD share_mode; /*文件屬性: FILE_FLAG_WRITE_THROUGH 操作系統不得推遲對文件的寫操作 FILE_FLAG_OVERLAPPED 允許對文件進行重疊操作 FILE_FLAG_NO_BUFFERING 禁止對文件進行緩沖處理。文件只能寫入磁盤卷的扇區塊 FILE_FLAG_RANDOM_ACCESS 針對隨機訪問對文件緩沖進行優化 FILE_FLAG_SEQUENTIAL_SCAN 針對連續訪問對文件緩沖進行優化 FILE_FLAG_DELETE_ON_CLOSE 關閉了上一次打開的句柄后,將文件刪除。特別適合臨時文件 */ DWORD mmf_flag; /*打開文件方式: CREATE_NEW 創建文件;如文件存在則會出錯 CREATE_ALWAYS 創建文件,會改寫前一個文件 OPEN_EXISTING 文件必須已經存在。由設備提出要求 OPEN_ALWAYS 如文件不存在則創建它 TRUNCATE_EXISTING 講現有文件縮短為零長度*/ DWORD file_create_mode; /*頁面內存訪問方式: PAGE_EXECUTE 可執行 PAGE_EXECUTE_READ 可讀,可執行 PAGE_EXECUTE_READWRITE 可讀,可寫,可執行 PAGE_EXECUTE_WRITECOPY 可讀,可寫,可執行,以Read-on-write和copy-on-write方式共享 PAGE_NOACCESS 不可訪問 PAGE_READONLY 只讀 並且hFile對應的文件必須以GENERIC_READ形式打開。 PAGE_READWRITE 可讀,可寫 並且hFile對應的文件必須以GENERIC_READ 和 GENERIC_WRITE形式打開。 PAGE_WRITECOPY copy-on-write保護機制 並且hFile對應的文件必須以GENERIC_READ 和 GENERIC_WRITE形式打開。 PAGE_GUARD 保護,如果訪問則異常(不能單獨使用) PAGE_NOCACHE 不進行CPU緩存(不能單獨使用) PAGE_WRITECOMBINE write-combined優化(不能單獨使用) */ DWORD page_access_mode; /*虛擬頁面視圖訪問方式 FILE_MAP_WRITE:一個可讀寫屬性的文件視圖被創建,保護模式為PAGE_READWRITE FILE_MAP_READ :一個只讀屬性的文件視圖被創建,保護模式為PAGE_READWRITE 或 PAGE_READONLY FILE_MAP_ALL_ACCESS:與FILE_MAP_WRITE模式相同 FILE_MAP_COPY:保護模式為PAGE_WRITECOPY時,得到一個視圖文件,當你對視圖文件寫操作時,頁面自動交換,並且你所做的修改不會損壞原始數據資料。 */ //文件映射為一個映像,映像的大小=> size_t view_size switch(type){ case SEQ_READ: { access_mode = GENERIC_READ; share_mode = FILE_SHARE_READ; mmf_flag = FILE_FLAG_SEQUENTIAL_SCAN; file_create_mode = OPEN_EXISTING; page_access_mode = PAGE_READONLY; m_ViewAccess = FILE_MAP_READ; view_size = 0;//將整個文件映射為一個映像 } break; case RANDOM_READ: { access_mode = GENERIC_READ; share_mode = FILE_SHARE_READ; mmf_flag = FILE_FLAG_RANDOM_ACCESS; file_create_mode = OPEN_EXISTING; page_access_mode = PAGE_READONLY; m_ViewAccess = FILE_MAP_READ; view_size = 0; } break; case SEQ_WRITE: { access_mode = GENERIC_READ | GENERIC_WRITE; share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; mmf_flag = FILE_FLAG_WRITE_THROUGH;//FILE_FLAG_SEQUENTIAL_SCAN file_create_mode = CREATE_NEW; page_access_mode = PAGE_READWRITE; m_ViewAccess = FILE_MAP_WRITE; } break; case RANDOM_WRITE: { access_mode = GENERIC_READ | GENERIC_WRITE; share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; mmf_flag = FILE_FLAG_RANDOM_ACCESS; file_create_mode = CREATE_NEW; page_access_mode = PAGE_READWRITE; m_ViewAccess = FILE_MAP_WRITE; } break; case SEQ_READ_WRITE: { access_mode = GENERIC_READ | GENERIC_WRITE; share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; mmf_flag = FILE_FLAG_SEQUENTIAL_SCAN; file_create_mode = OPEN_ALWAYS; page_access_mode = PAGE_READWRITE; m_ViewAccess = FILE_MAP_READ|FILE_MAP_WRITE;//FILE_MAP_ALL_ACCESS } break; case RANDOM_READ_WRITE: { access_mode = GENERIC_READ | GENERIC_WRITE; share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; mmf_flag = FILE_FLAG_RANDOM_ACCESS; file_create_mode = OPEN_ALWAYS; page_access_mode = PAGE_READWRITE; m_ViewAccess = FILE_MAP_READ|FILE_MAP_WRITE;//FILE_MAP_ALL_ACCESS } break; default: return false; } //1.創建文件 /* HANDLE CreateFile( LPCTSTR lpFileName, //指向文件名的指針 DWORD dwDesiredAccess, //訪問模式(寫/讀) DWORD dwShareMode, //共享模式 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全屬性的指針 DWORD dwCreationDisposition, //如何創建 DWORD dwFlagsAndAttributes, //文件屬性 HANDLE hTemplateFile //用於復制文件句柄 ); 返回值 如執行成功,則返回文件句柄。 INVALID_HANDLE_VALUE表示出錯,會設置GetLastError。 即使函數成功,但若文件存在,且指定了CREATE_ALWAYS 或 OPEN_ALWAYS,GetLastError也會設為ERROR_ALREADY_EXISTS */ m_FileHandler = CreateFile(fileName,access_mode,share_mode,NULL,file_create_mode,mmf_flag,NULL); err_code = GetLastError(); switch(err_code){ case INVALID_HANDLE_VALUE: sprintf(err_msg,"文件打開失敗"); ShowError(err_msg); return false; break; case ERROR_ALREADY_EXISTS: if(m_FileHandler == NULL &&(type == SEQ_WRITE || type == RANDOM_WRITE) ){ sprintf(err_msg,"文件已存在"); ShowError(err_msg); return false; } break; } //2.創建文件映射 /* HANDLE CreateFileMapping( HANDLE hFile, //物理文件句柄 LPSECURITY_ATTRIBUTES lpAttributes, //安全設置, 一般NULL DWORD flProtect, //保護設置 DWORD dwMaximumSizeHigh, //高位文件大小 DWORD dwMaximumSizeLow, //低位文件大小 LPCTSTR lpName //共享內存名稱 ); 調用CreateFileMapping的時候GetLastError的對應錯誤 ERROR_FILE_INVALID 如果企圖創建一個零長度的文件映射, 應有此報 ERROR_INVALID_HANDLE 如果發現你的命名內存空間和現有的內存映射, 互斥量, 信號量, 臨界區同名就麻煩了 ERROR_ALREADY_EXISTS 表示內存空間命名已經存在 */ //2.1獲取文件大小 DWORD fileSizeLow = 0,fileSizeHigh = 0; if(type == SEQ_READ || type == RANDOM_READ || type == SEQ_READ_WRITE || type == RANDOM_READ_WRITE){ fileSizeLow = GetFileSize(m_FileHandler,&fileSizeHigh); //文件長度 m_FileSize = ((unsigned __int64)fileSizeHigh << 32) + (unsigned __int64)fileSizeLow; } else { m_FileSize = view_size;//待創建的文件的大小 fileSizeHigh = view_size >> 32; fileSizeLow = view_size & 0xFFFFFFFF; } //2.2創建映射文件 m_FileMemMapHandler = CreateFileMapping(m_FileHandler,NULL,page_access_mode,fileSizeHigh,fileSizeLow,shared_name.c_str()); err_code = GetLastError();//錯誤類型定義在WinError.h if(m_FileMemMapHandler == NULL){ sprintf(err_msg,"創建映射文件錯誤"); CloseHandle(m_FileHandler); ShowError(err_msg); return false; } switch(err_code){ case ERROR_FILE_INVALID: { sprintf(err_msg,"企圖創建一個零長度的文件映射錯誤"); CloseHandle(m_FileHandler); ShowError(err_msg); return false; } break; case ERROR_INVALID_HANDLE: { sprintf(err_msg,"你的命名內存空間和現有的內存映射, 互斥量, 信號量, 臨界區同名"); CloseHandle(m_FileHandler); ShowError(err_msg); return false; } break; case ERROR_ALREADY_EXISTS: { sprintf(err_msg,"內存空間命名已經存在"); CloseHandle(m_FileHandler); ShowError(err_msg); return false; } break; } //3.加載映射文件 /* LPVOID MapViewOfFile( HANDLE hFileMappingObject, //物理文件句柄 DWORD dwDesiredAccess, //對文件數據的訪問方式 DWORD dwFileOffsetHigh, //文件的偏移地址高位 DWORD dwFileOffsetLow, //文件的偏移地址低位 DWORD dwNumberOfBytesToMap); 文件的偏移地址由DWORD型的參數dwFileOffsetHigh和dwFileOffsetLow組成的64位值來指定, 而且必須是操作系統的分配粒度的整數倍,對於Windows操作系統,分配粒度固定為64KB dwNumberOfBytesToMap:映射文件部分的大小,如果為0,則映射整個文件。 返回值: 如果成功返回返回映射視圖的起始地址,如果失敗返回NULL。 在完成對映射到進程地址空間區域的文件處理后,需要通過函數UnmapViewOfFile()完成對文件數據映像的釋放,該函數原型聲明如下: BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); */ //3.1動態獲取當前操作系統的分配粒度: SYSTEM_INFO sinf; GetSystemInfo(&sinf); m_MapChunkSize = sinf.dwAllocationGranularity; m_FileChunkSize = 1000*m_MapChunkSize; //3.2把文件數據映射到進程的地址空間 /* 而在某些特殊行業,經常要面對十幾GB乃至幾十GB容量的巨型文件,而一個32位進程所擁有的虛擬地址空間只有232 = 4GB,顯然不能一次將文件映像全部映射進來。對於這種情況只能依次將大文件的各個部分映射到進程中的一個較小的地址空間。這需要對上面的一般流程進行適當的更改: 1)映射文件開頭的映像。 2)對該映像進行訪問。 3)取消此映像 4)映射一個從文件中的一個更深的位移開始的新映像。 5)重復步驟2,直到訪問完全部的文件數據。 */ m_CurOffset = 0; if(m_FileSize > m_FileChunkSize) m_CurChunkSize = m_FileChunkSize; else m_CurChunkSize = m_FileSize; m_BaseAddr = (byte*)MapViewOfFile(m_FileMemMapHandler,m_ViewAccess,0,0,m_CurChunkSize); if(m_BaseAddr != NULL){ m_FileMapped = true; return true; } else{ err_code = GetLastError();//錯誤類型定義在WinError.h switch(err_code) { case ERROR_ACCESS_DENIED: sprintf(err_msg,"文件數據映射到進程的地址空間錯誤,無權限!"); break; } CloseHandle(m_FileMemMapHandler); CloseHandle(m_FileHandler); ShowError(err_msg); return false; } } bool ReleaseFileMapping(){ /* 在完成對映射到進程地址空間區域的文件處理后,需要通過函數UnmapViewOfFile()完成對文件數據映像的釋放,該函數原型聲明如下: BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); // lpBaseAddress 映射視圖起始地址 */ if(!m_FileMapped || m_BaseAddr == NULL) return false; //1.釋放文件數據映像 if(m_BaseAddr!=NULL) UnmapViewOfFile(m_BaseAddr); //2.關閉內存映射句柄 CloseHandle(m_FileMemMapHandler); //3.關閉進行內存映射的文件 CloseHandle(m_FileHandler); //重置狀態 m_FileMapped = false; m_FileHandler = NULL; m_FileMemMapHandler = NULL; m_BaseAddr = NULL; m_FileSize = 0; m_FileChunkSize = 0; m_CurChunkSize = 0; m_MapChunkSize = 0; m_CurOffset = 0; m_ViewAccess=0; return true; } string GetLastFileName(const char* pathName){ char spliter = '\\'; int pos = strlen(pathName); for(;pos>=0 &&(*(pathName+pos)) != spliter; pos--); const char* fname = pathName + (pos + 1); string fileName(fname); return fileName; } bool IsFileMapped(){ return m_FileMapped; } unsigned __int64 GetCurFileSize(){ if(m_FileMapped) return m_FileSize; else return -1; } bool MapFileChunk(unsigned __int64 offset, size_t len)//前提len <= m_FileChunkSize { if(offset+len > m_FileSize) return false; if(len > m_CurChunkSize) return false; bool needNewChunk = false; if((offset < m_CurOffset)||//請求的地址在上一個chunk中 ((offset + len) > (m_CurOffset + m_CurChunkSize)))//請求的地址在下一個chunk中 { m_CurOffset = offset; DWORD offsetmod = m_CurOffset % m_MapChunkSize; m_CurOffset -= offsetmod;//文件的偏移地址必須是操作系統的分配粒度的整數倍 if(m_CurOffset + m_FileChunkSize > m_FileSize) m_CurChunkSize = m_FileSize - m_CurOffset; else m_CurChunkSize = m_FileChunkSize; needNewChunk = true; } else needNewChunk = false; if(needNewChunk) { //3)取消此映像 //4)映射一個從文件中的一個更深的位移開始的新映像。 UnmapViewOfFile(m_BaseAddr); m_BaseAddr = (byte*)MapViewOfFile(m_FileMemMapHandler,m_ViewAccess,(m_CurOffset >> 32),(m_CurOffset & 0xFFFFFFFF),m_CurChunkSize); if(m_BaseAddr == NULL){ char err_msg[100]; DWORD err_code = GetLastError();//錯誤類型定義在WinError.h switch(err_code) { case ERROR_ACCESS_DENIED: sprintf(err_msg,"文件數據映射到進程的地址空間錯誤,無權限!"); break; } ShowError(err_msg); return false; } } return true; } //從相對m_BaseAddr首地址offset位置拷貝len長度的數據到dst;確保dst有足夠的內存空間 bool GetMemory(void* dst,unsigned __int64 offset, size_t len){ if(offset < 0 || offset + len > m_FileSize) return false; unsigned __int64 curoffset=offset; size_t curlen = 0; while(len>0) { if(len > m_FileChunkSize) curlen = m_FileChunkSize; else curlen = len; if(!MapFileChunk(curoffset,curlen)) return false; memcpy((char*)dst+(curoffset - offset),m_BaseAddr+(curoffset - m_CurOffset),curlen); curoffset += curlen; len -= curlen; } return true; } //向相對m_BaseAddr首地址offset位置寫入len長度的src數據;確保src有足夠的內存空間 bool WriteMemory(void* src,unsigned __int64 offset, size_t len){ /*在使用內存映射文件時,為了提高速度,系統將文件的數據頁面進行高速緩存, 而且在處理文件映射視圖時不立即更新文件的磁盤映像。 為解決這個問題可以考慮使用FlushViewOfFile()函數, 該函數強制系統將修改過的數據部分或全部重新寫入磁盤映像, 從而可以確保所有的數據更新能及時保存到磁盤。 將內存復制到所映射的物理文件上面 FlushMapViewOfFile函數可以將內存里面的內容DUMP到物理磁盤上面 FlushViewOfFile 把文件映射視圖中的修改的內容或全部寫回到磁盤文件中 BOOL FlushViewOfFile( LPCVOID lpBaseAddress, // 修改內容的起始地址 DWORD dwNumberOfBytesToFlush // 修改的字節數目 ); 函數執行成功返回非零。 */ if(offset < 0 || offset + len > m_FileSize) return false; unsigned __int64 curoffset=offset; size_t curlen = 0; while(len>0) { if(len > m_FileChunkSize) curlen = m_FileChunkSize; else curlen = len; if(!MapFileChunk(curoffset,curlen)) return false; memcpy(m_BaseAddr+(curoffset - m_CurOffset),(char*)src+(curoffset - offset),curlen); FlushViewOfFile(m_BaseAddr+(curoffset - m_CurOffset),curlen);//把文件映射視圖中的修改的內容或全部寫回到磁盤文件中 curoffset += curlen; len -= curlen; } return true; } }; #endif
測試:
1 // Demo.cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <iostream> 6 #include <fstream> 7 #include <string> 8 #include "MemFileMapHelper.h" 9 using namespace std; 10 11 12 typedef struct{ 13 double X; 14 double Y; 15 double Z; 16 }stru_pos; 17 18 int main(int argc, char* argv[]) 19 { 20 bool flag; 21 int nSize = 10; 22 char* fileName = "F:\\Code\\cpp\\Demo\\Demo\\test.txt"; 23 24 stru_pos *posArr = new stru_pos[nSize]; 25 for (int i=0;i<nSize;i++) 26 { 27 posArr[i].X = i+1; 28 posArr[i].Y = i+2; 29 posArr[i].Z = i+3; 30 } 31 32 CMemFileMapHelper fh; 33 //seq write 34 flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_WRITE,nSize * sizeof(stru_pos)); 35 if(flag){ 36 fh.WriteMemory(posArr,0,nSize*sizeof(stru_pos)); 37 fh.ReleaseFileMapping(); 38 } 39 if(!flag) return -1; 40 41 ////radom write 42 //flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::RANDOM_WRITE,nSize * sizeof(stru_pos)); 43 //if(flag){ 44 // for (int i=nSize-1;i>=0 && flag;i--) 45 // { 46 // flag = fh.WriteMemory(&posArr[i],i*sizeof(stru_pos),sizeof(stru_pos)); 47 // cout<<posArr[i].X <<" "<<posArr[i].Y <<" "<<posArr[i].Z<<endl; 48 // } 49 // fh.ReleaseFileMapping(); 50 //} 51 //if(!flag) return -1; 52 53 //seq read 54 flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_READ); 55 for (int i=0;i<nSize && flag;i++) 56 { 57 stru_pos pos; 58 flag = fh.GetMemory(&pos,i*sizeof(stru_pos),sizeof(stru_pos)); 59 cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl; 60 } 61 fh.ReleaseFileMapping(); 62 63 ////random read 64 //flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::RANDOM_READ); 65 //for (int i=nSize - 1;i>=0 && flag;i--) 66 //{ 67 // stru_pos pos; 68 // flag = fh.GetMemory(&pos,i*sizeof(stru_pos),sizeof(stru_pos)); 69 // cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl; 70 //} 71 //fh.ReleaseFileMapping(); 72 73 ////random read write 74 //flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_READ_WRITE); 75 //stru_pos pos; 76 //flag = fh.GetMemory(&pos,5*sizeof(stru_pos),sizeof(stru_pos)); 77 //cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl; 78 //pos.X = pos.Y = pos.Z = 13; 79 //flag = fh.WriteMemory(&pos,5*sizeof(stru_pos),sizeof(stru_pos)); 80 //cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl; 81 //fh.ReleaseFileMapping(); 82 83 delete[] posArr; 84 cin>>flag; 85 return 0; 86 }
32位程序處理4G以上文件,參考網絡內容: “
而在某些特殊行業,經常要面對十幾GB乃至幾十GB容量的巨型文件,而一個32位進程所擁有的虛擬地址空間只有232 = 4GB,顯然不能一次將文件映像全部映射進來。對於這種情況只能依次將大文件的各個部分映射到進程中的一個較小的地址空間。這需要對上面的一般流程進行適當的更改:
1)映射文件開頭的映像。
2)對該映像進行訪問。
3)取消此映像
4)映射一個從文件中的一個更深的位移開始的新映像。
5)重復步驟2,直到訪問完全部的文件數據。
下面給出一段根據此描述而寫出的對大於4GB的文件的處理代碼:
// 選擇文件
CFileDialog fileDlg(TRUE, "*.txt", "*.txt", NULL, "文本文件 (*.txt)|*.txt||", this);
fileDlg.m_ofn.Flags |= OFN_FILEMUSTEXIST;
fileDlg.m_ofn.lpstrTitle = "通過內存映射文件讀取數據";
if (fileDlg.DoModal() == IDOK)
{
// 創建文件對象
HANDLE hFile = CreateFile(fileDlg.GetPathName(), GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
TRACE("創建文件對象失敗,錯誤代碼:%d\r\n", GetLastError());
return;
}
// 創建文件映射對象
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hFileMap == NULL)
{
TRACE("創建文件映射對象失敗,錯誤代碼:%d\r\n", GetLastError());
return;
}
// 得到系統分配粒度
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
DWORD dwGran = SysInfo.dwAllocationGranularity;
// 得到文件尺寸
DWORD dwFileSizeHigh;
__int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
qwFileSize |= (((__int64)dwFileSizeHigh) << 32);
// 關閉文件對象
CloseHandle(hFile);
// 偏移地址
__int64 qwFileOffset = 0;
// 塊大小
DWORD dwBlockBytes = 1000 * dwGran;
if (qwFileSize < 1000 * dwGran)
dwBlockBytes = (DWORD)qwFileSize;
while (qwFileSize > 0)
{
// 映射視圖
LPBYTE lpbMapAddress = (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),
dwBlockBytes);
if (lpbMapAddress == NULL)
{
TRACE("映射文件映射失敗,錯誤代碼:%d\r\n", GetLastError());
return;
}
// 對映射的視圖進行訪問
for(DWORD i = 0; i < dwBlockBytes; i++)
BYTE temp = *(lpbMapAddress + i);
// 撤消文件映像
UnmapViewOfFile(lpbMapAddress);
// 修正參數
qwFileOffset += dwBlockBytes;
qwFileSize -= dwBlockBytes;
}
// 關閉文件映射對象句柄
CloseHandle(hFileMap);
AfxMessageBox("成功完成對文件的訪問");
}
在本例中,首先通過GetFileSize()得到被處理文件長度(64位)的高32位和低32位值。然后在映射過程中設定每次映射的塊大小為1000倍的分配粒度,如果文件長度小於1000倍的分配粒度時則將塊大小設置為文件的實際長度。在處理過程中由映射、訪問、撤消映射構成了一個循環處理。其中,每處理完一個文件塊后都通過關閉文件映射對象來對每個文件塊進行整理。CreateFileMapping()、MapViewOfFile()等函數是專門用來進行內存文件映射處理用的。
”
