CreateFileMapping用法

CreateFileMapping的MSDN翻译和使用心得编程

测试建立和打开文件映射的时候总是获得"句柄无效"的错误, 仔细看了MSDN之后才发觉是函数认识不透, 这里把相关的解释翻译出来缓存

HANDLE CreateFileMapping(  安全

HANDLE hFile,                                     //物理文件句柄  网络

LPSECURITY_ATTRIBUTES lpAttributes, //安全设置  app

DWORD flProtect,                                //保护设置  函数

DWORD dwMaximumSizeHigh,             //高位文件大小  性能

DWORD dwMaximumSizeLow,              //低位文件大小  测试

LPCTSTR lpName                                //共享内存名称 );大数据

1) 物理文件句柄    任何能够得到的物理文件句柄, 若是你须要建立一个物理文件无关的内存映射也无妨, 将它设置成为 0xFFFFFFFF(INVALID_HANDLE_VALUE)就能够了.spa

   若是须要和物理文件关联, 要确保你的物理文件建立的时候的访问模式和"保护设置"匹配, 好比: 物理文件只读, 内存映射须要读写就会发生错误. 推荐你的物理文件使用独占方式建立.

   若是使用 INVALID_HANDLE_VALUE, 也须要设置须要申请的内存空间的大小, 不管物理文件句柄参数是否有效, 这样 CreateFileMapping 就能够建立一个和物理文件大小无关的内存空间给你, 甚至超过实际文件大小, 若是你的物理文件有效, 而大小参数为0, 则返回给你的是一个和物理文件大小同样的内存空间地址范围.  返回给你的文件映射地址空间是能够经过复制, 集成或者命名获得, 初始内容为0.

2) 保护设置    就是安全设置, 不过通常设置NULL就能够了, 使用默认的安全配置. 在win2k下若是须要进行限制, 这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是, 能够考虑进行限制.

3) 高位文件大小    弟兄们, 我想目前咱们的机器都是32位的东东, 不可能获得超过32位进程所能寻址的私有32位地址空间, 通常仍是设置0吧, 我没有也不想尝试将它设置超过0的状况. 4) 低位文件大小    这个仍是能够进行设置的, 不过为了让其余共享用户知道你申请的文件映射的相关信息, 我使用的时候是在得到的地址空间头部添加一个结构化描述信息, 记录内存映射的大小, 名称等, 这样实际申请的空间就比输入的增长了一个头信息结构大小了, 我认为这样相似BSTR的方式应该是比较合理的.

5) 共享内存名称    这个就是我今天测试的时候碰壁的祸根, 由于为了对于内存进行互斥访问, 我设置了一个互斥句柄, 而名称我选择和命名共享内存同名, 之下就是由于他们使用共同的namespace致使了错误, 呵呵.

7) 调用CreateFileMapping的时候GetLastError的对应错误    ERROR_FILE_INVALID     若是企图建立一个零长度的文件映射, 应有此报    ERROR_INVALID_HANDLE   若是发现你的命名内存空间和现有的内存映射, 互斥量, 信号量, 临界区同名就麻烦了    ERROR_ALREADY_EXISTS   表示内存空间命名已经存在

8) 相关服务或者平台的命名保留    Terminal Services:    命名能够包含 "Global\" 或者 "Local\" 前缀在全局或者会话名空间初级文件映射. 其余部分能够包含任何除了(\)之外的字符, 能够参考 Kernel Object Name Spaces.

   Windows 2000 or later:    若是 Terminal Services 没有运行 "Global\" 和 "Local\" 前缀的特殊含义就被忽略了

CreateFileMapping 函数(转载) 内存映射API函数CreateFileMapping建立一个有名的共享内存: HANDLE CreateFileMapping( HANDLE hFile,                                    // 映射文件的句柄,                                                  //设为0xFFFFFFFF以建立一个进程间共享的对象 LPSECURITY_ATTRIBUTES lpFileMappingAttributes,   // 安全属性 DWORD flProtect,                                 // 保护方式 DWORD dwMaximumSizeHigh,                         //对象的大小 DWORD dwMaximumSizeLow, LPCTSTR lpName                                   // 必须为映射文件命名 );

与虚拟内存相似,保护方式能够是PAGE_READONLY或是PAGE_READWRITE。若是多进程都对同一共享内存进行写访问,则必须保持相互间同步。映射文件还能够指定PAGE_WRITECOPY标志,能够保证其原始数据不会遭到破坏,同时容许其余进程在必要时自由的操做数据的拷贝。

在建立文件映射对象后使用能够调用MapViewOfFile函数映射到本进程的地址空间内。

下面说明建立一个名为MySharedMem的长度为4096字节的有名映射文件: HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF), NULL,PAGE_READWRITE,0,0x1000,"MySharedMem"); 并映射缓存区视图: LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile, FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

