MFC-利用内存映射文件来读写文件

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


以下简介摘抄自搜狗百科:
文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的。

内存映射文件是由一个文件到进程地址空间的映射。Win32中,每个进程有自己的地址空间,一个进程不能轻易地访问另一个进程地址空间中的数据,所以不能像16位Windows那样做。Win32系统允许多个进程(运行在同一计算机上)使用内存映射文件来共享数据。实际上,其他共享和传送数据的技术,诸如使用SendMessage或者PostMessage,都在内部使用了内存映射文件。


利用内存映射文件来读写文件是大材小用了,这个速度当然是比一些I/O操作函数快了.

首先列出一些步骤和需要的函数

1.创建文件对象

//成功返回文件句柄,失败返回INVALID_HANDLE_VALUE 
HANDLE CreateFile
     (
        LPCTSTR lpFileName,//创建或打开的文件名
  	DWORD dwDesiredAccess,//想要对文件操作的方式
  	DWORD dwShareMode,//指定共享的方式,也就是允许多进程同时对该文件操作
  	LPSECURITY_ATTRIBUTES lpSecurityAttributes,//文件的安全属性,0表示默认
  	DWORD dwCreationDisposition,//指定创建新的文件还是打开文件
  	DWORD dwFlagsAndAttributes,//文件的标志和属性
  	HANDLE hTemplateFile //用已经创建的文件对象做模板,可以覆盖该文件的属性
      );

2.创建文件映射对象

//成功返回文件映射句柄,失败返回NULL
HANDLE CreateFileMapping
      (
  	HANDLE hFile,//文件句柄
  	LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//安全属性
  	DWORD flProtect,//内存页的页面保护
 	DWORD dwMaximumSizeHigh,//指定内存映射的大小
  	DWORD dwMaximumSizeLow,
  	LPCTSTR lpName //文件对象映射的名称,有了这个对象可以在多个进程之间
                       //通过这个名字来数据交换
      );

3.将文件的数据映射到进程的地址空间

//成功返回映射的地址空间地址,失败返回NULL
LPVOID MapViewOfFile
      (
  	HANDLE hFileMappingObject,//文件映射对象的句柄
  	DWORD dwDesiredAccess,   //内存映射文件的访问权限
  	DWORD dwFileOffsetHigh,  //指定文件从哪里开始映射,0,0表示从头开始
  	DWORD dwFileOffsetLow,
  	DWORD dwNumberOfBytesToMap//指定映射文件的大小,如果为0则表示把文件全部
                                 //内容映射到地址空间
       );

4.从进程的地址空间撤销对文件数据的映射

//成功返回非零,失败返回0
BOOL UnmapViewOfFile( 
  	LPCVOID lpBaseAddress 
	);

5.关闭文件映射对象

  BOOL CloseHandle(hFileMapping)

6.关闭文件对象

BOOL CloseHandle(hFile)

这个例子的效果图:
MFC-利用内存映射文件来读写文件_第1张图片

代码如下:

//打开文件对话框
	CFileDialog dlg(TRUE, nullptr, nullptr, OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST,
		TEXT("文本文件(*.txt)|*.txt||"), this);
	if (dlg.DoModal())
	{
		//获取文件路径
		CString path = dlg.GetPathName();
		//创建文件对象
		HANDLE hFile = CreateFile(
			path,                     //文件路径     
			GENERIC_READ, 			  //只读操作
			FILE_SHARE_READ, 		  //只读共享
			nullptr,				  //默认安全属性
			OPEN_EXISTING,			  //只能打开已经存在的文件,否则失败
			FILE_FLAG_SEQUENTIAL_SCAN,//对该文件进行顺序操作而不是随机操作
			nullptr					  //一般为0
			);
		if (hFile == INVALID_HANDLE_VALUE)
		{
			AfxMessageBox(TEXT("创建对象失败!"));
			return;
		}
		//创建文件映射对象
		HANDLE hFileMapping = CreateFileMapping(
			hFile,         //文件对象句柄
			nullptr,	   //默认安全属性
			PAGE_READONLY, //页面保护,只读
			0,			   //需要内存大小和打开文件大小一样
			0,			   //
			nullptr		   //共享内存,这里不需要,设为null
			);
		if (!hFileMapping)
		{
			AfxMessageBox(TEXT("创建文件映射对象失败!"));
			return;
		}
		//将文件映射到内存地址空间
		void* pBuffer = MapViewOfFile(
			hFileMapping,  //文件映射对象句柄 
			FILE_MAP_READ, //映射文件只读
			0,			   //0表示从文件开头开始读取
			0,			   //
			0			   //0表示全部读取
			);
		if (!pBuffer)
		{
			//出错就关闭句柄
			CloseHandle(hFileMapping);
			CloseHandle(hFile);
			return;
		}
		//把读取出来的数据转换后赋给m_content
		//要注意的是,必须先操作完 pBuffer才能UnmapViewOfFile,
		//如果先UnmapViewOfFile,再操作 pBuffer则会出错,因为pBuffer已经被释放了
		m_content = (LPCTSTR)pBuffer;
		UpdateData(FALSE);			 
		//取消映射
		UnmapViewOfFile(pBuffer);
		//关闭文件映射对象
		CloseHandle(hFileMapping);
		//关闭文件对象
		CloseHandle(hFile);
	}

这个例子只能打开Unicode的文本,如果想打开ASCII的文本,则需要进行一些转换.方法如下:

        CStringA strA = (char*)basepointer;
	    m_content = strA;

你可能感兴趣的:(MFC)