Remember to call CloseHandle on the file and mapping object handles when you are done with them. In fact, since the MapViewOfFile function increases the usage counts on the file object and mapping object that it requires, you can close these handles after calling MapViewOfFile and the objects will be destroyed when the call to UnmapViewOfFile decreases their usage counts to zero. This is a perfectly legal and convenient way of using a memory mapped file:
HANDLE hFile, hMapping;
LPVOID lpBaseAddress;
// map in the file...
hFile = CreateFile( "somefile.dat", ...);
hMapping = CreateFileMapping( hFile, ...);
lpBaseAddress = MapViewOfFile( hMapping, ...);
// close our handles, MapViewOfFile has incremented
// the usage count on the objects...
CloseHandle(hFile);
CloseHandle(hMapping);
// use the lpBaseAddress pointer to manipulate the
// file mapped into memory...
...
// all done, unmap the file which decreases the usage
// counts on the file and mapping objects to zero, causing
// the objects to be destroyed...
UnmapViewOfFile(lpBaseAddress);
This works perfectly under Win95. However, this does not work under WinNT with file mappings used for shared memory (the memory winds up not being shared). Therefore we recommend that you do not close the file handle until you are done using the mapped memory.
To make this all clear, Example 5-1 is a simple program that uses shared memory to pass a string among several instances of a running program. Bring up several command line windows and execute this program in each window. A string typed in one running instance of the program will be visible in all other instances. Because this is a simple example of using shared memory, there is no synchronization between processes. In a real application, you would use a named kernel object such as a mutex to synchronize access to the shared memory buffer.
Example 5-1. ShareStr.c, using shared memory to share a string
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#define MAX_STRING_SIZE 256
#define FILE_MAPPING_NAME __TEXT("ShareStrSharedMemory")
#define INITIAL_STRING __TEXT("(nothing yet)")
int main( int argc, char *argv[]) {
HANDLE hMapping;
LPTSTR lpSharedString;
TCHAR szLocalString[MAX_STRING_SIZE];
BOOL bCreated;
// create a named file mapping as shared memory...
hMapping = CreateFileMapping( (HANDLE) 0xFFFFFFFF, NULL, PAGE_READWRITE, 0,
MAX_STRING_SIZE, FILE_MAPPING_NAME);
if (hMapping != NULL) {
if (GetLastError() == ERROR_ALREADY_EXISTS) {
bCreated = FALSE;
printf("Opened preexisting file mapping.\n");
}
else {
bCreated = TRUE;
printf("Created file mapping.\n");
}
}
else {
printf("Unable to create file mapping, exiting!\n");
exit(-1);
}
// map the memory into this process...
lpSharedString = (LPTSTR) MapViewOfFile( hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (lpSharedString == NULL) {
printf("Unable to map into memory, exiting!\n");
exit(-1);
}
// initialize the string if necessary...
if (bCreated)
_tcscpy( lpSharedString, INITIAL_STRING);
while (TRUE) {
printf( "Type a string to share, [Enter] to display current string, or \"quit\":\n");
// input string...
_getts( szLocalString);
if (_tcscmp( szLocalString, __TEXT("quit")) == 0) {
// quit...
break;
}
else if (szLocalString[0] == __TEXT('\0')) {
// show the string...
printf( "Current string is '%s'.\n", lpSharedString);
}
else {
// set the string...
_tcscpy( lpSharedString, szLocalString);
}
}
// unmap the memory...
UnmapViewOfFile(lpSharedString);
// close our handle to the file mapping object...
CloseHandle(hMapping);
// exit...
return 0;
}
The program starts off creating a file mapping named "ShareStrSharedMemory", using the system swap file for backing store. The program calls GetLastError to check if we created the file mapping object or just opened one that was already in existence. Then the object is mapped into memory and we save the pointer in
lpSharedString. The file mapping object can then be closed, since the mapped string incremented its usage count.If this process created, as opposed to opened, the file mapping object, we copy an initial value into the shared memory. Then the program loops, checking the input string to determine which action to take. If the input string is "quit", the loop exits and the memory is unmapped. If the input is an empty string, the contents of the shared memory are printed. Any other input results in the shared buffer being updated with new data.
Each running instance of the program reads and writes into the same memory mapped file buffer. Other programs could also map the shared buffer by creating a file mapping using the same name string. This is the most general way of sharing memory between programs.