The following sections show short example programs implementing a simple sequential file copy program in three different ways:
Using the Standard C library
Using Windows
Using a single Windows convenience function, CopyFile
In addition to showing contrasting programming models, these examples show the capabilities and limitations of the C library and Windows. Alternative implementations will enhance the program to improve performance and increase flexibility.
Sequential file processing is the simplest, most common, and most essential capability of any file system, and nearly any large program processes at least some files sequentially. Therefore, a simple file processing program is a good way to introduce Windows and its conventions.
File copying, often with updating, and the merging of sorted files are common forms of sequential processing. Compilers and text processing tools are examples of other applications that access files sequentially.
Although sequential file processing is conceptually simple, efficient processing that attains optimal speed can be much more difficult to achieve. It can require overlapped I/O, memory mapping, threads, or other techniques.
Simple file copying is not very interesting by itself, but comparing programs gives us a quick way to contrast different systems and to introduce Windows. The following examples implement a limited version of the UNIX cp command, copying one file to another, where the file names are specified on the command line. Error checking is minimal, and existing files are simply overwritten. Subsequent Windows implementations of this and other programs will address these and other shortcomings. Note: A UNIX implementation is included on the book's Web site.
As illustrated in Program 1-1, the Standard C library supports stream FILE I/O objects that are similar to, although not as general as, the Windows HANDLE objects shown in Program 1-2.
/* Chapter 1. Basic cp file copy program. C library Implementation. */ /* cp file1 file2: Copy file1 to file2. */ #include <stdio.h> #include <errno.h> #define BUF_SIZE 256 int main (int argc, char *argv []) { FILE *in_file, *out_file; char rec [BUF_SIZE]; size_t bytes_in, bytes_out; if (argc != 3) { printf ("Usage: cpC file1 file2\n"); return 1; } in_file = fopen (argv [1], "rb"); if (in_file == NULL) { perror (argv [1]); return 2; } out_file = fopen (argv [2], "wb"); if (out_file == NULL) { perror (argv [2]); return 3; } /* Process the input file a record at a time. */ while ((bytes_in = fread (rec, 1, BUF_SIZE, in_file)) > 0) { bytes_out = fwrite (rec, 1, bytes_in, out_file); if (bytes_out != bytes_in) { perror ("Fatal write error."); return 4; } } fclose (in_file); fclose (out_file); return 0; }
This simple example clearly illustrates some common programming assumptions and conventions that do not always apply with Windows.
Open file objects are identified by pointers to FILE structures (UNIX uses integer file descriptors). NULL indicates an invalid value. The pointers are, in effect, a form of handle to the open file object.
The call to fopen specifies whether the file is to be treated as a text file or a binary file. Text files contain system-specific character sequences to indicate situations such as an end of line. On many systems, including Windows, I/O operations on a text file convert between the end-of-line character sequence and the null character that C interprets as the end of a string. In the example, both files are opened in binary mode.
Errors are diagnosed with perror, which, in turn, accesses the global variable errno to obtain information about the function call failure. Alternatively, the ferror function could be used to return an error code that is associated with the FILE rather than the system.Appendix C, C library performance for sequential I/O is competitive with alternative implementations. Nonetheless, programs are still constrained to synchronous I/O operations, although this constraint will be lifted somewhat when using Windows threads (starting in Chapter 7).
C library file processing programs, like their UNIX equivalents, are able to perform random access file operations (using fseek or, in the case of text files, fsetpos and fgetpos), but that is the limit of sophistication of Standard C library file I/O. Note: Visual C++ does provide nonstandard extensions that support, for example, file locking. Finally, the C library cannot control file security.
In summary, if simple synchronous file or console I/O is all that is needed, then use the C library to write portable programs that will run under Windows.
Program 1-2 shows the same program using the Windows API, and the same basic techniques, style, and conventions will be used throughout this book.
/* Chapter 1. Basic cp file copy program. Windows Implementation. */ /* cpW file1 file2: Copy file1 to file2. */ #include <windows.h> #include <stdio.h> #define BUF_SIZE 256 int main (int argc, LPTSTR argv []) { HANDLE hIn, hOut; DWORD nIn, nOut; CHAR Buffer [BUF_SIZE]; if (argc != 3) { printf ("Usage: cpW file1 file2\n"); return 1; } hIn = CreateFile (argv [1], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (hIn == INVALID_HANDLE_VALUE) { printf ("Cannot open input file. Error: %x\n", GetLastError ()); return 2; } hOut = CreateFile (argv [2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hOut == INVALID_HANDLE_VALUE) { printf ("Cannot open output file. Error: %x\n", GetLastError ()); return 3; } while (ReadFile (hIn, Buffer, BUF_SIZE, &nIn, NULL) && nIn > 0) { WriteFile (hOut, Buffer, nIn, &nOut, NULL); if (nIn != nOut) { printf ("Fatal write error: %x\n", GetLastError ()); return 4; } } CloseHandle (hIn); CloseHandle (hOut); return 0; }
This simple example illustrates some Windows programming features that Chapter 2 will start to explain in detail.
<windows.h> is always included and contains all Windows function definitions and data types.Appendix A shows how to exclude unwanted definitions to expedite compilations and save disk space.
All Windows objects are identified by variables of type HANDLE, and a single generic CloseHandle function applies to most objects.Program 2-2 shows how to obtain Windows-generated textual error messages.Chapter 15. The output file in this example is not secured.
Functions such as CreateFile have a rich set of options, and the example uses default values.
Windows has a number of convenience functions that combine several functions to perform a common task. These convenience functions can also improve performance in some cases (see Appendix C). CopyFile, for example, greatly simplifies the file copy program (Program 1-3). Among other things, there is no need to be concerned with the appropriate buffer size, which was arbitrarily set to 256 in the two preceding programs.
/* Chapter 1. Basic cp file copy program. Windows implementation using CopyFile for convenience and improved performance. */ /* cpCF file1 file2: Copy file1 to file2. */ #include <windows.h> #include <stdio.h> int main (int argc, LPTSTR argv []) { if (argc != 3) { printf ("Usage: cpCF file1 file2\n"); return 1; } if (!CopyFile (argv [1], argv [2], FALSE)) { printf ("CopyFile Error: %x\n", GetLastError ()); return 2; } return 0; }