Getting and Setting File Information

The Windows CE API contains several functions that are useful for retrieving file information. For example, you can find out when a file was last modified, how its attribute bits are currently set, and the size of the file. The following sections detail the different capabilities that are available. Several of these functions require an open file handle rather than the file's name.

Getting the File Times

The GetFileTime function retrieves three different pieces of time information from an open file: the Creation time, the Last Access time, and the Last Write time.

Table 2.4. GetFileTime—Gets file time information
GetFileTime 
HANDLE fileHandle to a file from CreateFile
LPFILETIME creationTimeTime of file creation
LPFILETIME lastAccessTimeTime of last file access
LPFILETIME lastWriteTimeTime of last file write
BOOL Return ValueReturns TRUE on success, otherwise FALSE

In Listing 2.3, the CreateFile function opens the requested file name. GetFileTime uses the handle that it returns to access the file times, and then passes the last write time up to the ShowTime function to dump the time to cout.

Listing 2.3. Displays the file times associated with the given file
void ShowTime(FILETIME t)
{
  FILETIME ft;
  SYSTEMTIME st;
  FileTimeToLocalFileTime(&t, &ft);
  FileTimeToSystemTime(&ft, &st);
  cout ≪ st.wMonth ≪ _T("/") ≪ st.wDay
            ≪ _T("/") ≪ st.wYear ≪ _T(" ") ≪ st.wHour
            ≪ _T(":") ≪ st.wMinute ≪ endl;
}
void Listing2_3()
{
  HANDLE hFile;
  TCHAR szFilename[MAX_PATH + 1];
  FILETIME ftCreate, ftLastWrite, ftLastAccess;
  if(!GetFilename(_T("Enter filename:"),
      szFilename, MAX_PATH))
    return;
  hFile = CreateFile(szFilename,
      GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
  if(hFile == INVALID_HANDLE_VALUE)
  {
    cout  ≪ _T("Could not open file. Error:")
          ≪ GetLastError();
    return;
  }
  if(GetFileTime(hFile, &ftCreate,
    &ftLastWrite, &ftLastAccess))
  {
    cout ≪ _T("Create time: ");
    ShowTime(ftCreate);
    cout ≪ _T("Last write time: ");
    ShowTime(ftLastWrite);
    cout ≪ _T("Last Access time: ");
    ShowTime(ftLastAccess);
  }
  else
    cout ≪ _T("Could not file times. Error: ")
         ≪ GetLastError();
  CloseHandle(hFile);
}

FILETIME is a structure that contains two 32-bit values. The 64 bits together represent the number of 100-nanosecond time increments that have passed since January1, 1601. The FileTimeToLocalTime and FileTimeToSystemTime functions convert the 64-bit value to local time and then to a form suitable for output. The times returned by GetFileTime are in UTC (Universal Coordinated Time, otherwise known as Greenwich Mean Time or GMT), and so should be converted to local time when displayed to users.

The function SetFileTime can be used to set one or all of the three file times. Note that when changing just one of the times on an object store file, the other two file times are updated by default. This behavior does not occur with FAT files.

Getting File Size

The GetFileSize function returns the size of the file in bytes, or 0xFFFFFFFF on error. The file size returned is the uncompressed file size—files in the object store are automatically compressed. In the Object Store the largest file size possible can be represented in less than 32 bits, but NTFS (which you may connect to through the network) is a 64-bit file system. GetFileSize therefore returns 64 bits of size information if you request it. There is currently no easy way to deal with integers larger than 32bits.

Table 2.5. GetFileSize—Returns a 64-bit size value for the file
GetFileSize 
HANDLE fileHandle to a file from CreateFile
LPDWORD fileSizeHighPointer to a DWORD that returns the high-order 32 bits of size
Return ValueReturns the low-order 32 bits of the file size, or 0xFFFFFFFF on failure

The low-order 32 bits of size information comes from the return value, while the high-order 32 bits come from the fileSizeHigh parameter when you pass in a pointer to a DWORD. You can also pass in NULL for this parameter if you are not interested in receiving the high-order 32 bits of information. Listing 2.4 shows how to access the information.

Listing 2.4. Reports size of file in bytes
void Listing2_4()
{
  HANDLE hFile;
  TCHAR szFilename[MAX_PATH + 1];
  DWORD dwSizeLo, dwSizeHi;
  if(!GetFilename(_T("Enter filename:"),
      szFilename, MAX_PATH))
    return;
  hFile = CreateFile(szFilename, GENERIC_READ,
      0, 0, OPEN_EXISTING, 0, 0);
  if(hFile == INVALID_HANDLE_VALUE)
  {
    cout ≪ _T("Could not open file. Error:")
         ≪ GetLastError();
    return;
  }
 
  dwSizeLo = GetFileSize (hFile, &dwSizeHi);
  if(dwSizeLo == 0xFFFFFFFF && GetLastError()
            != NO_ERROR)
    cout  ≪ _T("Error getting file size: ")
          ≪ GetLastError();
  else
    cout  ≪ _T("Filesize (Low, High) : ") ≪ dwSizeLo ≪
_T(",") ≪ dwSizeHi;
  CloseHandle(hFile);
}

Getting File Attributes

Files have associated with them attribute bits that hold special information about the file. You can view most of the attributes from the Explorer by selecting a file and then choosing the Properties option in the File menu. Inside a program you can examine attribute bits with the GetFileAttributes function.

Table 2.6. GetFileAttributes—Gets the attribute bits for a file
GetFileAttributes 
LPTSTR fileNameThe name of the file
Return ValueReturns the attribute bits as a DWORD, or 0xFFFFFFFF on error

Listing 2.5 demonstrates how to acquire and examine the attribute bits. The system returns not only the four standard bits seen in the Explorer (archive, read only, system, and hidden), but also bits indicating that the file name is actually a directory, as well as In-ROM and related attributes. Note that not all the available attributes are listed in the code sample.

Listing 2.5. Reports file attributes
void ShowAttributes(DWORD dwAttributes)
{
  if(dwAttributes & FILE_ATTRIBUTE_READONLY)
    cout ≪ _T("Read only") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_HIDDEN)
    cout ≪ _T("Hidden") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_SYSTEM)
    cout ≪ _T("System") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
    cout ≪ _T("Directory") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_ARCHIVE)
    cout ≪ _T("Archive") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_INROM)
    cout ≪ _T("In ROM") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_NORMAL)
    cout ≪ _T("Normal") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_TEMPORARY)
    cout ≪ _T("Temporary") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_COMPRESSED)
    cout ≪ _T("Compressed") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_ROMSTATICREF)
    cout ≪ _T("ROM Static Ref") ≪ endl;
  if(dwAttributes & FILE_ATTRIBUTE_ROMMODULE)
    cout ≪ _T("ROM Module") ≪ endl;
}
void Listing2_5()
{
  TCHAR szFilename[MAX_PATH + 1];
  DWORD dwAttributes;
  if(!GetFilename(_T("Enter filename:"),
      szFilename, MAX_PATH))
    return;
  dwAttributes = GetFileAttributes(szFilename);
  ShowAttributes(dwAttributes);
}

