Memory-Mapped Files Backed by the Paging File

[Previous] [Next]

So far I've discussed techniques that allow you to map a view of a file that resides on a disk drive. Many applications create some data while they run and need to transfer the data or share it with another process. It would be terribly inconvenient if the applications had to create a data file on a disk drive and store the data there in order to share it.

Microsoft realized this and added the ability to create memory-mapped files that are backed by the system's paging file rather than a dedicated hard disk file. This method is almost identical to the method for creating a memory-mapped disk file except that it's even easier. For one thing, there is no need to call CreateFile since you will not be creating or opening a dedicated file. Instead, you simply call CreateFileMapping as you would normally and pass INVALID_HANDLE_VALUE as the hFile parameter. This tells the system that you are not creating a file-mapping object whose physical storage resides in a file on the disk; instead, you want the system to commit physical storage from the system's paging file. The amount of storage allocated is determined by CreateFileMapping's dwMaximumSizeHigh and dwMaximumSizeLow parameters.

After you have created this file-mapping object and mapped a view of it into your process's address space, you can use it as you would any region of memory. If you want to share this data with other processes, call CreateFileMapping and pass a zero-terminated string as the pszName parameter. Then other processes that want to access the storage can call CreateFileMapping or OpenFileMapping and pass the same name.

When a process no longer needs access to the file-mapping object, that process should call CloseHandle. When all the handles are closed, the system will reclaim the committed storage from the system's paging file.

NOTE
Here is an interesting problem that has caught unsuspecting programmers by surprise. Can you guess what is wrong with the following code fragment?

 HANDLE hFile = CreateFile(...); HANDLE hMap = CreateFileMapping(hFile, ...); if (hMap == NULL) return(GetLastError());  

If the call to CreateFile above fails, it returns INVALID_HANDLE_VALUE. However, the unsuspecting programmer who wrote this code didn't test to check whether the file was created successfully. When CreateFileMapping is called, INVALID_HANDLE_ VALUE is passed in the hFile parameter, which causes the system to create a file mapping using storage from the paging file instead of the intended disk file. Any additional code that uses the memory-mapped file will work correctly. However, when the file-mapping object is destroyed, all the data that was written to the file-mapping storage (the paging file) will be destroyed by the system. At this point, the developer sits and scratches his or her head, wondering what went wrong! You must always check CreateFile's return value to see if an error occurred because CreateFile can fail for so many reasons!

The Memory-Mapped File Sharing Sample Application

The MMFShare application ("17 MMFShare.exe") listed in Figure 17-3 demonstrates how to use memory-mapped files to transfer data among two or more separate processes. The source code and resource files for the application are in the 17-MMFShare directory on the companion CD-ROM.

You're going to need to execute at least two instances of MMFShare. Each instance creates its own dialog box, shown here.

To transfer data from one instance of MMFShare to another, type the data to be transferred into the Data edit box. Then click on the Create Mapping Of Data button. When you do, MMFShare calls CreateFileMapping to create a 4-KB memory-mapped file object backed by the system's paging file and names the object MMFSharedData. If MMFShare sees that a file-mapping object with this name already exists, it displays a message box notifying you that it could not create the object. If, on the other hand, MMFShare succeeds in creating the object, it proceeds to map a view of the file into the process's address space and copies the data from the edit control into the memory-mapped file.

After the data has been copied, MMFShare unmaps the view of the file, disables the Create Mapping Of Data button, and enables the Close Mapping Of Data button. At this point, a memory-mapped file named MMFSharedData is just sitting somewhere in the system. No processes have mapped a view to the data contained in the file.

If you now go to another instance of MMFShare and click on this instance's Open Mapping And Get Data button, MMFShare attempts to locate a file-mapping object called MMFSharedData by calling OpenFileMapping. If an object of this name cannot be found, MMFShare notifies you by displaying another message box. If MMFShare finds the object, it maps a view of the object into its process's address space, copies the data from the memory-mapped file into the edit control of the dialog box, and unmaps and closes the file-mapping object. Voilà! You have transferred data from one process to another.

The Close Mapping Of Data button in the dialog box is used to close the file-mapping object, which frees up the storage in the paging file. If no file-mapping object exists, no other instance of MMFShare will be able to open one and get data from it. Also, if one instance has created a memory-mapped file, no other instance is allowed to create one and overwrite the data contained within the file.