其余进程访问共享对象,须要得到对象名并调用OpenFileMapping函数。 HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITE, FALSE,"MySharedMem");

一旦其余进程得到映射对象的句柄,能够象建立进程那样调用MapViewOfFile函数来映射对象视图。用户可使用该对象视图来进行数据读写操做,以达到数据通信的目的。

当用户进程结束使用共享内存后,调用UnmapViewOfFile函数以取消其地址空间内的视图: if (!UnmapViewOfFile(pszMySharedMapView)) {

         AfxMessageBox("could not unmap view of file");

} 建立文件视图

    要将文件中的数据映射到进程的虚拟内存中,你必须建立一个文件的视图。     MapViewOfFile和MapViewOfFileEx函数使用CreateFileMapping返回的文件映射对象句柄来在进程的虚拟地址空间里创建文件的视图,或者文件的某个部分。若是这些函数指定的权限标志和CreateFileMapping中的权限标志不一致,则会执行失败。     MapViewOfFile函数返回一个指向文件视图的指针。利用MapViewOfFile中声明的地址指针,程序就能够从文件中读以及向文件中写入数据。向文件视图中写入数据会致使文件映射对象改变。真正将数据写入到磁盘上的文件,由系统负责处理。数据并非立刻就别写到磁盘上,不少文件的输入输出都被缓存起来,以改善系统的性能。程序能够调用FlushViewOfFile函数来越过这个方式,强迫系统立刻将数据写入到磁盘中去。     MapViewOfFileEx函数和MapViewOfFile函数做的工做是如出一辙的,只不过能够利用MapViewOfFileEx函数的lpvBase参数,来指定文件视图在进程虚拟地址空间中的基础地址。若是在指定的地址处没有足够的空间,则调用失败。     一、lpvBase参数必须是系统内存最小单位的整数倍,不然调用会失败。要获得系统内存的最小单位,使用GetSystemInfo函数,他将信息写到SYSTEM_INFO结构的成员中。     程序能够从同一个文件映射对象中建立多个文件视图。文件视图能够是不一样的大小,但他们必须小于文件映射对象。MapViewOfFile函数的dwOffsetHigh和dwOffsetLow参数必须是系统内存最小单位的整数倍。

文件映射问题 内存映射文件并非简单的文件I/O操做,实际用到了Windows的核心编程技术--内存管理。因此,若是想对内存映射文件有更深入的认识,必须对Windows操做系统的内存管理机制有清楚的认识,内存管理的相关知识很是复杂,超出了本文的讨论范畴,在此就再也不赘述,感兴趣的读者能够参阅其余相关书籍。下面给出使用内存映射文件的通常方法:          首先要经过CreateFile()函数来建立或打开一个文件内核对象,这个对象标识了磁盘上将要用做内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操做系统后,只指定了映像文件的路径,映像的长度尚未指定。为了指定文件映射对象须要多大的物理存储空间还须要经过CreateFileMapping()函数来建立一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。在建立了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据做为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责经过系统的管理而将文件映射对象的所有或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同一般加载到内存中的文件数据的处理方式基本同样,在完成了对内存映射文件的使用时,还要经过一系列的操做完成对其的清除和使用过资源的释放。这部分相对比较简单,能够经过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、经过CloseHandle()关闭前面建立的文件映射对象和文件对象。