It is also possible to set some file attributes using the SetFileAttributes function. This function accepts a file name and one or more attribute constants, and returns a Boolean value indicating success or failure.

Table 2.7. SetFileAttributes—Sets file attributes
SetFileAttributes 
LPTSTR filenameThe name of the file
DWORD attributesAttributes as for GetFileAttributes
Return ValueReturns TRUE on success, otherwise FALSE

The same attribute constants seen in the ShowAttributes function of Listing 2.5 are available. For example, you might set a file as hidden and read-only with the following statement:

success = SetFileAttributes(_T("xxx"),
  FILE_ATTRIBUTE_HIDDEN |
  FILE_ATTRIBUTE_READONLY);

Generally those are the only two attributes you will want to set. The other bits, for example the directory bit, are set automatically by system calls when they are appropriate and should not be altered. File attributes can be set when the file is created using CreateFile. Table 2.8 shows the Windows CE file attributes; indicates whether they can be accessed using GetFileAttributes, SetFileAttributes, and CreateFile; and gives a brief definition.

Table 2.8. File Attributes and Their Purposes
AttributePurpose
FILE_ATTRIBUTE_ARCHIVEFile has been archived or backed up.
FILE_ATTRIBUTE_COMPRESSEDFile is stored in compressed format.
FILE_ATTRIBUTE_DIRECTORYFile is a directory.
FILE_ATTRIBUTE_ENCRYPTEDFile is encrypted.
FILE_ATTRIBUTE_HIDDENFile is hidden and not included in normal directory listings.
FILE_ATTRIBUTE_INROMFile is located in ROM. It is read-only and cannot be modified.
FILE_ATTRIBUTE_NORMALNormal file, has no other attributes.
FILE_ATTRIBUTE_OFFLINEFile contents not currently available.
FILE_ATTRIBUTE_READONLYFile is read-only.
FILE_ATTRIBUTE_REPARSE_POINTThe file has an associated reparse point.
FILE_ATTRIBUTE_ROMMODULEDLL or EXE in ROM. CreateFile cannot be used to access these files.
FILE_ATTRIBUTE_SPARSE_FILEEmpty spaces in a file are not stored.
FILE_ATTRIBUTE_SYSTEMFile is part of the system file set.
FILE_ATTRIBUTE_TEMPORARYTemporary file, will be deleted.
FILE_FLAG_WRITE_THROUGHNo buffering for file I/O.
FILE_FLAG_RANDOM_ACCESSOpen optimized for random access.
FILE_FLAG_SEQUENTIAL_SCANOpen optimized for sequential option.
FILE_ATTRIBUTE_ROMSTATICREFModule is in ROM and contains static references to other modules. It cannot be replaced (shadowed) with a file in RAM.