Figure 17-3 The MMFShare application

MMFShare.cpp

 /****************************************************************************** Module: MMFShare.cpp Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ #include "..\CmnHdr.h" /* See Appendix A. */ #include <windowsx.h> #include <tchar.h> #include "Resource.h" /////////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { chSETDLGICONS(hwnd, IDI_MMFSHARE); // Initialize the edit control with some test data. Edit_SetText(GetDlgItem(hwnd, IDC_DATA), TEXT("Some test data")); // Disable the Close button because the file can't // be closed if it was never created or opened. Button_Enable(GetDlgItem(hwnd, IDC_CLOSEFILE), FALSE); return(TRUE); } /////////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { // Handle of the open memory-mapped file static HANDLE s_hFileMap = NULL; switch (id) { case IDCANCEL: EndDialog(hwnd, id); break; case IDC_CREATEFILE: if (codeNotify != BN_CLICKED) break; // Create a paging file-backed MMF to contain the edit control text. // The MMF is 4 KB at most and is named MMFSharedData. s_hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4 * 1024, TEXT("MMFSharedData")); if (s_hFileMap != NULL) { if (GetLastError() == ERROR_ALREADY_EXISTS) { chMB("Mapping already exists - not created."); CloseHandle(s_hFileMap); } else { // File mapping created successfully. // Map a view of the file into the address space. PVOID pView = MapViewOfFile(s_hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (pView != NULL) { // Put edit text into the MMF. Edit_GetText(GetDlgItem(hwnd, IDC_DATA), (LPTSTR) pView, 4 * 1024); // Protect the MMF storage by unmapping it. UnmapViewOfFile(pView); // The user can't create another file right now. Button_Enable(hwndCtl, FALSE); // The user closed the file. Button_Enable(GetDlgItem(hwnd, IDC_CLOSEFILE), TRUE); } else { chMB("Can't map view of file."); } } } else { chMB("Can't create file mapping."); } break; case IDC_CLOSEFILE: if (codeNotify != BN_CLICKED) break; if (CloseHandle(s_hFileMap)) { // User closed the file; fix up the buttons. Button_Enable(GetDlgItem(hwnd, IDC_CREATEFILE), TRUE); Button_Enable(hwndCtl, FALSE); } break; case IDC_OPENFILE: if (codeNotify != BN_CLICKED) break; // See if a memory-mapped file named MMFSharedData already exists. HANDLE hFileMapT = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, TEXT("MMFSharedData")); if (hFileMapT != NULL) { // The MMF does exist; map it into the process's address space. PVOID pView = MapViewOfFile(hFileMapT, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (pView != NULL) { // Put the contents of the MMF into the edit control. Edit_SetText(GetDlgItem(hwnd, IDC_DATA), (LPTSTR) pView); UnmapViewOfFile(pView); } else { chMB("Can't map view."); } CloseHandle(hFileMapT); } else { chMB("Can't open mapping."); } break; } } /////////////////////////////////////////////////////////////////////////////// INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); } return(FALSE); } /////////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) { DialogBox(hinstExe, MAKEINTRESOURCE(IDD_MMFSHARE), NULL, Dlg_Proc); return(0); } //////////////////////////////// End of File////////////////////////////////// 

MMFShare.rc

 //Microsoft Developer Studio generated resource script. // #include "Resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_MMFSHARE DIALOG DISCARDABLE 38, 36, 186, 61 STYLE WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Memory-Mapped File Sharing" FONT 8, "MS Sans Serif" BEGIN PUSHBUTTON "&Create mapping of Data",IDC_CREATEFILE,4,4,84,14, WS_GROUP PUSHBUTTON "&Close mapping of Data",IDC_CLOSEFILE,96,4,84,14 LTEXT "&Data:",IDC_STATIC,4,26,18,8 EDITTEXT IDC_DATA,28,24,153,12 PUSHBUTTON "&Open mapping and get Data",IDC_OPENFILE,40,44,104,14, WS_GROUP END ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_MMFSHARE ICON DISCARDABLE "MMFShare.Ico" #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "Resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED 



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net