内存映射文件相关函数           在使用内存映射文件时,所使用的API函数主要就是前面提到过的那几个函数,下面分别对其进行介绍:         HANDLE   CreateFile(LPCTSTR   lpFileName,     DWORD   dwDesiredAccess,     DWORD   dwShareMode,     LPSECURITY_ATTRIBUTES   lpSecurityAttributes,     DWORD   dwCreationDisposition,     DWORD   dwFlagsAndAttributes,       HANDLE   hTemplateFile);               函数CreateFile()即便是在普通的文件操做时也常常用来建立、打开文件,在处理内存映射文件时,该函数来建立/打开一个文件内核对象,并将其句柄返回,在调用该函数时须要根据是否须要数据读写和文件的共享方式来设置参数dwDesiredAccess和dwShareMode,错误的参数设置将会致使相应操做时的失败。        HANDLE   CreateFileMapping(HANDLE   hFile,     LPSECURITY_ATTRIBUTES   lpFileMappingAttributes,     DWORD   flProtect,     DWORD   dwMaximumSizeHigh,     DWORD   dwMaximumSizeLow,     LPCTSTR   lpName);               CreateFileMapping()函数建立一个文件映射内核对象,经过参数hFile指定待映射到进程地址空间的文件句柄(该句柄由CreateFile()函数的返回值获取)。因为内存映射文件的物理存储器实际是存储于磁盘上的一个文件,而不是从系统的页文件中分配的内存,因此系统不会主动为其保留地址空间区域,也不会自动将文件的存储空间映射到该区域,为了让系统可以肯定对页面采起何种保护属性,须要经过参数flProtect来设定,保护属性PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY分别表示文件映射对象被映射后,能够读取、读写文件数据。在使用PAGE_READONLY时,必须确保CreateFile()采用的是GENERIC_READ参数;PAGE_READWRITE则要求CreateFile()采用的是GENERIC_READ|GENERIC_WRITE参数;至于属性PAGE_WRITECOPY则只须要确保CreateFile()采用了GENERIC_READ和GENERIC_WRITE其中之一便可。DWORD型的参数dwMaximumSizeHigh和dwMaximumSizeLow也是至关重要的,指定了文件的最大字节数,因为这两个参数共64位,所以所支持的最大文件长度为16EB,几乎能够知足任何大数据量文件处理场合的要求。        LPVOID   MapViewOfFile(HANDLE   hFileMappingObject,     DWORD   dwDesiredAccess,     DWORD   dwFileOffsetHigh,     DWORD   dwFileOffsetLow,     DWORD   dwNumberOfBytesToMap);             MapViewOfFile()函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject为CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,并且一样要与CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可使应用程序能更多的对数据的保护属性实行有效控制。MapViewOfFile()函数容许所有或部分映射文件,在映射时,须要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,并且必须是操做系统的分配粒度的整数倍,对于Windows操做系统,分配粒度固定为64KB。固然,也能够经过以下代码来动态获取当前操做系统的分配粒度:        SYSTEM_INFO   sinf;     GetSystemInfo(&sinf);     DWORD   dwAllocationGranularity   =   sinf.dwAllocationGranularity;             参数dwNumberOfBytesToMap指定了数据文件的映射长度,这里须要特别指出的是,对于Windows   9x操做系统,若是MapViewOfFile()没法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL);可是在Windows   2000下,MapViewOfFile()只须要为必要的视图找到足够大的一个区域便可,而无须考虑整个文件映射对象的大小。          在完成对映射到进程地址空间区域的文件处理后,须要经过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明以下:         BOOL   UnmapViewOfFile(LPCVOID   lpBaseAddress);             惟一的参数lpBaseAddress指定了返回区域的基地址,必须将其设定为MapViewOfFile()的返回值。在使用了函数MapViewOfFile()以后,必需要有对应的UnmapViewOfFile()调用,不然在进程终止以前,保留的区域将没法释放。除此以外,前面还曾由CreateFile()和CreateFileMapping()函数建立过文件内核对象和文件映射内核对象,在进程终止以前有必要经过CloseHandle()将其释放,不然将会出现资源泄漏的问题。          除了前面这些必须的API函数以外,在使用内存映射文件时还要根据状况来选用其余一些辅助函数。例如,在使用内存映射文件时,为了提升速度,系统将文件的数据页面进行高速缓存,并且在处理文件映射视图时不当即更新文件的磁盘映像。为解决这个问题能够考虑使用FlushViewOfFile()函数,该函数强制系统将修改过的数据部分或所有从新写入磁盘映像,从而能够确保全部的数据更新能及时保存到磁盘。 

共享内存对象方法(MapViewOfFile) 共享内存对象方法一般,将页面文件支持的内存映射文件做为在用户进程之间共享内存的技术。可是,可使用相同的技术在用户进程与设备驱动程序之间共享内存。使用这种技术有两种方法。

