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. Listing 2.1 Determines the content type of a text file (ANSI or Unicode)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. Listing 2.2 Displays the contents of a Unicode text filevoid 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] = '\0'; 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.
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.
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.
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.
|