Let's say that you want to write a program that performs the simplest possible file operation: you want to open a file, read from it, and write its contents to the screen. First, however, you need to determine what type of text file you have. The file could contain single-byte characters using the ANSI character set. Alternatively, the file could contain text using Unicode characters, where two bytes are used to store each character. Further, Unicode characters can be stored with the most significant byte either first or last. It is important to determine which byte-ordering scheme is being used before the file is read.
In Unicode text files, the first two characters have the value 0xfeff if the file is a Unicode file, or 0xfffe if the file is Unicode with reversed byte order. In ANSI files, the first two bytes store regular characters.
Listing 2.1 shows code that opens a text file and determines the character set being used.
void Listing2_1() { HANDLE hFile; WORD wLeadin; DWORD dwNumRead; TCHAR szFilename[MAX_PATH + 1]; 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(ReadFile(hFile, &wLeadin, 2, &dwNumRead, 0)) { // Is this a Unicode file? // Determine byte order sequence if(wLeadin == 0xFEFF) cout ≪ _T("Unicode File") ≪ endl; else if(wLeadin == 0xFFFE) cout ≪ _T("Byte reversed Unicode file") ≪ endl; else cout ≪ _T("Text file") ≪ endl; } else { cout ≪ _T("Could not read file. Error: ") ≪ GetLastError(); } CloseHandle(hFile); } |
In this program, the code requests a file name from the user, opens the file using CreateFile, reads the first two characters from the file using ReadFile, and then closes the file using CloseHandle. Listing 2.2 modifies the code in Listing 2.1 so that the contents of the file are listed if the file contains Unicode text.
void Listing2_2() { HANDLE hFile; WORD wLeadin; DWORD dwNumRead; TCHAR szFilename[MAX_PATH + 1], szChar[2]; 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(ReadFile(hFile, &wLeadin, 2, &dwNumRead, 0)) { if(wLeadin == 0xFEFF) // read file character by character while(ReadFile(hFile, szChar, sizeof(TCHAR), &dwNumRead, 0) && dwNumRead > 0) { szChar[1] = ' '; cout ≪ szChar; } else cout ≪ _T("File is not Unicode!") ≪ endl; } else cout ≪ _T("Could not read file. Error: ") ≪ GetLastError(); CloseHandle(hFile); } |
The CreateFile function opens a file for read and/or write access. We will see in Chapter 9 that this same function also opens serial communications ports. It is also dealt with in more detail later in this chapter.
CreateFile | |
---|---|
LPCTSTR name | Name of the file to open |
DWORD accessMode | How the file should be accessed |
DWORD shareMode | The way the file should be shared |
LPSECURITY_ATTRIBUTES securityAttributes | Address of a security structure (not supported, should be NULL) |
DWORD create | The way the file should be created |
DWORD attributes | Settings for file attribute bits and flags |
HANDLE templateFile | File containing extended attributes (not supported, should be NULL) |
HANDLE Return Value | Returns a handle on success, or INVALID_HANDLE_VALUE |
In Listing 2.2, the CreateFile function accepts the name of the file, a GENERIC_READ access mode that stipulates that the file will be used in a read-only mode, a share mode that prevents any other process from opening the file, and an OPEN_EXISTING creation mode that specifies that the file already exists. Windows CE does not support security attributes or a template file. The function returns either a handle to the file object that it opened, or returns INVALID_HANDLE_VALUE if an error is detected. If an error occurs, you can use the GetLastError function to retrieve an error code. A very common mistake is to test the returned handle for NULL rather than INVALID_HANDLE_VALUE, and so failures in CreateFile remain undetected.
Once the file is open, the ReadFile function reads two bytes of data that are used to determine the text file type. Then, ReadFile is used to read data from the file one character at a time. ReadFile is a generic block-reading function. You pass it a buffer and the number of bytes for it to read, and the function retrieves the specified number of bytes from the file starting at the current offset.
ReadFile | |
---|---|
HANDLE file | File handle created with CreateFile |
LPVOID buffer | Buffer to hold the read bytes |
DWORD requestedBytes | The number of bytes desired |
LPDWORD actualBytes | The number of bytes actually placed in the buffer |
LPOVERLAPPED overlapped | Overlapped pointer to overlapped structure (not supported) |
BOOL Return Value | TRUE on success, otherwise FALSE |
In Listing 2.2 the code reads the file one character at a time until ReadFile indicates end-of-file. The CloseHandle function closes the file once the operations on it are complete.
CloseHandle | |
---|---|
HANDLE object | The handle to close |
BOOL Return Value | TRUE on success, otherwise FALSE |
In this section the goal has been to show that file access using the Windows CE API functions is not much different from normal file access techniques that you already understand.
18.217.182.45