第一种方法中,经过使用 OpenFileMapping,而后调用 MapViewOfFile 函数以获取指向某个区域或全部共享内存的指针,驱动程序能够建立命名内存对象(称为“区域对象”),而且一个或多个用户应用程序能够打开相同的对象。经过向区域对象指定保护属性,能够定义进程操纵内存的方式。

第二种方法中,应用程序能够用 CreateFileMapping 在用户模式下建立命名内存对象。驱动程序经过使用 ZwOpenSection 并调用 ZwMapViewOfSection 获取指向它的指针,能够打开相同的内存对象。始终用异常处理程序在内核模式下访问此内存地址。

因为该对象始终映射在进程的用户地址空间(小于 0x80000000,不管对象是在内核模式仍是在用户模式中建立的)中,所以只在进程上下文中访问地址时,地址才有效。每次在相同内存对象上调用 MapViewOfFile 或 ZwMapViewOfSection 时,都将返回不一样的内存地址(即便是相同的进程,也是如此)。建议不要使用这种方法(尤为是低级设备驱动程序),正如前面所述,这是由于地址范围限定于进行对象映射的进程,而且不能在 DPC 或 ISR 中对地址进行访问。另外,在 DDK 中没有记载在内核模式下建立内存对象的 API。

可是,要在提升的 IRQL(如 DPC 或 ISR 中)上使用该地址,必须查明并锁定缓冲区页面,并获取系统虚拟地址 MmGetSystemAddressForMdl(正如本文前面 IOCTL 方法中所述)。

仅当要在两个(或更多)用户进程与一个(或多个)设备驱动程序之间共享内存的状况下,这种方法才比较简便。不然,使用 IOCTL 技术在用户进程与设备驱动程序之间共享内存更加简单高效。

内存映射文件技术 1. 用途和基本操做 用于不一样进程之间的内存共享操做, 能够将一个物理文件映射到内存当中而后直接利用分配到的或者打开的命名共享内存的地址空间实现资源共享访问

2. 相关流程 1) 新建命名共享内存 首先利用CreateFile或者CreateFileForMapping得到一个用于映射的物理文件句柄, 而后利用该文件句柄结合CreateFileMapping获得一个命名的共享内存映射文件句柄。 //CreateFileMapping 为指定文件建立一个有名或无名的文件映象; HANDLE CreateFileMapping(   HANDLE hFile,              // 映射文件的句柄   LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 安全描述符指针   DWORD flProtect,           // 对映射对象的保护   DWORD dwMaximumSizeHigh,   // 对象最大长度的高32位   DWORD dwMaximumSizeLow,    // 对象最大长度的低32位   LPCTSTR lpName             // 文件内存映射对象的名字 );

注意: hFile:映射文件的句柄,文件的打开模式必须与flProtect参数指定的相一致;若是这个参数值为0xFFFFFFFF,那么必须在dwMaximumSizeHigh和dwMaximumSizeLow参数中指定映射对象的大小。而且将在操做系统虚拟内存页面替换文件中建立文件映射对象,而不是使用磁盘文件,同时必须给出这个映射对象的大小。文件映射对象经过副本,遗传或名字来共享。 lpFileMappingAttributes:安全描述符指针,决定返回句柄是否能被子进程继承,若是是NULL,那么子进程不能继承。WinNt中,若是是NULL,那么文件映射对象获得一个默认的安全描述符。 flProtect:为获得的文件试图指定保护模式,能够被设置为下列值: PAGE_READONLY :只读属性,而且hFile对应的文件必须以GENERIC_READ形式打开。 PAGE_READWRITE:可读可写属性,而且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。 PAGE_WRITECOPY:对可写区域复制后操做,而且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。 dwMaximumSizeHigh,dwMaximumSizeLow:若是这两个参数为0,则文件映射对象的最大长度等于hFile指定的文件长度。 lpName:文件映射对象的名字,若是这个名字已存在,则按照flProtect指定的来处理映射对象。若是此参数为空,则建立一个无名字的文件映射对象。若是此参数的名字与系统事件的名字相同,则函数执行失败,GetLastError返回 ERROR_INVALID_HANDLE;

返回值:函数调用成功返回文件映射对象的句柄,若是文件映射对象已经存在则返回原有映射对象的句柄,GetLastError返回ERROR_ALREADY_EXISTS。函数执行失败返回Null。

