- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
Win32 File Objects
The basic low-level file-manipulation and device I/O functions provided by the Win32 API are CreateFile(), ReadFile(), WriteFile(), SetFilePointer(), LockFile(), UnlockFile(), and CloseHandle(). There are many more, but these probably are the most commonly used.
These functions manipulate a Win32 file object referenced by a Win32 HANDLE type. The object can be of various types, such as these:
- Simple disk (or RAM disk) file
- Directory (disk directory)
- Disk drive (physical floppy disk or hard drive accessed as raw data)
- Sequential communications device, such as a serial or parallel port
- Console object that simulates a simple ANSI-style text terminal
- Named pipe (an interprocess communication or IPC mechanism that allows one process to efficiently send data to another via shared memory)
- Mailslot (another IPC mechanism that lets one process send data to a number of other processes simultaneously)
Basic File I/O
CreateFile() is used to both create new files and open existing files, and it returns a Win32 HANDLE to the open file.
The first parameter to CreateFile() is the filename, which can be a UNC filename and pathname or the name of a device, such as COM1: or LPT1:.
The size of this filename is limited under Windows 95 and Windows 98 to the maximum size defined by MAX_PATH (which is set to 260 characters). Under Windows NT, however, you can prefix the pathname with the characters \\?\ to bypass this limitation.
The second parameter lets you specify the requested access mode, which can be 0 if you only want to determine the attributes of the file. Or, this parameter can be a combination of GENERIC_READ and GENERIC_WRITE for read and write access.
The third parameter lets you specify how you want to share the file with other applications. If you want exclusive access, you can pass a NULL value. Otherwise, FILE_SHARE_READ or FILE_SHARE_WRITE indicates that you will let other applications have read or write access to the file.
The fourth parameter to CreateFile() lets you specify security attributes by passing a pointer to a SECURITY_ATTRIBUTES structure. These attributes are available only under Windows NT, and you normally would pass a NULL to this parameter to gain the same (default) attributes as the calling process. If your application spawns other processes and then wants to inherit this file handle, however, you should set the bInheritHandle member of this structure to TRUE. Otherwise, the child process will not be able to use the file. This restriction on inheritance also applies to Windows 95 and Windows 98.
The fifth parameter lets you specify how you want to open the file. You can pass any of the values from Table 8.1.
Table 8.1. File Open/Creation Flags Used with CreateFile()
| Flag Value | Description |
| OPEN_EXISTING | Opens an existing file or device and returns an error if the specified name doesn't exist. |
| OPEN_ALWAYS | If the file doesn't exist, the function creates it as if you had passed the CREATE_NEW flag. |
| TRUNCATE_EXISTING | Opens an existing file and wipes out the contents so that the file becomes a zero-length file. |
| CREATE_NEW | Creates a new file but returns an error if the file already exists. |
| CREATE_ALWAYS | Creates a new file, regardless of any existing file with the same name. |
The sixth parameter lets you set and combine specific file attributes, such as hidden, archive, and read-only. Normally, you would use the FILE_ATTRIBUTE_NORMAL flag value.
You also can set a large number of flags in this parameter that change the way the subsequent file read and write functions work, as in the following ways:
- Data is written immediately without waiting for a write cache to be flushed.
- No buffering is performed.
- A special asynchronous mode called overlapping may be set where read and write requests can return immediately, even though the data hasn't been read or written, to allow the calling program to perform some other processing after issuing the request. (This topic is discussed later in this chapter, in the section, "Asynchronous I/O.")
- The file is deleted when closed.
- The file is tagged as a random-access or sequential-access file, enabling the operating system to optimize access to the file.
The seventh parameter lets you specify the handle of a template file that can be used to set the file attributes of a file being created. This functionality isn't supported on Windows 95, however, and returns an unsupported error code.
If CreateFile() successfully creates the file, a valid HANDLE to that Win32 file object is returned; otherwise, INVALID_HANDLE_VALUE is returned. You can use the GetLastError() function to find out what went wrong and return an appropriate error code, as with all the file I/O functions.
You then can use the HANDLE value for subsequent ReadFile() and WriteFile() operations. The parameters for ReadFile() and WriteFile() indicate a buffer to store or read the data from, the number of bytes to write or read, and a pointer to a DWORD value to store the number of bytes successfully read or written. There is also a pointer to an OVERLAPPED structure for overlapped I/O (discussed later in this chapter, in the section, "Asynchronous I/O"). You can pass NULL for this parameter for normal blocking I/O (waiting for the function to complete). These functions return a simple Boolean value to indicate success (TRUE) or failure (FALSE).
You can reposition the current file position with the SetFilePointer() function. You can pass an amount to move by, as well as a flag value or FILE_BEGIN, FILE_CURRENT, or FILE_END to indicate that the amount is relative to the start, current position, or end of the file.
This function lets you specify 64-bit amounts to move by. Normally (for files smaller than 4GB), you'd only use the second parameter to specify the low 32-bit value and pass zero to the third parameter. However, if you have truly huge files, you can pass the high-order 32-bit word in the third parameter to access 264 bytes of data!
If a call to SetFilePointer() fails, a value of (DWORD)-1 is returned.
You can lock and unlock sections of a file using the LockFile() and UnlockFile() functions. These functions also let you specify two 64-bit numbers for the byte position to lock from and the size of the locked region. A Boolean return code indicates success or failure.
Finally, you can close the file with a call to the CloseHandle() function.
You can copy files by passing the source and destination filenames to the CopyFile() function, or you can rename them with the MoveFile() function.
The following listing illustrates these various file-handling functions with a program that uses a Win32 file object to calculate prime numbers (unusually getting faster with the higher primes!):
Example 8.1. File Handling Functions
#include "stdafx.h"
#include "windows.h"
#include "iostream.h"
const DWORD dwPrimes = 50000; // Primes to 50,000
DWORD TestPrimes(HANDLE hFile);
DWORD DisplayError(LPSTR strError);
int main(int argc, char* argv[])
{
HANDLE hPrimes = CreateFile(
"Primes", // Filename
GENERIC_READ|GENERIC_WRITE, // Read & Write
NULL, // No Sharing
NULL, // Default Security
CREATE_ALWAYS, // Create it
FILE_FLAG_DELETE_ON_CLOSE, // Delete afterwards
NULL); // No template
if (hPrimes == INVALID_HANDLE_VALUE)
return DisplayError("Creating");
// Set the file size to the max number of primes
char buf[100];
memset(buf,0,sizeof(buf));
DWORD dwBytesWritten;
while(WriteFile(hPrimes,&buf,sizeof(buf),
&dwBytesWritten,NULL))
if (GetFileSize(hPrimes,NULL)>dwPrimes) break;
TestPrimes(hPrimes);
return CloseHandle(hPrimes); // Close the file
}
DWORD TestPrimes(HANDLE hFile)
{
DWORD dwBytesRead,dwBytesWritten,dwTestPrime = 2;
do
{
BOOL bFillMode = FALSE;
DWORD dwPos = dwTestPrime;
while(dwPos<dwPrimes)
{
if (SetFilePointer(hFile,dwPos,0,
FILE_BEGIN)==(DWORD)-1)
return DisplayError("Positioning");
if (!bFillMode) // Is it a prime?
{
BYTE byteTestByte;
if (!ReadFile(hFile,&byteTestByte,1,
&dwBytesRead,NULL))
return DisplayError("Reading");
if (byteTestByte == 0) // Yes!
{
cout << dwPos << "" << flush;
bFillMode = TRUE;
// Backup a byte
if (SetFilePointer(hFile,-1,0,
FILE_CURRENT)==(DWORD)-1)
return DisplayError("Positioning");
}
}
if (bFillMode) // Remove all the factors
{
BYTE byteFill = 1;
if (!WriteFile(hFile,&byteFill,1,
&dwBytesWritten,NULL))
return DisplayError("Writing");
}
dwPos+=dwTestPrime;
}
} while(dwTestPrime++ < dwPrimes);
return 0;
}
DWORD DisplayError(LPSTR strError)
{
DWORD dwError = GetLastError();
cout << "An error occurred when " << strError
<< ", Errno = " << dwError;
return dwError;
}
Asynchronous I/O
Asynchronous I/O under Windows is curiously termed overlapped I/O. Fundamentally, these terms refer to the capability to start a read or write request, and then return from the read or write function immediately without waiting for the request to finish. Given that I/O usually involves considerable waiting, asynchronous I/O can speed up a program by allowing it to perform some other processing while the I/O request is handled in the background. After your program has performed some other processing, it can check to see whether the I/O request has completed and process the I/O or continue with other jobs.
Some single-threaded programs may need to perform asynchronous operations so that they don't block while waiting for input (such as characters from a serial port or keyboard).
You can indicate that you want to perform an asynchronous I/O operation on a Win32 file object by passing the FILE_FLAG_OVERLAPPED value to the sixth parameter of the CreateFile function used to open the object. You then must provide a pointer to an OVERLAPPED structure for subsequent read and write operations.
The OVERLAPPED structure then is filled with internal information about the request when you call ReadFile() or WriteFile(). The ReadFile() or WriteFile() function returns immediately, and your program can perform other tasks. The functions return a FALSE value, indicating that an error occurred, with GetLastError() returning an ERROR_IO_PENDING value. This is a normal return code for asynchronous operations and just indicates that the operation is in progress.
You then can periodically check whether the operation has completed by using the HasOverlappedIoCompleted() macro, passing it a pointer to the OVERLAPPED structure.
You should ensure that the memory for this structure doesn't fall out of scope or become deleted before the function returns. Otherwise, a nasty crash or a blocked I/O situation can occur.
You can set the Offset and OffsetHigh DWORD members of the OVERLAPPED structure to specify a start position for a disk file transfer. You also can set the hEvent member to a manual-reset synchronization event handle that will be set to the signaled state when the operation has been completed. This lets you use the WaitForSingleObject() or WaitForMultipleObjects() function to wait for the I/O to complete.
You can find more details about an asynchronous operation using the GetOverlappedResult() function. This function needs the handle of the file object, a pointer to the OVERLAPPED structure, as input. It then can return the number of bytes transferred so far into a pointer to a DWORD passed as the third parameter. The fourth parameter is a Boolean value that you can use to make the function wait until the I/O operation is completed by passing TRUE. Otherwise, a FALSE value causes GetOverlappedResult() to return immediately, even though the I/O hasn't completed.
If an error occurs during the I/O operation, GetOverlappedResult() returns a FALSE value, and GetLastError() shows the reason for failure. However, this return code also may be ERROR_IO_PENDING if the operation is still in progress.
If you want to supply callback functions to be called when the I/O operation completes, you can use the ReadFileEx() and WriteFileEx() functions. These functions let you pass a pointer to an application CALLBACK function that will be passed any error codes, the number of bytes transferred, and a pointer to the OVERLAPPED structure after the I/O request completes.
You can find a sample listing of asynchronous overlapped I/O in the "Asynchronous Communications" section later in this chapter.
You can cancel an asynchronous I/O operation in progress by using the CancelIo() function and passing it the file object handle.
Compatibility I/O | Next Section

Account Sign In
View your cart