MobileUsing Memory-Mapped Files On CE Devices

Using Memory-Mapped Files On CE Devices

In the last lesson, we saw how to read and write files under CE using techniques more or less equivalent to those we use in disk-based, desktop computing style file access. (The example program for that lesson was called GreatWriting.) If you are handling larger files than the ones in the great writers example, memory mapped files offer you some advantages.

In a nutshell, when a file is mapped into memory, you can treat the content of the file as writeable array of bytes. Any changes to the region in the mapped memory are automatically mirrored to the file. This means you can handle the file content directly, without calling ReadFile() and WriteFile(), and without specifically allocating memory to buffer file content.

Memory-mapped regions of the file are moveable. This means you can create a mapped region that is smaller than the file. By changing the mapping parameters, you can operate on one region of the file content, then remap and operate on another region. File maps are created in the system’s address space, and so don’t expand the local heap. We’ll explore how to set up and read from a memory-mapped file in the MemMapFiles example. Writing is to memory mapped files is a trivial extension of this example, but before Windows CE 2.1, it was unsupported. For this reason, we confine the example to behavior that will work on all CE platforms.

Here’s how the MemMapFiles example program looks running on an HPC.

Figure 1 TheMemMapFiles Example

The table in Figure 2 shows the content of the file we’ll be mapping. It consists of three text strings, each null delimited and padded to an even 20 bytes in length.

Figure 2: //MsAmericasWishList.txt: This file gets mapped into a region of memory

Null Delimited Strings, Each 20 Bytes in Length
World Peace
End Of Hunger
Waterproof Mascara

We’ll display the strings as choices in a dialog box, so we’ll do all the set up and access of the memory mapped file in the dialog procedure, MsDlgProc( ). Notice first that we declare a number of static variables.

BOOL CALLBACK MsDlgProc(HWND hDlg,
                        UINT message,
                        WPARAM wParam,
                        LPARAM lParam)
{
    static HANDLE hMappedFile;
    static HANDLE hFileMap;
    static CHAR*  pFileMemory;
    static int    iOffset;

    TCHAR  tszWish[24];

The variables hMappedFile, hFileMap, and pFileMemory are the handle to mapped file, the handle to the mapping object, and the pointer to the region of memory that mirrors the file content, respectively. These retain their value across invocations of the dialog function.

Opening a memory-mapped file and setting up the mapped region is a three-step process. You create a memory-mapped file, create a mapping object to manage and co-ordinate the stored and memory-resident file content, and retrieve a pointer to the memory region in which you use to access file data.

//create a file for mapping
hMappedFile = CreateFileForMapping(
     TEXT("My DocumentsMsAmericasWishList.txt"),
          GENERIC_READ ,
          FILE_SHARE_READ, NULL, OPEN_EXISTING, 
          FILE_ATTRIBUTE_NORMAL, NULL);

if( hMappedFile != INVALID_HANDLE_VALUE )
{
   //create a file mapping object
   hFileMap = CreateFileMapping(
                    hMappedFile, 
                    NULL,
                    PAGE_READONLY,
                    0,0,0);
   if( hFileMap )
   {
      pFileMemory = (CHAR*)MapViewOfFile(
                           hFileMap,
                           FILE_MAP_READ,
                           0,0,0 );

CreateFileForMapping() looks almost exactly like CreateFile(), and in general the only difference is that for Windows CE versions before 2.1, the flags that specify write access to the file are unsupported. The parameters to CreateFileMapping() are, in the order shown the handle to the mapped file, a NULL placeholder for the unsupported mapping attributes parameter, protection to be applied to the committed pages of the mapped view of the file, the high order DWORD of the mapped region’s size, the loworder DWORD of the mapped region’s size, and a pointer to a name for the mapping object created by this call. Setting the size parameters causes the entire file to be mapped into memory, and setting the pointer to the object name to zero creates the mapping object without a name.

MapViewOfFile() returns a pointer to the file’s mapped range of memory. The parameters to MapViewOfFile() are, in the order shown, the handle to the mapping object, the requested access to the object, the high order DWORD of the file offset where the mapping begins, the loworder DWORD of the file offset where the mapping begins, and the number of bytes to map. If the last three parameters are set to zero, the entire file is mapped into view. If you want to map a smaller region, you need to make sure that parameters that set the offset into the file are an even multiple of the system allocation granularity. Use GetSystemInfo() to find this value. The allocation granularity is returned in the SYSTEM_INFO structure’s dwAllocationGranularity member.

Once you have a pointer to the mapped region, file access is really very streamlined. Here’s how we load strings into the edit control using mapped file access.

case IDC_NEXT:
   iOffset += 20;
   iOffset = ( iOffset >= 60 )? 0: iOffset;
   //update edit control
   //with next wish list item
   memset( &tszWish[0], 0x0, sizeof(tszWish ));
   mbstowcs( (LPTSTR)&tszWish, (CHAR*)pFileMemory + iOffset,
           (size_t)strlen(pFileMemory) );
   SetDlgItemText(hDlg, IDC_WISH, (LPCTSTR)&tszWish);
break;

If you want to change the region of the file that is mapped, first you unmap the current region, and then call MapViewOf File() for the new region of the file.

UnmapViewOfFile(pFileMemory);

To exit, we clean up the objects by unmapping the view and closing handles to the mapping object and mapped file.

case IDCANCEL:
   //clean up
   UnmapViewOfFile(pFileMemory);
   CloseHandle(hFileMap);
   CloseHandle(hMappedFile);
   EndDialog(hDlg, LOWORD(wParam));
   return TRUE;

Wrapping Up:

Memory-mapped files are an extremely useful tool on CE devices, largely because they correspond much more closely to the physical realities of the environment than the stream-based file routines of the desktop. In our next installment, we are going to shift our examination from files themselves to the things they contain: data.

You’ve seen that one of the biggest porting challenges for CE developers is data formats, but so far these challenges have mostly had to do with differences in text representation. There is another gotcha that’s somewhat more rare but potentially more devastating. Not all CE devices can do floating-point math. In our next lesson, we’ll develop a tool for providing simple floating-point capabilities on integer math devices.

Downloads

Source Code: MemMapFiles.zip – 4 kb

About the Author

Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, and entry time data validation.

In addition to writing for Developer.com, she has written several books, including Making Win 32 Applications Mobile.

# # #

Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.
Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.

Latest Posts

Related Stories