All files in the object store are compressed, and will have the FILE_ATTRIBUTE_COMPRESSED attribute. You cannot set this attribute to compress a file as you can with Windows NT and 2000.

Getting All File Information

The function GetFileInformationByHandle returns all of the information described in the previous three sections in one call. It is useful when you want to access or display all information about a file in one call.

Table 2.9. GetFileInformationByHandle—Retrieves all file information
GetFileInformationByHandle 
HANDLE fileHandle to an open file from CreateFile
LPBY_HANDLE_FILE_INFORMATIONInformation about the file
Return ValueReturns TRUE on success, otherwise FALSE

The information comes back in a structure that contains the attributes, size, and time data discussed in the previous sections, along with volume, index, and link information not available anywhere else. The volume serial number is a unique number assigned to the volume when it was formatted. The file index is a unique identifier attached to the file while it is open. Listing 2.6 demonstrates the process.

Listing 2.6. Lists all information for a given file
void Listing2_6()
{
  HANDLE hFile;
  TCHAR szFilename[MAX_PATH + 1];
  BY_HANDLE_FILE_INFORMATION fiInfo;
  if(!GetFilename(_T("Enter filename:"),
      szFilename, MAX_PATH))
    return;
  hFile = CreateFile(szFilename, GENERIC_READ,
      0, 0, OPEN_EXISTING, 0, 0);
  if(hFile == INVALID_HANDLE_VALUE)
  {
    cout ≪ _T("Could not open file. Error:")
         ≪ GetLastError();
    return;
  }
  if(GetFileInformationByHandle(hFile, &fiInfo))
  {
    ShowAttributes(fiInfo.dwFileAttributes);
    cout ≪ _T("Create time: ");
    ShowTime(fiInfo.ftCreationTime);
    cout ≪ _T("Last write time: ");
    ShowTime(fiInfo.ftLastWriteTime);
    cout ≪ _T("Last Access time: ");
    ShowTime(fiInfo.ftLastAccessTime);
    cout  ≪ _T("Volume serial number: ") ≪
fiInfo.dwVolumeSerialNumber ≪ endl;
    cout  ≪ _T("File size: ") ≪ fiInfo.nFileSizeLow ≪ endl;
    cout  ≪ _T("High index: ") ≪ fiInfo.nFileIndexHigh ≪ endl;
    cout  ≪ _T("Low index: ") ≪ fiInfo.nFileIndexLow ≪ endl;
    cout  ≪ _T("Object ID: ") ≪ fiInfo.dwOID ≪ endl;
  }
  CloseHandle(hFile);
}

