MFC内存映射文件
MFC内存映射文件是由一个文件到一块内存的映射。Win32提供了允许应用程序把文件映射到一个进程的函数 (CreateFileMapping)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。
内存映射文件一般有以下三个作用
- 读取文件
- Windows系统加载和执行程序的exe文件和DLL文件。
- 在多个进程之间通过内存映射文件交换数
读文的几种方式:
一般读文件有以下几种方式:
- C语言:通过File* PFile = fopen(“文件名”)
- C++:输出文件流:ofstream ofs(“文件名”)
输入文件流:ifs(“文件名”)
- Windows API函数:HANDLE hFife = CreateFile(...)
WriteFile(...)
以上三种函数都可以回去文件的类容,但是不是最好的方法,而使用内存映射文件是最好的,但是用此技术读取文件有些大材小用了。
内存映射文件的步骤:
- 第一步、创建文件对象 CreateFile()
- 第二步、创建文件映射对象CreateFileMapping()
- 第三步、将文件的数据映射到进程的地址空间 MapViewofFile()
- 第四步、从进程的地址空间撤销对文件数据的映射 UNmapViewofFile()
- 第五步、关闭文件映射对象 CloseHandle()
- 第六步、关闭文件对象 CloseHandle()
使用的函数
1、CreateFile()
这是一个多功能的函数,可打开或创建文件或者I/O设备,并返回可访问的句柄:控制台,通信资源,目录(只读打开),磁盘驱动器,文件,邮槽,管道。
HANDLE CreateFile(LPCTSTR lpFileName, //普通文件名或者设备文件名DWORD dwDesiredAccess, //访问模式(写/读)DWORD dwShareMode, //共享模式LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针DWORD dwCreationDisposition, //如何创建DWORD dwFlagsAndAttributes, //文件属性HANDLE hTemplateFile //用于复制文件句柄);
- lpFileName String要打开的文件的名或设备名。这个字符串的最大长度在ANSI版本中为MAX_PATH,在unicode版本中为32767。
- dwDesiredAccess指定类型的访问对象。如果为 GENERIC_READ 表示允许对设备进行读访问;如果为 GENERIC_WRITE 表示允许对设备进行写访问(可组合使用);如果为零,表示只允许获取与一个设备有关的信息 。另外,还可以指定下面的控制标志:
标准控制权限(16-23位掩码):
DELETE | 删除对象的权限。 |
READ_CONTROL | 从对象的安全描述符中读取信息的权限,但不包括SACL(系统访问控制列表)中的信息。 |
WRITE_DAC | 修改对象安全描述符中的DACL(随机访问控制列表)的权限 |
WRITE_OWNER | 修改对象安全描述符中的属主的权限 |
SYNCHRONIZE | 同步化使用对象的权限,即可以创建一个线程等待信号量释放(但有些对象不支持这个权限)。 |
STANDARD_RIGHTS_REQUIRED | 等价于前面四种权限的总合(通常这四种是必须具有的权限)。 |
STANDARD_RIGHTS_READ | 一般等价于READ_CONTROL |
STANDARD_RIGHTS_WRITE | 一般等价于WRITE_CONTROL |
STANDARD_RIGHTS_EXECUTE | 一般等价于EXECUTE_CONTROL |
STANDARD_RIGHTS_ALL | 等价于前面五种权限的总合。 |
特殊控制权限(0-15位掩码):
SPECIFIC_RIGHTS_ALL | |
ACCESS_SYSTEM_SECURITY | |
MAXIMUM_ALLOWED | |
GENERIC_READ | |
GENERIC_WRITE | |
GENERIC_EXECUTE | |
GENERIC_ALL |
注:实质上是通过ACCESS_MASK结构体的一个双字值来设置标准权限、特殊权限和一般权限的。
- dwShareModeLong, 如果是零表示不共享; 如果是FILE_SHARE_DELETE表示随后打开操作对象会成功,但只有删除访问请求的权限;如果是FILE_SHARE_READ随后打开操作对象会成功只有请求读访问的权限;如果是FILE_SHARE_WRITE 随后打开操作对象会成功,但只有请求写访问的权限。
- lpSecurityAttributesSECURITY_ATTRIBUTES, 指向一个SECURITY_ATTRIBUTES结构的指针,定义了文件的安全特性(如果操作系统支持的话)
- dwCreationDispositionLong,下述常数之一:
CREATE_NEW | 创建文件;如文件存在则会出错 |
CREATE_ALWAYS | 创建文件,会改写前一个文件 |
OPEN_EXISTING | 文件必须已经存在。由设备提出要求 |
OPEN_ALWAYS | 如文件不存在则创建它 |
TRUNCATE_EXISTING | 将现有文件缩短为零长度 |
- dwFlagsAndAttributesLong, 一个或多个下述常数
FILE_ATTRIBUTE_ARCHIVE | 标记归档属性 |
FILE_ATTRIBUTE_COMPRESSED | 将文件标记为已压缩,或者标记为文件在目录中的默认压缩方式 |
FILE_ATTRIBUTE_NORMAL | 默认属性 |
FILE_ATTRIBUTE_HIDDEN | 隐藏文件或目录 |
FILE_ATTRIBUTE_READONLY | 文件为只读 |
FILE_ATTRIBUTE_SYSTEM | 文件为系统文件 |
FILE_FLAG_WRITE_THROUGH | 操作系统不得推迟对文件的写操作 |
FILE_FLAG_OVERLAPPED | 允许对文件进行重叠操作 |
FILE_FLAG_NO_BUFFERING | 禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块 |
FILE_FLAG_RANDOM_ACCESS | 针对随机访问对文件缓冲进行优化 |
FILE_FLAG_SEQUENTIAL_SCAN | 针对连续访问对文件缓冲进行优化 |
FILE_FLAG_DELETE_ON_CLOSE | 关闭了上一次打开的句柄后,将文件删除。特别适合l临时文件 |
也可在Windows NT下组合使用下述常数标记:
SECURITY_ANONYMOUS, SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION, SECURITY_DELEGATION, SECURITY_CONTEXT_TRACKING, SECURITY_EFFECTIVE_ONLY
- hTemplateFile,hTemplateFile为一个文件或设备句柄,表示按这个参数给出的句柄为模板创建文件(就是将该句柄文件拷贝到lpFileName指定的路径,然后再打开)。它将指定该文件的属性扩展到新创建的文件上面,这个参数可用于将某个新文件的属性设置成与现有文件一样,并且这样会忽略dwAttrsAndFlags。通常这个参数设置为NULL,为空表示不使用模板,一般为空。
- 返回值Long,如执行成功,则返回文件句柄。INVALID_HANDLE_VALUE表示出错
2、CreateFileMapping()
CreateFileMapping函数用于创建一个文件映射内核对象
HANDLE CreateFileMapping(HANDLE hFile, //物理文件句柄LPSECURITY_ATTRIBUTES lpAttributes, //安全设置DWORD flProtect, //保护设置DWORD dwMaximumSizeHigh, //高位文件大小DWORD dwMaximumSizeLow, //低位文件大小LPCTSTR lpName //共享内存名称
);
- hFile:Long,指定欲在其中创建映射的一个文件句柄。0xFFFFFFFF(-1,即INVALID_HANDLE_VALUE)表示在页面文件中创建一个可共享的文件映射。
- lpFileMappigAttributes:SECURITY_ATTRIBUTES,它指明返回的句柄是否可以被子进程所继承,指定一个安全对象,在创建文件映射时使用。如果为NULL(用ByVal As Long传递零),表示使用默认安全对象。
flProtect:Long,下述常数之一:
PAGE_READONLY | 以只读方式打开映射 |
PAGE_READWRITE | 以可读、可写方式打开映射 |
PAGE_WRITECOPY | 为写操作留下备份 |
可组合使用下述一个或多个常数:
SEC_COMMIT | 为文件映射一个小节中的所有页分配内存 |
SEC_IMAGE | 文件是个可执行文件 |
SEC_RESERVE | 为没有分配实际内存的一个小节保留虚拟内存空间 |
- dwMaximumSizeHigh:Long,文件映射的最大长度的高32位。
- dwMaximumSizeLow:Long,文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。
- lpName:String,指定文件映射对象的名字。如存在这个名字的一个映射,函数就会打开它。用vbNullString可以创建一个无名的文件映射。
- 返回值调用CreateFileMapping的时候可能会出现的GetLastError的相应错误:
ERROR_FILE_INVALID | (错误_文件_无效)如果企图创建一个零长度的文件映射 |
ERROR_INVALID_HANDLE | (错误_无效_处理) 内存空间的命名和现有的内存映射,互斥量,信号量,临界区有同名 |
ERROR_ALREADY_EXISTS | (错误或已经存在)表示内存空间命名已经存在 |
3、MapViewofFile()
MapViewOfFile是计算机函数,功能是将一个文件映射对象映射到当前应用程序的地址空间。MapViewOfFileEx允许我们指定一个基本地址来进行映射。
LPVOID MapViewOfFile(HANDLE hFileMappingObject,DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap
);
- 参数1:hFileMappingObject 为CreateFileMapping()或OpenFileMapping()返回的文件映像对象句柄。
- 参数2:dwDesiredAccess 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。 可取以下值:
FILE_MAP_ALL_ACCESS | 等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ. 文件映射对象被创建时必须指定PAGE_READWRITE 选项. |
FILE_MAP_COPY | 可以读取和写入文件.写入操作会导致系统为该页面创建一份副本.在调用CreateFileMapping时必须传入PAGE_WRITECOPY保护属性. |
FILE_MAP_EXECUTE | 可以将文件中的数据作为代码来执行.在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性. |
FILE_MAP_READ | 可以读取文件.在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性. |
FILE_MAP_WRITE | 可以读取和写入文件.在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性. |
- 参数3:dwFileOffsetHigh 表示文件映射起始偏移的高32位.
- 参数4:dwFileOffsetLow 表示文件映射起始偏移的低32位.(64KB对齐不是必须的)
- 参数5:dwNumberOfBytes 指定映射文件的字节数.
4、UNmapViewofFile()
在当前应用程序的内存地址空间解除对一个文件映射对象的映射。
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress // address where mapped view begins
);
- lpBaseAddress Long,指定要解除映射的一个文件映射的基准地址。这个地址是早先用MapViewOfFile函数获得的。
- 返回值Bool,非零表示成功,零表示失败。可以通过GetLastError()获取错误代码。
5、CloseHandle()
CloseHandle包括文件、文件映射、进程、线程、安全和同步对象等。涉及文件处理时,这个函数通常与vb的close命令相似。应尽可能的使用close,因为它支持vb的差错控制。
BOOL CloseHandle(HANDLE hObject
);
- hObject :代表一个已打开对象handle。
- 返回值
TRUE:执行成功;
FALSE:执行失败,可以调用GetLastError()获知失败原因。
6、OpenFileMapping
OpenFileMapping是打开一个现成的文件映射对象的函数。
HANDLE OpenFileMapping(DWORD dwDesiredAccess, // access mode BOOL bInheritHandle, // inherit flag LPCTSTR lpName // pointer to name of file-mapping object
);
- 返回值零表示出错,可以设置GetLastError。
- dwDesiredAccess是指带有前缀FILE_MAP_???的一个常数。参考MapViewOfFile函数dwDesiredAccess参数的说明。
FILE_MAP_ALL_ACCESS | 等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ. 文件映射对象被创建时必须指定PAGE_READWRITE 选项. |
FILE_MAP_COPY | 可以读取和写入文件.写入操作会导致系统为该页面创建一份副本.在调用CreateFileMapping时必须传入PAGE_WRITECOPY保护属性. |
FILE_MAP_EXECUTE | 可以将文件中的数据作为代码来执行.在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性. |
FILE_MAP_READ | 可以读取文件.在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性. |
FILE_MAP_WRITE | 可以读取和写入文件.在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性. |
- bInheritHandle Long,如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE。
- lpName String,指定要打开的文件映射对象名称。
实例1:
通过加载特定文件中的类容,将其显示在文件编辑框中。环境搭建,需要在软件的exe文件的目录中添加一个.txt文件的文本文档。
内存文件映射的代码:
bool CMFC内存映射技术Dlg::LoadFile(CString strFileName)
{HANDLE hFile, hMapping;//定义一个点创建文件和文件映射的句柄void *basepointer;//定义一个指针变量,通过指针读操作内存if ((hFile = CreateFile(strFileName,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0)) == INVALID_HANDLE_VALUE)//创建文件对象//用来打开已经存在的文件{AfxMessageBox(_T("Could not open file"));return FALSE;}if (!(hMapping = CreateFileMapping(hFile,0,PAGE_READONLY,0,0,NULL)))//创建文件映射对象{AfxMessageBox(_T("Mapping failed"));CloseHandle(hFile);return FALSE;}if (!(basepointer = MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0)))//文件通过映射文件映射到内存{AfxMessageBox(_T("View failed"));CloseHandle(hFile);CloseHandle(hMapping);}m_strText = (LPTSTR)basepointer;//将文件的数据映射到进程的地址空间的指针转化为字符串的指针。UnmapViewOfFile(basepointer); //从进程的地址空间撤销对文件数据的映射CloseHandle(hFile);// 关闭文件对象CloseHandle(hMapping);//关闭文件映射对象return true;
}
实例2:
内存映射文件_共享数据,两个进程之间进行数据交换,有进程A通过内存映射文件技术将输入放入到内存中,之后由进程B将数据取出来显示。
进程A的源码:
- 在进程的主界面的初始化函数中创建文件映射对象和内存指针
m_hMapObject = CreateFileMapping((HANDLE)0xffffffff,NULL,PAGE_READWRITE,0,0x1000,_T("shared_memory")); if (!m_hMapObject) {AfxMessageBox(_T("Unable to create shared memory file"));return FALSE; } m_pszMapView = (LPTSTR)MapViewOfFile(m_hMapObject,FILE_MAP_WRITE,0,0,0); if (!m_pszMapView) {AfxMessageBox(_T("Unable to map shared memory file"));return FALSE; }
- 将界面中的数值写入到内存中
void CDemoADlg::OnBnClickedSend()
{// TODO: 在此添加控件通知处理程序代码UpdateData();lstrcpy(m_pszMapView,m_strSend);
}
进程B源码
1、在进程的主界面的初始化函数中打开文件映射对象和内存指针
m_hMapObject = OpenFileMapping(FILE_MAP_READ,FALSE , _T("shared_memory"));
if (!m_hMapObject)
{AfxMessageBox(_T("Can`t open shared memory file"));return FALSE;
}
m_pszMapView = (LPTSTR)MapViewOfFile(m_hMapObject, FILE_MAP_READ, 0, 0, 0);
if (!m_pszMapView)
{AfxMessageBox(_T("Unable to map shared memory file"));return FALSE;
}
SetTimer(0x10,500,NULL);
2、通过定时器获取内存地址中的数值
void CDemoBDlg::OnTimer(UINT_PTR nIDEvent)
{// TODO: 在此添加消息处理程序代码和/或调用默认值m_strText = m_pszMapView;UpdateData(FALSE);CDialogEx::OnTimer(nIDEvent);
}
注意:此一文主要是记录小编的笔记,有些事网上凑的,若有侵权,请联系删除!