Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By Mickey Williams and David Bennett

File I/O Differences Between MFC and Win32

Win32 and the MFC class library approach application input and output in different ways:

  • When using Win32 file I/O functions, you create a file handle and manipulate the file handle with functions such as ReadFile and WriteFile. If there is an error in a Win32 file I/O function, these functions return FALSE, and you must call the Win32 GetLastError function to determine the specific cause of the error.
  • When using MFC file I/O, input and output are performed in a more object-oriented manner using the CFile and CArchive classes. If an error occurs, a C++ exception is raised that contains detailed error information.

Using Basic Win32 File I/O

In a Win32 program, file I/O requires a file handle, which is a variable of type HANDLE. No matter what specific type of file I/O you are using, whether it's dealing with disk files, sockets, pipes, or some other Win32 I/O type, you always start by creating a HANDLE. After you open a file handle, the actual input and output are performed with the WriteFile and ReadFile functions. (Enhanced versions of these functions also exist that are used exclusively on Windows NT.) Listing 9.1 provides a short C++ example of a typical Win32 console-mode application named FileCopy to demonstrate how Win32 file I/O is used. FileCopy is a naive implementation of the Win32 CopyFile function.

Example 9.1. Using Win32 Functions to Copy a File

#include <windows.h>

char g_szSource[_MAX_PATH];
char g_szDest[_MAX_PATH];
bool fetchParameters(int argc, char* argv[]);

int main(int argc, char* argv[])
{
    char szMsg[_MAX_PATH + 128];
    if(!fetchParameters(argc, argv))
        return 0;
    HANDLE hRead = CreateFile(g_szSource, GENERIC_READ,
                              FILE_SHARE_READ, NULL,
                              OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL, 0);
    if(hRead == INVALID_HANDLE_VALUE)
    {
        wsprintf(szMsg, "Can't open file: %s", (char*)g_szSource);
        MessageBox(NULL, szMsg, argv[0], MB_ICONHAND);
        return 0;
    }
    HANDLE hWrite = CreateFile(g_szDest, GENERIC_WRITE,
                               0, NULL,
                               CREATE_ALWAYS,
                               FILE_ATTRIBUTE_NORMAL, 0);
    if(hWrite == INVALID_HANDLE_VALUE)
    {
        wsprintf(szMsg, "Can't open file: %s", (char*)g_szDest);
        MessageBox(NULL, szMsg, argv[0], MB_ICONHAND);
        CloseHandle(hRead);
        return 0;
    }
    BOOL fRead = FALSE;
    BOOL fWrite = FALSE;
    DWORD dwRead = 0;
    DWORD dwWrite = 0;
    BYTE buff[4096];
    do
    {
        fRead = ReadFile(hRead, buff, sizeof(buff), &dwRead, 0);
        if(fRead && dwRead)
        {
            fWrite = WriteFile(hWrite, buff, dwRead, &dwWrite, 0);
        }
    } while(fRead && dwRead && fWrite && dwWrite);
    CloseHandle(hRead);
    CloseHandle(hWrite);
    return 0;
}
bool fetchParameters(int argc, char* argv[])
{
    if(argc != 3)
    {
        char szMsg[128] = "Usage: FileCopy Source Dest";
        MessageBox(NULL, szMsg, argv[0], MB_ICONHAND);
        return false;
    }
    lstrcpyn(g_szSource, argv[1], _MAX_PATH);
    lstrcpyn(g_szDest, argv[2], _MAX_PATH);
    return true;

}
						

You can compile the example program in Listing 9.1 by creating a Win32 Console Application project, or by using the following command-line (assuming the source file is saved as main.cpp):

cl /FeFileCopy.exe filecopy.cpp user32.lib

The FileCopy program accepts two filenames as parameters:

FileCopy sourcefile destfile

FileCopy reads up to 4,096 bytes from the source file into a buffer and copies the contents of the buffer into the destination file. Most of the source code shown in Listing 9.1 is devoted to parameter validation and error checking, even though the error checking is very basic—no information is provided about the specific type of error that occurred.

Using Basic MFC File I/O

The MFC class library uses the CFile class to represent a file I/O object. Listing 9.2 contains the MFCCopy program, a console-mode application that uses MFC for file I/O. Like the FileCopy program in the preceding section, MFCCopy copies the contents of a file from one location to another. Unlike FileCopy, MFCCopy takes advantage of the object-oriented nature of the CFile class and reduces the amount of code that must be written.

Example 9.2. A Console-Mode Program That Uses MFC File I/O

#include <afx.h>
#include <afxwin.h>

char g_szSource[_MAX_PATH];
char g_szDest[_MAX_PATH];
bool fetchParameters(int argc, char* argv[]);

int main(int argc, char* argv[])
{
    AfxWinInit(::GetModuleHandle(NULL), NULL,
               ::GetCommandLine(), 0);
    if(!fetchParameters(argc, argv))
        return 0;
    try
    {
        DWORD dwRead;
        CFile fileSrc(g_szSource, CFile::modeRead);
        CFile fileDest(g_szDest, CFile::modeCreate|CFile::modeWrite);
        BYTE buff[4096];
        while((dwRead = fileSrc.Read(&buff, sizeof(buff)))!= 0)
        {
            fileDest.Write(&buff, dwRead);
        }
        // CFile destructors will close the files.
    }
    catch(CFileException* pe)
    {
        char szMsg[256];
        pe->GetErrorMessage(szMsg, sizeof(szMsg));
        MessageBox(NULL, szMsg, argv[0], MB_ICONHAND);
        pe->Delete();
    }
    return 0;
}

bool fetchParameters(int argc, char* argv[])
{
    if(argc != 3)
    {
        char szMsg[128] = "Usage: MFCCopy Source Dest";
        MessageBox(NULL, szMsg, argv[0], MB_ICONHAND);
        return false;
    }
    lstrcpyn(g_szSource, argv[1], _MAX_PATH);
    lstrcpyn(g_szDest, argv[2], _MAX_PATH);
    return true;
}


						

Compare the MFCCopy source in Listing 9.2 to the source code for FileCopy in Listing 9.1. One difference in the MFCCopy project is that the CFile class greatly simplifies how you open a disk file; you can specify the name of the file as a parameter to the object's constructor. Another difference is the use of C++ exception handling—all errors are reported as exceptions. MFCCopy actually has more functionality than FileCopy, with fewer lines of code. MFCCopy displays an accurate error message for all types of file I/O errors, for example. The CFile class is discussed in more detail in the next section, "Working with the CFile Class."

The MFCCopy project is located on the CD-ROM included with this book. To create the MFCCopy project from scratch, create a Win32 console-mode application using AppWizard. When the wizard for the type of console-mode application prompts you, select the An Empty Project radio button. After adding the source code from Listing 9.2 to the project, modify the build settings so that the MFC class library is included with the project. Choose Settings from the Project menu. After the Project Settings dialog box appears, select the General tab. You can use the General tab to change the current MFC build settings for the project. Select one of the settings that includes MFC and build the project.

Alternatively, you can save Listing 9.2 as main.cpp, and use the following command-line to create the MFCCopy executable:

cl /GX /MT /FeMFCCopy.exe main.cpp user32.lib

+ Share This