This article addresses some common problems encountered when using filemapping. It also points out some platform differences in the file mappingimplementation.
This article does not describe the procedures for performing file mapping.For information on using file mapping, please see the File Mappingoverview in the Microsoft Win32 Programmer's Reference. Also see thedescriptions for CreateFileMapping(), OpenFileMapping(), MapViewOfFile(),MapViewOfFileEx(), UnmapViewOfFile(), and FlushViewOfFile().
Name Space Conflicts
The names of event, semaphore, mutex, and file-mapping objects sharethe same name space, so it is not possible to have two different objecttypes with the same name. It is an error to attempt to create or open anobject of one type using a name that is already being used by an object ofanother type.
CreateFileMapping() and OpenFileMapping() will fail if they specify anobject name that is in use by an object of another type. In both cases,GetLastError() will return ERROR_INVALID_HANDLE (6).
To avoid conflicts between object types, one solution is to includethe object type in the name. For example, use "EV_myapp_block_ready" for anevent object name and "FM_myapp_missile_data" for a file mapping objectname.
Necessity of Unmapping All Views of a Mapped File
Windows maintains an internal handle to a file mapping object for eachview of that object, whether created by MapViewOfFile() orMapViewOfFileEx(). This internal handle is kept in addition to the handlereturned by CreateFileMapping(). The internal handle is not closed untilthe view associated with the handle is unmapped by callingUnmapViewOfFile(). To completely close a file mapping object requires thatall handles for the object, including internal handles, be closed. Thus, toclose a file mapping object, all views of that object must be unmapped, andthe handle returned by CreateFileMapping() must be closed.
Extant unmapped views of a file mapping object will NOT cause aCloseHandle() on the object's handle to fail. In other words, when yourhandle to the object is closed successfully, it is not necessarily truethat all views have been unmapped, so the file mapping object has notnecessarily been freed.
Failure to properly unmap all views of the object and to close the handleto the object will cause leaks in the application's paged pool,nonpaged pool, virtual bytes, and also in the system wide committed bytes.
Restrictions on the Size of File Mapping Objects
The size of a file mapping object backed by the system paging file islimited to available system virtual memory (meaning the amount of memorythat could be committed with a call to VirtualAlloc()).
On Windows NT, the size of a file mapping object backed by a named diskfile is limited by available disk space. The size of a mapped view ofan object is limited to the largest contiguous block of unreservedvirtual memory in the process performing the mapping (at most, 2GB minusthe virtual memory already reserved by the process).
On Win32s, the size of a file mapping object backed by a named diskfile is limited to available system virtual memory, due to the virtualmemory management implementation of Win32s. Win32s allocates regularvirtual memory for the memory mapped section even though it does notneed swap space, and the amount of VM set by Windows is too small to usefor mapping large files. As with Windows NT, available disk space willalso impose a limitation.
On Windows 95, the size of a file mapping object backed by a named diskfile is limited to available disk space. The size of a mapped view ofan object is limited to the largest contiguous block of unreservedvirtual memory in the shared virtual arena. This block will be at most1GB, minus any memory in use by other components of Windows 95 which usethe shared virtual arena (such as 16-bit Windows-based applications). Eachmapped view will use memory from this arena, so this limit applies to thetotal size of all non-overlapping mapped views for all applications runningon the system.
Mapped File May Not be Automatically Grown
If the size specified for a file mapping object backed by a named diskfile in a call to CreateFileMapping() is larger than the size of thefile used to back the mapping, the file will normally be grown to thespecified size by the CreateFileMapping() call.
On Windows NT only, if PAGE_WRITECOPY is specified for the fdwProtectparameter, the file will not automatically be grown. This will causeCreateFileMapping() to fail, and GetLastError() will returnERROR_NOT_ENOUGH_MEMORY (8). To set the size of the file before callingCreateFileMapping(), use SetFilePointer() and SetEndOfFile().
MapViewOfFileEx() and Valid Range of lpvBase
On Windows NT, views of file mapping objects are mapped in the addressrange of 0-2 GB. Passing an address outside of this range as thelpvBase parameter to MapViewOfFileEx() will cause it to fail, andGetLastError() will return ERROR_INVALID_PARAMETER (87).
On Windows 95, views of file mapping objects are mapped in the addressrange of 2-3 GB (the shared virtual arena). Passing an address outside ofthis range will cause MapViewOfFileEx() to fail, and GetLastError() willreturn ERROR_INVALID_ADDRESS (487). Note that future updates to Windows 95may change the mapping range to 0-2 GB, as on Windows NT.
MapViewOfFileEx() and Allocation Status of lpvBase
If an address is specified for the lpvBase parameter of MapViewOfFileEx(),and there is not a block of unreserved virtual address space at thataddress large enough to satisfy the number of bytes specified in thecbMap parameter, then MapViewOfFileEx() will fail, and GetLastError() willreturn ERROR_NOT_ENOUGH_MEMORY (8). This does not mean that the system islow on memory or that the process cannot allocate more memory. It simplymeans that the virtual address range requested has already been reservedin that process.
Prior to calling MapViewOfFileEx(), VirtualQuery() can be used to determinean appropriate range of unreserved virtual address space.
MapViewOfFileEx() and Granularity of lpvBase
For the lpvBase parameter specified in a call to MapViewOfFileEx(), youshould use an integral multiple of the system's allocation granularity. OnWindows NT, not specifying such a value will cause MapViewOfFileEx() tofail, and GetLastError() to return ERROR_MAPPED_ALIGNMENT (1132). OnWindows 95, the address is rounded down to the nearest integral multiple ofthe system's allocation granularity.
To determine the system's allocation granularity, call GetSystemInfo().
Addresses of Mapped Views
When mapping a view of a file (or shared memory), it is possible to eitherlet the operating system determine the address of the view, or to specifyan address as the lpvBase parameter of the MapViewOfFileEx() function. Ifthe file mapping is going to be shared among multiple processes, thenthe recommended method is to use MapViewOfFile() and let the operatingsystem select the mapping address for you. There are good reasons for doingso:
- On Windows NT, views are mapped independently into each process's address space. While it may be convenient to try to map the view at the same address in each process, the specified virtual address range may not be free in all of the processes involved. Therefore, the mapping could fail in one (or more) of the processes trying to share the file mapping.
- On Windows 95, file mapping objects exist in the 2-3 GB address range (the shared virtual arena). Therefore, once the initial address for the view is determined, additional views of the mapping will be mapped to the same address in each process anyway, and there is no benefit in trying to force the initial mapping to a specific address. For the second and subsequent views of a mapping object, if the address specified for lpvBase does not match the actual address where Windows 95 has mapped the view, then MapViewOfFileEx() fails, and GetLastError() returns ERROR_INVALID_ADDRESS (487). Additionally, when attempting to map the first view at a pre-determined address, that address may already be in use by other components of Windows 95 which use the shared virtual arena. Note that future updates to Windows 95 may change the mapping range to 0-2 GB, as on Windows NT.
If it is absolutely necessary to create the mappings at the same addressin multiple processes under Windows NT, here are two possible approaches:
- Pick an appropriate address and manage the virtual address space so that this address is left available. This means basing your DLLs, allocating memory at specific locations, and using a tool such as Process Walker to observe the virtual address space pattern. As soon as possible in the execution of the application, either reserve the desired address space or perform the mapping. One good place to do this is in the PROCESS_ATTACH handling in a DLL, because it is called before the executable itself is started. NOTE: There is still no guarantee that some DLL will not have already loaded at the address in question. If not all involved processes can map at the predetermined address, they can either fail or try a new address.
- Have all processes involved negotiate an appropriate address. The processes can all use the VirtualQuery() function to scan their address spaces until a common address is found in each process that has a large enough unreserved block. This requires that all processes involved map the address at the same time. A process that starts after the address has been determined must map at that address, and fail if it cannot do so. Alternatively, the negotiation process could be repeated, with each process remapping at the new address. Then, all pointers into the mapping must be readjusted.
The second method is far more likely to succeed. It can also be combinedwith the first to make it more likely that an appropriate address will befound quickly.
When views are mapped to different addresses under Windows NT, thedifficulty that arises is storing pointers to the mapping within themapping itself. This is because a pointer in one process does not pointto the same location within the mapping in another process. To overcomethis problem, store offsets rather than pointers in the mapping, andcalculate actual addresses in each process by adding the base address ofthe mapping to the offset. It is also possible to used based pointers andthus perform the base + offset conversion implicitly. A short SDK samplecalled BPOINTER demonstrates this technique.
Additional Platform Differences
Additional limitations when performing file mapping under Windows 95:
- The dwOffsetHigh parameters of MapViewOfFile() and MapViewOfFileEx() are not used, and should be zero. Windows 95 uses a 32-bit file system.
- The dwMaximumSizeHigh parameter of CreateFileMapping() is not used, and should be zero. Again, this is due to the 32-bit file system.
- The SEC_IMAGE and SEC_NOCACHE flags for the fdwProtect parameter of CreateFileMapping() are not supported.
- If the FILE_MAP_COPY flag is used to map a view of a file mapping object, the object must have been created using PAGE_WRITECOPY protection. Additionally, the object must be backed by a named file rather than the system paging file (in other words, a valid file handle, not (HANDLE)0xFFFFFFFF, must be specified for the hFile parameter of CreateFileMapping()). Failure to do either of these causes MapViewOfFile() to fail, and GetLastError() to return ERROR_INVALID_PARAMETER (87).
- If two or more processes map a PAGE_WRITECOPY view of the same file mapping object (by using a named object, for example), they are able to see changes made to the view by the other process(es). The actual disk file is not modified, however. Under Windows NT, if one process writes to the view, it receives its own copy of the modified pages and will not affect the pages in the other process(es) or the disk file.