MFC is the C++ class library Microsoft provides to place an object-oriented wrapper around the Windows API. Version 6 contains about 200 classes, some of which you'll use directly and others of which will serve primarily as base classes for classes of your own. Some MFC classes are exceedingly simple, such as the CPoint class that represents a point (a location defined by x and y coordinates). Others are more complex, such as the CWnd class that encapsulates the functionality of a window. In an MFC program, you don't often call the Windows API directly. Instead, you create objects from MFC classes and call member functions belonging to those objects. Many of the hundreds of member functions defined in the class library are thin wrappers around the Windows API and even have the same names as the corresponding API functions. An obvious benefit of this naming convention is that it speeds the transition for C programmers making the move to MFC. Want to move a window? A C programmer would probably call the SetWindowPos API function. Look up SetWindowPos in an MFC reference, and you'll find that MFC supports SetWindowPos, too. It's a member of the CWnd class, which makes sense when you think of a window as an object and SetWindowPos as an operation you might want to perform on that object.
MFC is also an application framework. More than merely a collection of classes, MFC helps define the structure of an application and handles many routine chores on the application's behalf. Starting with CWinApp, the class that represents the application itself, MFC encapsulates virtually every aspect of a program's operation. The framework supplies the WinMain function, and WinMain in turn calls the application object's member functions to make the program go. One of the CWinApp member functions called by WinMain—Run—provides the message loop that pumps messages to the application's window. The framework also provides abstractions that go above and beyond what the Windows API has to offer. For example, MFC's document/view architecture builds a powerful infrastructure on top of the API that separates a program's data from graphical representations, or views, of that data. Such abstractions are totally foreign to the API and don't exist outside the framework of MFC or a similar class library.
The fact that you're reading this book means you've probably already heard the traditional arguments in favor of using an object-oriented design methodology: reusability, tighter binding of code and data, and so on. And you should already be familiar with common object-oriented programming (OOP) terms such as object, inheritance, and encapsulation, particularly as they pertain to the C++ language. But without a good class library to serve as a starting point, OOP does little to reduce the amount of code you write.
That's where MFC comes in. Want to add a toolbar to your application—one that can be docked to different sides of a window or floated in a window of its own? No problem: MFC provides a CToolBar class that does the bulk of the work for you. Need a linked list or a resizeable array? That's easy, too: CList, CArray, and other MFC collection classes provide canned containers for your data. And don't forget about COM, OLE, and ActiveX. Few among us have the desire or the know-how to write an ActiveX control from scratch. MFC simplifies the development of ActiveX controls by providing the bulk of the code you need in classes such as COleControl and COlePropertyPage.
Another advantage to using MFC is that the framework uses a lot of tricks to make Windows objects such as windows, dialog boxes, and controls behave like C++ objects. Suppose you want to write a reusable list box class that displays a navigable list of drives and directories on the host PC. Unless you create a custom control to do the job, you can't implement such a list box in C because clicking an item in the list box sends a notification to the list box's parent (the window or the dialog box in which the list box appears), and it's up to the parent to process that notification. In other words, the list box control doesn't control its own destiny; it's the parent's job to update the list box's contents when a drive or a directory is changed.
Not so with MFC. In an MFC application, windows and dialog boxes reflect unprocessed notifications back to the controls that sent them. You can create a self-contained and highly reusable list box class that responds to its own click notifications by deriving your own list box class from CListBox. The resulting list box implements its own behavior and can be ported to another application with little more than a #include statement in a source code file. That's what reusability is all about.
When the programmers at Microsoft set out to create MFC, they had a vision of the future that included a pair of key design goals:
MFC should provide an object-oriented interface to the Windows operating system that supports reusability, self-containment, and other tenets of OOP. It should do so without imposing undue overhead on the system or unnecessarily adding to an application's memory requirements.
The first goal was accomplished by writing classes to encapsulate windows, dialog boxes, and other objects and by including key virtual functions that can be overridden to alter the behavior of derived classes. The second goal required the architects of MFC to make some choices early on about how windows, menus, and other objects would be wrapped by MFC classes such as CWnd and CMenu. Efficient use of memory was important then and it's important today, because nobody likes a class library that produces bloated code.
One of the ways in which the designers of MFC minimized the overhead added by the class library is manifested in the relationship between MFC objects and Windows objects. In Windows, information about the characteristics and current state of a window is stored in memory owned by the operating system. This information is hidden from applications, which deal exclusively with window handles, or HWNDs. Rather than duplicate all the information associated with an HWND in the data members of the CWnd class, MFC wraps a window in a CWnd by storing the HWND in a public CWnd data member named m_hWnd. As a rule, if Windows exposes an object through a handle of some type, the corresponding MFC class will contain a data member for that handle. This knowledge can be useful if you want to call an API function that requires a handle but you have, say, a CWnd or CWnd pointer instead of an HWND.
The cornerstone of MFC's application framework is the document/view architecture, which defines a program structure that relies on document objects to hold an application's data and on view objects to render views of that data. MFC provides the infrastructure for documents and views in the classes CDocument and CView. CWinApp , CFrameWnd, and other classes work in conjunction with CDocument and CView to bind all the pieces together. It's a little early to discuss the details of the document/view architecture, but you should at least be familiar with the term document/view because it inevitably comes up in any discussion of MFC.
The reason documents and views are so important is that document/view applications derive the greatest benefit from the application framework. You can write MFC programs that don't use documents and views (and we'll do a lot of that in this book, especially in Chapters 1 through 8), but to get the most out of the framework and take advantage of some of MFC's most advanced features, you must use the document/view architecture. That's not as restricting as it sounds, because almost any program that relies on documents of some type can be cast in the document/view mold. Don't let the term document mislead you into thinking that the document/view architecture is useful only for writing word processors and spreadsheet programs. A document is simply an abstract representation of a program's data. A document could just as easily be a byte array that stores board positions in a computerized game of chess as it could be a spreadsheet.
What kinds of support does MFC provide to document/view applications? Among other things, the document/view architecture vastly simplifies printing and print previewing, the mechanics of saving documents to disk and reading them back again, and converting applications into Active document servers whose documents can be opened in Microsoft Internet Explorer. You'll learn all about the document/view architecture in Part II of this book, but only after you've done some programming without documents and views so that you can get to know MFC without having too much heaped on your plate at once.
MFC provides a variety of classes designed to serve a wide range of needs. You'll find a handy diagram of the MFC 6.0 class hierarchy inside the front cover of this book.
The majority of MFC classes are derived, either directly or indirectly, from CObject. CObject provides three important features to classes that inherit from it:
Serialization support Run-time class information support Diagnostic and debugging support
Serialization is the process of streaming an object's persistent data to or from a storage medium such as a disk file. By using CObject as a base class, you can write serializable classes whose instances are easily saved and re-created. Run-time class information (RTCI) lets you retrieve an object's class name and other information about the object at run time. RTCI is implemented apart from the run-time type information (RTTI) mechanism in C++ because it predated RTTI by a number of years. Diagnostic and debugging support built into CObject let you perform validity checks on instances of CObject-derived classes and dump state information to a debugging window.
CObject provides other benefits to its derived classes as well. For example, it overloads the new and delete operators to provide protection against memory leaks. If you create an object from a CObject-derived class and fail to delete it before the application terminates, MFC will warn you by writing a message to the debug output window. The overarching importance of this most basic of MFC classes will become increasingly clear as you grow more familiar with MFC.
Not all of the functions that MFC offers are members of classes. MFC provides an API of sorts all its own in the form of global functions whose names begin with Afx. Class member functions can be called only in the context of the objects to which they belong, but AFX functions are available anytime and anywhere.
The following table lists some of the more commonly used AFX functions. AfxBeginThread simplifies the process of creating threads of execution. AfxMessageBox is the global equivalent of the Windows MessageBox function and, unlike CWnd::MessageBox, can be called just as easily from a document class as from a window class. AfxGetApp and AfxGetMainWnd return pointers to the application object and the application's main window and are useful when you want to access a function or data member of those objects but don't have a pointer readily available. AfxGetInstanceHandle is handy when you need an instance handle to pass to a Windows API function. (Even MFC programs call API functions every now and then!)
Commonly Used AFX Functions
|AfxAbort||Unconditionally terminates an application; usually called when an unrecoverable error occurs|
|AfxBeginThread||Creates a new thread and begins executing it|
|AfxEndThread||Terminates the thread that is currently executing|
|AfxMessageBox||Displays a Windows message box|
|AfxGetApp||Returns a pointer to the application object|
|AfxGetAppName||Returns the name of the application|
|AfxGetMainWnd||Returns a pointer to the application's main window|
|AfxGetInstanceHandle||Returns a handle identifying the current application instance|
|AfxRegisterWndClass||Registers a custom WNDCLASS for an MFC application|