File Operations

The API provides three functions for the common file operations of moving, copying, and deleting files. You can use these functions in your programs to duplicate the functionality of the command line equivalents.

The CopyFile function copies the source file to the destination file name. If an error occurs during the copy, GetLastError contains the error code.

Table 2.10. CopyFile—Copies a file
CopyFile 
LPTSTR sourceFileFile name for the source file.
LPTSTR destFileFile name for the destination.
BOOL existFailPassing TRUE causes the call to fail if the file exists. FALSE allows existing files to be overwritten.
BOOL Return ValueTRUE on success, otherwise FALSE.

The existFail parameter controls the behavior of the function when the destination file name already exists. If you set it to TRUE, then the function fails when the destination file name already exists. When set to FALSE, the function overwrites an existing file. This code fragment demonstrates the use of this function.

  success = CopyFile(sourceFilename,
      destFilename, TRUE);
  if (!success)
    cout ≪ _T("Error code = ") ≪ GetLastError();
  else
    cout ≪ _T("success
");

Files can be deleted using the DeleteFile function, which is passed the filename to be deleted (Table 2.11).

If the return value is FALSE, use the GetLastError function to retrieve the error code, as shown in this code fragment.

Table 2.11. DeleteFile—Deletes a file
DeleteFile 
LPTSTR fileNameFilename to delete
Return ValueReturns TRUE on success, FALSE on failure.

  success = DeleteFile(filename);
  if (success)
    cout ≪ _T("success
");
  else
    cout ≪ _T("Error number: ") " ≪ GetLastError();

File Reading and Writing

The section "Opening and Reading from a File" in this chapter briefly introduced simple file reading using CreateFile, ReadFile, and CloseHandle. In this section we will examine file seeking, reading, and writing in more detail, and look at the CreateFile function more carefully. The operations here are all synchronous, so they block (that is, do not return) until complete. Asynchronous file operations are not supported in Windows CE. Listing 2.7 demonstrates a file-write operation that writes structures to a new file.

Listing 2.7. Writes structures to a file
typedef struct
{
  int a, b, c;
} DATA;
void Listing2_7()
{
  HANDLE hFile;
  TCHAR szFilename[MAX_PATH + 1];
  BOOL bSuccess;
  DATA dataRec;
  int x;
  DWORD numWrite;
  if(!GetFilename(_T("Enter filename to create:"),
         szFilename, MAX_PATH, TRUE))
    return;
  cout ≪ szFilename;
  hFile = CreateFile(szFilename,
      GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);
  if(hFile == INVALID_HANDLE_VALUE)
  {
    cout  ≪ _T("Could not open file. Error:")
          ≪ GetLastError();
    return;
  }
  × = 0;
  do
  {
    dataRec.a = dataRec.b = dataRec.c = x;
    bSuccess = WriteFile(hFile, &dataRec,
         sizeof(dataRec), &numWrite, 0);
  }
  while(bSuccess && x++  10);
  CloseHandle(hFile);
}

The WriteFile function is similar to the ReadFile function, writing the specified number of bytes to disk. The function does not care what the bytes represent, so you can use it to write text or structures. In Listing 2.7, the program writes one structure's set of bytes in a single operation and repeats the operation ten times.

Table 2.12. WriteFile—Writes data to a file
WriteFile 
HANDLE fileHandleHandle to a file created by CreateFile
CONST VOID *bufferData to write
DWORD bytesToWriteThe number of bytes to write
LPDWORD bytesWrittenPointer to a DWORD that returns the number of bytes actually written
LPOVERLAPPED overlappedOverlapped structure (not supported, pass as NULL)
BOOL Return ValueTRUE for success, FALSE for failure

Listing 2.7 uses the CreateFile function in its simplest configuration. For example, in Listing 2.7 the GENERIC_WRITE constant indicates that we need write access to the file, and the CREATE_NEW constant indicates that the system should create a new file rather than overwrite an existing one (if the file name already exists, the function fails). However, CreateFile has many other capabilities.

When using the CreateFile function, you have control over several different properties:

  • The read and write mode

  • The way the file will be shared

  • A variety of attributes and performance hints

Table 2.13. CreateFile—Creates a new file or opens an existing file
CreateFile 
LPCTSTR nameName of the file to open
DWORD accessModeRead/Write mode
DWORD shareModeThe way the file should be shared
LPSECURITY_ATTRIBUTES securityAttributesAddress of a security structure (not supported, pass as NULL)
DWORD createThe way the file should be created
DWORD attributesSettings for normal file attribute bits
HANDLE templateFileFile containing extended attributes (not supported, pass as NULL)
HANDLE Return ValueReturns a handle to the file, or INVALID_HANDLE_VALUE on failure

The first parameter contains the name of the file to be opened. The function GetTempFileName can be used to obtain a valid temporary filename from the operating system. The second parameter passed to CreateFile controls read and write access. You can pass in any of the following three combinations:

Table 2.14. Read/write access control
ConstantPurpose
GENERIC_READRead only
GENERIC_WRITEWrite only
GENERIC_READ | GENERIC_WRITERead/write

Generally you use the third option when you plan to open a file of structures that you will read and modify simultaneously. You use GENERIC_READ when you want read-only access, and GENERIC_WRITE when you need write-only access.

The third parameter passed to CreateFile controls the share mode of the file. You control access to the entire file using this parameter. Four variations are possible (Table 2.15).

Table 2.15. Share mode options
ConstantPurpose
0Exclusive use of the file
FILE_SHARE_READRead-sharing of the file
FILE_SHARE_WRITEWrite-sharing of the file
FILE_SHARE_READ | FILE_SHARE_WRITERead/Write sharing

If you pass 0 to the shareMode parameter, then the entire file is locked while you have it open. Any other process attempting to open the file will receive a share violation. The remaining options grant increasing levels of access to other processes.

The Create parameter controls the failure behavior of CreateFile during creation. Any of the options in Table 2.16 may be used. If you create a new file with the same name as a file in ROM, the ROM file will be "shadowed." Your new file will replace the ROM file. If your file is deleted, the ROM file comes back into use.

Table 2.16. Create Parameters
ConstantPurpose
CREATE_NEWCreate a new file. Fails if file name exists.
CREATE_ALWAYSCreate a new file. Destroys any existing file.
OPEN_EXISTINGOpens an existing file. Fails if file not found.
OPEN_ALWAYSCreates a file if one does not exist, or opens the existing file.
TRUNCATE_EXISTINGDeletes the contents of the file if it exists. Fails if it does not exist.

The Attributes parameter lets you set the file attributes, and it also lets you tell the system your intended use of the file so that you can improve overall system performance. Table 2.17 shows all the available attributes and indicates which ones can be used in CreateFile, GetFileAttributes, and SetFileAttributes. Table 2.8 provides a description of the attributes. You can OR together nonconflicting combinations shown in Table 2.17 as needed in an application.

Many of the flag options are hints that you give to help the operating system improve its overall performance. For example, if you know you are opening a 1-MB file that you will read from beginning to end and never use again, then it is a waste for the operating system to cache any of it. You should therefore use the FILE_FLAG_SEQUENTIAL_SCAN option.

It is possible to read from or write to a file either sequentially or at random byte offsets in the file. You typically use random offsets when the file contains a set of structures. The SetFilePointer function moves the file pointer to the indicated position.

The new file position can move a distance that is relative to the beginning of the file, the end of the file, or the current position. Positive values move forward, and negative values move backward. Listing 2.8 demonstrates a program that sets the file pointer to the fifth structure in the file written by Listing 2.7.

Table 2.17. File Attributes
AttributeCreate-FileGetFile-AttributesSetFile-Attribute
FILE_ATTRIBUTE_ARCHIVEXXX
FILE_ATTRIBUTE_COMPRESSED X 
FILE_ATTRIBUTE_DIRECTORY X 
FILE_ATTRIBUTE_ENCRYPTED X 
FILE_ATTRIBUTE_HIDDENXXX
FILE_ATTRIBUTE_INROM X 
FILE_ATTRIBUTE_NORMALXXX
FILE_ATTRIBUTE_OFFLINE XX
FILE_ATTRIBUTE_READONLYXXX
FILE_ATTRIBUTE_REPARSE_POINT X 
FILE_ATTRIBUTE_ROMMODULE X 
FILE_ATTRIBUTE_SPARSE_FILE X 
FILE_ATTRIBUTE_SYSTEMXXX
FILE_ATTRIBUTE_TEMPORARYXXX
FILE_FLAG_WRITE_THROUGHX  
FILE_FLAG_RANDOM_ACCESSX  
FILE_FLAG_SEQUENTIAL_SCANX  
FILE_ATTRIBUTE_ROMSTATICREF X 

Table 2.18. SetFilePointer—Moves the file pointer
SetFilePointer 
HANDLE fileHandleHandle created by CreateFile.
LONG distanceDistance to move pointer (low 32 bits).
PLONG distanceHighPointer to distance to move pointer (high 32 bits), or NULL.
DWORD methodFILE_BEGIN—move from start of file.
 FILE_CURRENT—move from current postion.
 FILE_END—move from end of file.
DWORD Return ValueReturns the new location of the file pointer, or 0xFFFFFFFF on error.

Listing 2.8. Gets 5th record from file created in Listing 2.7 and displays it
void Listing2_8()
{
  HANDLE hFile;
  DWORD dwNumRead;
  TCHAR szFilename[MAX_PATH + 1];
  DATA dataRec;
  if(!GetFilename(_T("Enter filename:"),
      szFilename, MAX_PATH))
    return;
  hFile = CreateFile(szFilename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
  if(hFile == INVALID_HANDLE_VALUE)
  {
    cout  ≪ _T("Could not open file. Error:")
          ≪ GetLastError();
    return;
  }
  SetFilePointer(hFile, 5 * sizeof(DATA), 0, FILE_BEGIN);
  if(GetLastError() != NO_ERROR)
  {
    cout  ≪ _T("Could not seek to file. Error:")
          ≪ GetLastError();
  }
  else
  {
    if(ReadFile(hFile, &dataRec,
                sizeof(DATA), &dwNumRead, 0))
    {
      cout  ≪ _T("Record 5: ") ≪ dataRec.a
            ≪ _T(" ")
            ≪ dataRec.b ≪ _T(" ")
            ≪ dataRec.c ≪ endl;
    }
    else
    {
      cout  ≪ _T("Could not read file. Error: ")
            ≪ GetLastError();
    }
  }
  CloseHandle(hFile);
}

File Mapping

The Win32 API provides a feature called file mapping that allows you to map a file directly into the Windows CE virtual memory space. This capability is often used to implement interprocess communication schemes and is also useful for simplifying or speeding file access.

You can map a file either for read-only or read-write access. Once mapped, you access the file by address (using array or pointer syntax) rather than using file access functions such as ReadFile or WriteFile.

For example, say that you need to access data in a file and you know that you will make a large number of writes to the file in rapid succession. Also imagine that, for performance reasons, you cannot afford the time it takes to perform all of those writes. Typically you would solve this problem by reading the file to an array, accessing the array, and then writing the array back to disk. File mapping does this automatically—it maps the file into memory for you. In addition, you can share the memory image among multiple processes, and the image will remain coherent to all viewers on a single machine. If several processes all use the same file-mapping object, all changes to the mapped file will be reflected in the data read by all processes.

Listing 2.9 shows how to use file mapping in read-only mode.

Listing 2.9. Displays Unicode text file using file mapping
void Listing2_9()
{
  HANDLE hFile;
  TCHAR szFilename[MAX_PATH + 1];
  HANDLE hFileMap;
  LPTSTR lpFile;
  if(!GetFilename(_T("Enter filename:"),
      szFilename, MAX_PATH))
    return;
  hFile = CreateFileForMapping(szFilename,
      GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
  if(hFile == INVALID_HANDLE_VALUE)
  {
    cout ≪ _T("Could not open file. Error:")
         ≪ GetLastError();
    return;
  }
  hFileMap = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, NULL);
  if(hFileMap == NULL)
  {
    cout  ≪ _T("Could not create file mapping:")
          ≪ GetLastError();
    CloseHandle(hFile);
    return;
  }
  lpFile = (LPTSTR) MapViewOfFile(hFileMap,
      FILE_MAP_READ, 0, 0, 0);
  if(lpFile == NULL)
    cout  ≪ _T("Could not create view of map:")
          ≪ GetLastError();
  else
  {
    if((DWORD)*lpFile != 0xFEFF)
      cout ≪ _T("Not a Unicode file");
    else
    {
      lpFile++;  // skip over first two bytes.
      // DANGEROUS! Assumes '' terminated file
      cout ≪ lpFile;
    }
    UnmapViewOfFile(lpFile);
  }
  CloseHandle(hFileMap);
  CloseHandle(hFile);
}

The program in Listing 2.9 begins by asking the user for a filename and opening the file with CreateFileForMapping. In Windows CE, CreateFileForMapping should be used to open a file ready for file mapping, instead of CreateFile. As Table 2.19 shows, this function takes the same arguments as CreateFile.

Table 2.19. CreateForFileMapping—Opens a file for mapping
CreateForFileMapping 
LPCTSTR lpFileNameFile for which a mapping is to be created.
DWORD dwDesiredAccessType of access. 0, GENERIC_READ or GENERIC_WRITE.
DWORD dwShareModeHow the file can be shared. 0, FILE_SHARE_READ, FILE_SHARE_WRITE.
LPSECURITY_ATTRIBUTES lpSecurityAttributesNot supported, pass as NULL.
DWORD dwCreationDispositionHow the file will be created. See CreateFile for options.
DWORD dwFlagsAndAttributesAttributes and flags for file. See CreateFile for options.
HANDLE hTemplateFileNot supported, pass as NULL.
HANDLE Return ValueHandle to a file object that can be mapped, or INVALID_HANDLE_VALUE on failure.

Listing 2.9 then calls the CreateFileMapping function to create the mapping. This step determines the size of the mapping as well as its data. The protection is set to read-only, and setting sizeLow and sizeHigh to zero sets the size to the current file size.

Table 2.20. CreateFileMapping—Creates and names a mapping
CreateFileMapping 
HANDLE fileHandleHandle to the file, or 0xFFFFFFFF for a memory block
LPSECURITY_ATTRIBUTES securitySecurity attributes (not supported, pass as NULL)
DWORD protectAccess protection (read-only vs. read-write)
DWORD sizeHighMaximum size of the mapping, high 32 bits
DWORD sizeLowMaximum size of the mapping, low 32 bits
LPTSTR mapNameName of the mapping
HANDLE Return ValueReturns a handle to the mapping, or NULL on error

The MapViewOfFile function reserves data in an address range set aside for memory-mapped files, and returns the new address of the data. The address range for memory-mapped files is above the address range used for processes. The data from the file will be paged into this memory space as you access it. In Listing 2.9, lpFile is declared as a pointer to a character so that the data can be treated text. You can declare lpFile to be of any type. For example, if the file contains a set of structures, let lpFile be a pointer to that type of structure.

Table 2.21. MapViewOfFile—Loads a file mapping into memory
MapViewOfFile 
HANDLE mapHandleHandle to the mapping
DWORD accessType of access (read-only, read-write, etc.)
DWORD offsetHighOffset into the file, high 32 bits
DWORD offsetLowOffset into the file, low 32 bits
DWORD numberNumber of bytes to map
LPVOID Return ValueReturns the starting address of the view, or 0 on error

In Listing 2.9, the code maps the entire file with read-only access. Once mapped, lpFile points to the address of the mapping, and you use it just like any other pointer or array. If you load a text file with this program, the cout statement displays the entire file, as shown. This is dangerous, since cout will assume that whatever lpFile points at is null-character terminated, but this is not generally the case for text files. The code will work until you try to open a file that contains an exact number of memory pages. In this situation, cout will look beyond the last page for the null character, and this will often cause a page fault.

Once you have finished with the file, use UnmapViewOfFile to unload the memory and write any changes back to the original file. No changes were made here, but the next example makes use of this feature.

Table 2.22. UnmapViewOfFile—Releases the view and writes changes back to the file
UnmapViewOfFile 
LPVOID addressAddress of the mapping that was returned from MapViewOfFile
BOOL Return ValueReturns TRUE on success, or FALSE on failure

Listing 2.10 shows a second example of file mapping. Here the program opens the mapped file for read-write access and then writes to the file. The changes are flushed to disk only when the program calls UnmapViewOfFile.

Listing 2.10. Displays Unicode text file using writable file mapping
void Listing2_10()
{
  HANDLE hFile;
  TCHAR szFilename[MAX_PATH + 1];
  HANDLE hFileMap;
  LPTSTR lpFile;
  DWORD dwSizeLo;
  if(!GetFilename(_T("Enter filename:"),
      szFilename, MAX_PATH))
    return;
  hFile = CreateFileForMapping(szFilename,
    GENERIC_READ | GENERIC_WRITE,
    0, 0, OPEN_EXISTING, 0, 0);
  if(hFile == INVALID_HANDLE_VALUE)
  {
    cout  ≪ _T("Could not open file. Error:")
          ≪ GetLastError();
    return;
  }
  // assume < 4 gigabytes
  dwSizeLo = GetFileSize (hFile, NULL); 
  hFileMap = CreateFileMapping(hFile, 0,
    PAGE_READWRITE, 0, dwSizeLo + 1, NULL);
  if(hFileMap == NULL)
  {
    cout  ≪ _T("Could not create file mapping:")
          ≪ GetLastError();
    CloseHandle(hFile);
    return;
  }
  lpFile = (LPTSTR) MapViewOfFile(hFileMap,
      FILE_MAP_WRITE, 0, 0, 0);
  if(lpFile == NULL)
    cout  ≪ _T("Could not create view of map:")
          ≪ GetLastError();
  else
  {
    if((DWORD)*lpFile != 0xFEFF)
      cout ≪ _T("Not a Unicode file");
    else
    {
      // add terminating NULL character
      lpFile[dwSizeLo] = '';
      // skip over first two bytes.
      lpFile++;
      cout ≪ lpFile;
    }
    UnmapViewOfFile(lpFile);
  }
  CloseHandle(hFileMap);
  // remove NULL character at end of file
  SetFilePointer(hFile, -2, NULL, FILE_END);
  SetEndOfFile(hFile);
  CloseHandle(hFile);
}

Listing 2.10 opens the mapping for reading and writing. A null character is appended to the end of the file, and this makes writing the contents of the file to cout safe. The null character needs to be removed once the mapping is closed. This can be done by moving the file pointer to the byte before the null character and then calling SetEndOfFile to set the end of file to the current file position.

Table 2.23. SetEndOfFile—Sets end of file to current file position
SetEndOfFile 
HANDLE hFileHandle of file to set end of file for
BOOL Return ValueReturns TRUE on success, or FALSE on failure

The function FlushViewOfFile can be used to write any changed data out to the Object Store. This function is also useful when using a read-only mapped file. As you read through a file, pages of memory are used to store the data. If you are reading a large file, significant amounts of the device's scarce memory can be used up. Calling FlushViewOfFile will release these pages of memory.

Table 2.24. FlushViewOfFile—Flushes changes in the view to Object Store
FlushViewOfFile 
LPVOID addressThe base address of the bytes to flush
DWORD numberThe number of bytes to flush
BOOL Return ValueReturns TRUE on success, FALSE on failure

When using FlushViewOfFile, you generally flush the entire file. The system is smart enough to write back to disk only those memory pages that actually contain modified data.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.17.40