2) 打开命名共享内存 若是须要共享已经存在的命名共享内存映射文件, 使用OpenFileMapping函数。 //OpenFileMapping 打开一个已命名的文件映射对象 HANDLE OpenFileMapping(   DWORD dwDesiredAccess,  // 访问模式   BOOL bInheritHandle,    // 继承标志   LPCTSTR lpName          // 文件映射对象名指针 ); 注意: dwDesiredAccess:访问模式与MapViewOfFile中的访问模式相同。 bInheritHandle:继承标志,是否能够被一个新的进程继承使用,若是为TRUE,就能够被一个新进程继承句柄。 返回值: 成功返回一个已命名的文件映射对象,失败返回NULL。

3) 得到地址空间指针 进行内存映射文件的读写和通常的文件读写不一样, 是直接面对你申请的地址空间, 为此须要使用MapViewOfFile获得相关的地址LPVOID类型的指针。若是须要进行文件写入, 能够经过类型转换直接对于内存地址进行赋值, 好比: memcpy( lpAddress, lpBuf, ....) 这里天然须要防止内存溢出的状况。 若是是读取操做,将参数顺序调整一下就能够了。

MapViewOfFile 在调用进程的地址空间映射一个文件视图 LPVOID MapViewOfFile(   HANDLE hFileMappingObject,  // 已建立的文件映射对象句柄   DWORD dwDesiredAccess,      // 访问模式   DWORD dwFileOffsetHigh,     // 文件偏移的高32位   DWORD dwFileOffsetLow,      // 文件偏移的低32位   DWORD dwNumberOfBytesToMap  // 映射视图的大小 ); 注意: hFileMappingObject: 由CreateFileMapping 或 OpenFileMapping 返回的文件映射对象句柄。 dwDesiredAccess:映射视图的访问模式,与建立文件映射对象的保护模式flProtect有关,能够被设置为下列值: FILE_MAP_WRITE:一个可读写属性的文件视图被建立,保护模式为PAGE_READWRITE FILE_MAP_READ :一个只读属性的文件视图被建立,保护模式为PAGE_READWRITE 或 PAGE_READONLY FILE_MAP_ALL_ACCESS:与FILE_MAP_WRITE模式相同 FILE_MAP_COPY:保护模式为PAGE_WRITECOPY时,获得一个视图文件,当你对视图文件写操做时,页面自动交换,而且你所作的修改不会损坏原始数据资料。 dwNumberOfBytesToMap:映射文件部分的大小,若是为0,则映射整个文件。 返回值: 若是成功返回返回映射视图的起始地址,若是失败返回NULL。

4)MapViewOfFileEx 在调用进程的地址空间映射一个文件视图,而且容许调用进程为映射视图指定特殊的内存地址 LPVOID MapViewOfFileEx(   HANDLE hFileMappingObject,  // 文件映射对象的句柄   DWORD dwDesiredAccess,      // 访问模式   DWORD dwFileOffsetHigh,     // 文件偏移的高32位   DWORD dwFileOffsetLow,      // 文件偏移的低32位   DWORD dwNumberOfBytesToMap, // 映射视图的大小   LPVOID lpBaseAddress        // 指定映射视图的其实内存地址 ); 注意: 与MapViewOfFile用法相同,可是若是指定的内存地址空间大小不够,则函数执行失败。

5) 将内存复制到所映射的物理文件上面 FlushMapViewOfFile函数能够将内存里面的内容DUMP到物理磁盘上面 FlushViewOfFile 把文件映射视图中的修改的内容或所有写回到磁盘文件中 BOOL FlushViewOfFile(   LPCVOID lpBaseAddress,       // 修改内容的起始地址   DWORD dwNumberOfBytesToFlush // 修改的字节数目 ); 函数执行成功返回非零。

6) 卸载内存映射文件地址指针 UnmapViewOffFile函数就是卸载 UnmapViewOfFile 删除文件的映射视图 BOOL UnmapViewOfFile(   LPCVOID lpBaseAddress   // 映射视图起始地址 ); 注意: lpBaseAddress:映射视图起始地址,由 MapViewOfFile 函数 MapViewOfFileEx产生。 返回值: 若是调用成功返回非零,而且全部指定地址内的脏页面会被写入硬盘。调用失败返回零。

7) 关闭内存映射文件 太简单了, CloseHandle搞定

相关文章
相关标签/搜索