Chapter 4

Overview: What's in an Application?


CONTENTS

The Microsoft Foundation Classes were written for one single purpose: To make Windows programming easier by providing classes with methods and data that handle tasks common to all Windows programs. The classes that are in MFC are designed to be useful to a Windows programmer specifically. The methods within each class perform tasks that Windows programmers often need to perform. Many of the classes have a close correspondence to structures and "windows classes" in the old Windows sense of the word class. Many of the methods correspond closely to API (Application Programming Interface) functions that are already familiar to Windows programmers.

Programming for Windows

If you've programmed for Windows in C, you know that the word class was used to describe the definition of a window long before C++ programming came to Windows. A windows class is vital to any Windows C program. A standard structure holds the data that describes this windows class, and a number of standard windows classes are provided by the operating system. A programmer usually builds a new windows class for each program and registers it by calling an an API function, RegisterClass(). Windows that appear on the screen can then be created, based on that class, by calling another API function, CreateWindow().

A C-Style Windows Class

The WNDCLASS structure, which describes the windows class, is equivalent to the WNDCLASSA structure, which looks like this:


Listing 4.1  WNDCLASSA Structure from WINUSER.H

typedef struct tagWNDCLASSA {

    UINT        style;

    WNDPROC     lpfnWndProc;

    int         cbClsExtra;

    int         cbWndExtra;

    HINSTANCE   hInstance;

    HICON       hIcon;

    HCURSOR     hCursor;

    HBRUSH      hbrBackground;

    LPCSTR      lpszMenuName;

    LPCSTR      lpszClassName;

} WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA;


WINUSER.H sets up two very similar windows class structures, WNDCLASSA for programs that use normal strings, and WNDCLASSW for Unicode progams. Unicode programs are covered in Chapter 32, "Additional Advanced Topics," in the "Unicode" section.

TIP
WINUSER.H is code supplied with Developer Studio. It's typically in the folder \MSDEV\include.

If you were creating a Windows program in C, you would need to fill a WNDCLASS structure. The members of the WNDCLASS structure are:

Window Creation

If you've never written a Windows program before, you might be quite intimidated by having to fill out a structure like that. But this is the first step in Windows programming in C. However you can always find simple sample programs to copy, like this one:


WNDCLASS wcInit;



wcInit.style = 0;

wcInit.lpfnWndProc = (WNDPROC)MainWndProc;

wcInit.cbClsExtra = 0;

wcInit.cbWndExtra = 0;

wcInit.hInstance = hInstance;

wcInit.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(ID_ICON));

wcInit.hCursor = LoadCursor (NULL, IDC_ARROW);

wcInit.hbrBackground = GetStockObject (WHITE_BRUSH);

wcInit.lpszMenuName = "DEMO";

wcInit.lpszClassName ="NewWClass";



return (RegisterClass (&wcInit));

Hungarian Notation

What kind of variable name is lpszClassName? Why is it wcInit and not just Init? Because Microsoft programmers use a variable naming convention called Hungarian Notation. It is so named because it was popularized at Microsoft by a Hungarian programmer called Charles Simonyi, and probably because, at first glance, the variable names seem to be written in another language.

In Hungarian Notation, the variable is given a descriptive name like Count or ClassName that start with a capital letter. If it is a multi-word name, each word is capitalized. Then, before the descriptive name, letters are added to indicate the type of the variable- for example, nCount for an integer, or bFlag for a Boolean (True or False) variable. In this way the programmer should never forget a variable type, or do something foolish like passing a signed variable to a function that is expecting an unsigned value.

The style has gained widespread popularity, though some people hate it. If you long for the good old days of arguing where to put the brace brackets, or better still whether to call them brace, face, or squiggle brackets, but can't find anyone to rehash those old wars any more, you can probably find somebody to argue about Hungarian Notation instead. The arguments in favor boil down to "you catch yourself making stupid mistakes" and the arguments against to "it's ugly and hard to read." But the practical truth is that the structures used by the API and the classes defined in MFC all use Hungarian Notation, so you might as well get used to it. You'll probably find yourself doing it for your own variables too. The prefixes are:

PrefixVariable Type Comment
aArray 
bBoolean 
dDouble 
hHandle 
iInteger"index into"
lLong 
lpLong pointer to  
lpfnLong pointer to function  
m_Member variable C++ convention
nInteger"number of"
pPointer to  
sString 
szZero terminated string  
uUnsigned integer  
CClassC++ convention

Many people add their own type conventions to variable names; the wc in wcInit stands for windows class.

Filling the wcInit structure and calling RegisterClass is fairly standard stuff, registering a class called NewWClass with a menu called DEMO and a WindProc called MainWndProc. Everything else about it is ordinary to an experienced Windows C programmer. After registering the class, when those old-time Windows programmers wanted to create a window on the screen, out popped some code like this:


HWND hWnd;

hInst = hInstance;

hWnd = CreateWindow (

"NewWClass",

"Demo 1",

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

NULL,

NULL,

hInstance,

NULL);



if (! hWnd)

return (FALSE);



ShowWindow (hWnd, nCmdShow);

UpdateWindow (hWnd);

This code calls CreateWindow(), then ShowWindow(), and UpdateWindow(). The parameters to the API function CreateWindow() are:

CreateWindow() returns a window handle-everybody calls their window handles hWnd-and this handle is used in the rest of the standard code. If it's NULL, the window creation failed. If the handle returned has any non-NULL value, the creation succeeded and the handle is passed to ShowWindow() and UpdateWindow(), which together draw the actual window on the screen.

Handles

A handle is more than just a pointer. Windows programs refer to resources like windows, icons, cursors, and so on with a handle. Behind the scenes there is a handle table that tracks the address of the resource as well as information about the resource type. It's called a handle because a program uses it as a way to "get hold of" a resource. Handles are typically passed around to functions that need to use resources, and returned from functions that allocate resources.

There are a number of basic types of handles: HWND for a window handle, HICON for an icon handle and so on. No matter what kind of handle is being used, remember it's a way to reach a resource so that you can use it.

Programming in C++

This book will not teach you the C++ language, or the concepts of object-oriented programming. This section will briefly recap what you should have learned elsewhere. There are a number of important concepts to draw on, but perhaps the most important is that of objects and classes.

An object is a collection of data (variables) and functions. A C structure is simply a collection of data; in a C++ object, functions are kept with the data they use and change. One example of an object might be all of the data previously kept in the WNDCLASS structure, bundled together with all of the functions that use or return a WNDCLASS structure.

A class is the abstract idea of an object; some people say it is the definition of a class of objects. CWnd is a class, a definition of what a window is and does. An actual window on the screen is represented in memory by one CWnd object, one instance of the class.

The variables gathered into a class are called member variables, data, or properties. The functions gathered into a class are called the member functions, or sometimes the methods of the class. There are two very special functions for every class which are called not by the programmer but by the operating system. The constructor is called whenever an instance of the class is initialized and the destructor is called whenever an instance of the class goes out of scope. These functions do not return a value.

One class can be defined in terms of another: this is called inheritance and is a great timesaver, enabling one programmer to build on the work of others. For example, you could define a class called CSpecialWnd and use the existing definition of CWnd as a starting point, adding member variables or functions to define your new class. All objects that were instances of CSpecialWnd would have all the member variables and functions of a CWnd object as well as the extra ones you defined. The class used as a starting point (CWnd in this example) is called the base class, and the new class is called a derived class. Most of the MFC classes are designed to be used as base classes so that you can extend their features.

One final concept is that of encapsulation. This means hiding some of the internal details of an object from the parts of the program that use the object. For example, in a class like CWnd that describes a window, the block of code that fills a WNDCLASS structure then calls CreateWindow(), ShowWindow(), and UpdateWindow() is gathered together into one member function. The window handle, hWnd, is a member variable, used by the member function and ignored by most of the rest of your application.

Encapsulating the Windows API

API functions create and manipulate windows on the screen, handle drawing, connect programs to Help files, facilitate threading, manage memory, and much more. When these functions are encapsulated into MFC classes, your programs can accomplish these same basic Windows tasks, with less work on your part.

There are literally thousands of API functions, and it can take six months to a year to get a good handle on the API, so this book does not attempt to present a mini-tutorial on the API. In the "Programming for Windows" section earlier in this chapter, you were reminded about two API functions, RegisterClass() and CreateWindow(). These form a good illustration of what was difficult about C Windows programming with the API, and how the MFC classes make it easier.

Inside CWnd

CWnd is a hugely important MFC class. Roughly a third of all the MFC classes use it as a base class-classes like CDialog, CEditView, CButton, and many more. It serves as a wrapper for the old style windows class and the API functions that create and manipulate windows classes. For example, the only public member variable is m_hWnd, the member variable that stores the window handle. This variable is set by the member function CWnd::Create() and used by almost all the other member functions when they call their associated API functions.

You might think that the call to the API function CreateWindow() would be handled automatically in the CWnd constructor, CWnd::CWnd, so that when the constructor is called to initialize a CWnd object the corresponding window on the screen is created. This would save you, the programmer, a good deal of effort, because you can't forget to call a constructor. In fact, that's not what Microsoft has chosen to do. The constructor looks like this:


CWnd::CWnd()

{

AFX_ZERO_INIT_OBJECT(CCmdTarget);

}

AFX_ZERO_INIT_OBJECT is just a macro, expanded by the C++ compiler's preprocessor, that uses the C function memset to zero out every byte of every member variable in the object, like this:


#define AFX_ZERO_INIT_OBJECT(base_class) 

›;memset(((base_class*)this)+1, 0, sizeof(*this) 

›;- sizeof(class base_class));

The reason why Microsoft chose not to call CreateWindow() in the constructor is that constructors cannot return a value. If something goes wrong with the window creation, there are no elegant or neat ways to deal with it. Instead, the constructor does almost nothing, a step that essentially cannot fail, and the call to CreateWindow() is done from within the member function Cwnd::Create(), or the closely related CWnd::CreateEx(), which looks like this:


Listing 4.2  CWnd::CreateEx() from WINCORE.CPP

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,

 LPCTSTR lpszWindowName, DWORD dwStyle,

 int x, int y, int nWidth, int nHeight,

 HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)

{

 // allow modification of several common create parameters

 CREATESTRUCT cs;

 cs.dwExStyle = dwExStyle;

 cs.lpszClass = lpszClassName;

 cs.lpszName = lpszWindowName;

 cs.style = dwStyle;

 cs.x = x;

 cs.y = y;

 cs.cx = nWidth;

 cs.cy = nHeight;

 cs.hwndParent = hWndParent;

 cs.hMenu = nIDorHMenu;

 cs.hInstance = AfxGetInstanceHandle();

 cs.lpCreateParams = lpParam;



 if (!PreCreateWindow(cs))

 {

 PostNcDestroy();

 return FALSE;

 }



 AfxHookWindowCreate(this);

 HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,

 cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,

 cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);



#ifdef _DEBUG

 if (hWnd == NULL)

 {

 TRACE1("Warning: Window creation failed: 

   ›;GetLastError returns 0x%8.8X\n",

 GetLastError());

 }

#endif



 if (!AfxUnhookWindowCreate())

 PostNcDestroy();

 // cleanup if CreateWindowEx fails too soon



 if (hWnd == NULL)

 return FALSE;

 ASSERT(hWnd == m_hWnd); // should have been set in send msg hook

 return TRUE;

}


TIP
WINCORE.CPP is code supplied with Developer Studio. It's typically in the folder \MSDEV\mfc\src.

This sets up a CREATESTRUCT structure very much like a WNDCLASS, and fills it with the parameters that were passed to CreateEx(). It calls PreCreateWindow, AfxHookWindowCreate(), ::CreateWindow(), and AfxUnhookWindowCreate() before checking hWnd and returning.

TIP
The AFX prefix on many useful MFC functions dates back to the days when Microsoft's internal name for their class library was Application Framework. The :: in the call to CreateWindow identifies it as an API function, sometimes referred to as an SDK function in this context. The other functions are member functions of CWnd that set up other background boilerplates for you.

So, on the face of it, there doesn't seem to be any savings here. You declare an instance of some CWnd object, call its Create() function, and have to pass just as many parameters as you did in the old C way of doing things. What's the point? Well, CWnd is really a class from which to inherit. Things get much simpler in the derived classes. Take CButton, for example, a class that encapsulates the concept of a button on a dialog box. A button is just a tiny little window, but its behavior is constrained-for example, the user cannot resize a button. Its Create() member function looks like this:


BOOL CButton::Create(LPCTSTR lpszCaption, DWORD dwStyle,

 const RECT& rect, CWnd* pParentWnd, UINT nID)

{

 CWnd* pWnd = this;

 return pWnd->Create(_T("BUTTON"), lpszCaption, dwStyle, rect, pParentWnd, nID);

}

That's a lot less parameters! If you want a button, you create a button, and let the class hierarchy fill in the rest.

What Are All These Classes, Anyway?

There are 216 MFC classes. Why so many? What do they do? How can any normal human keep track of them and know which one to use for what? Good questions. Questions that we'll take a pretty large piece of this book to answer. The next eight chapters tackle eight natural divisions of the MFC classes, presenting the most commonly used classes. But first, let's look at some of the more important base classes.

CObject

Figure 4.1 shows a high-level overview of the inheritance tree for the 189 classes in MFC. Only 34 MFC classes do not inherit from CObject. CObject contains the basic functionality that all the MFC classes (and most of the new classes you create) will be sure to need, like persistence support, and diagnostic output. As well, classes derived from CObject can be contained in the MFC container classes, discussed in Chapter 10, "Utility and Collection Classes."

Figure 4.1 : Almost all the classes in MFC inherit from CObject.

CCmdTarget

Some of the classes that inherit from CObject, like CFile and CException, and their derived classes, do not need to interact directly with the user and the operating system through messages and commands. All the classes that do need to receive messages and commands inherit from CCmdTarget. Figure 4.2 shows a bird's eye view of CCmdTarget's derived classes, generally called command targets.

Figure 4.2 : Any class that will receive a command must inherit from CCmdTarget.

CWnd

As already mentioned, CWnd is a hugely important class. Only classes derived from CWnd can receive messages; threads and documents can receive commands but not messages.

TIP
Chapter 5 "Messages and Commands," explores the distinction between commands and messages. Chapter 6 "The Document/View Paradigm," explains documents, and Chapter 31, "Multitasking with Windows Threads," explains threads.

Cwnd provides window-oriented functionality like calls to CreateWindow and DestroyWindow, functions to handle painting the window in the screen, processing messages, talking to the Clipboard, and much more-almost 250 member functions in all. Only a handful of these will need to be overridden in derived classes. Figure 4.3 shows the classes that inherit from CWnd; there are so many control classes that to list them all would clutter up the diagram, so they are lumped together as control classes.

Figure 4.3 : Any class that will receive a message must inherit from CWnd, which provides lots of window-related functions.

All Those Other Classes

So you've seen 10 classes so far on these three figures. What about the other 206? You'll meet them in context, some in the next eight chapters, some later than that. The following sections list a quick rundown of what's to come.

Messages and Commands  Messages form the heart of Windows programming. Whenever anything happens on a Windows machine, such as a user clicking the mouse or pressing a key, a message is triggered and sent to one or more windows, which do something about it. Visual C++ makes it easy for you to write code that catches these messages and acts on them. Chapter 5 "Messages and Commands," explains the concept of messages and how MFC and other aspects of Visual C++ lets you deal with them.
The Document/View Paradigm  A paradigm is a model, a way of looking at things. The designers of MFC chose to design the framework using the assumption that every program has something it wants to save in a file. That collection of information is referred to as the document. A view is one way of looking at a document. There are a lot of advantages to separating the view and the document, and they are explained further in Chapter 6 "The Document/View Paradigm." MFC provides classes from which to inherit your document class and your view class, so common programming tasks like implementing scroll bars are no longer your problem.
Classes for Controls and Dialogs  What Windows program doesn't have a dialog box? An edit box? A button? Dialog boxes and controls are vital to Windows user interfaces, and all of them, even the simple button or piece of static text, are windows. The common controls allow you to take advantage of the learning time users have put in on other programs, and the programming time developers have put in on the operating system, to use the same File Open dialog box as everybody else, the same hierarchical tree control, and so on. Learn more about all these controls in Chapter 7 "Dialog and Controls."
Utility and Collection Classes  Some of the most useful MFC classes don't have anything to do with windows, or drawing on the screen, or interacting with a user at all. They are just useful, the sorts of classes you would find in any C++ class library for any operating system. MFC includes a string class, a time and date class, and a number of classes for dealing with collections (arrays, linked lists, and lookup tables) of other objects. These useful classes are covered in Chapter 10, "Utility and Collection Classes."
Drawing on the Screen  No matter how smart your Windows program is, if you can't tell the user what's going on by putting some words or pictures onto the screen, no one knows what the program has done. A remarkably large amount of the work is done automatically by your view classes (one of the advantages of adopting the document/view paradigm) but there will be times you have to do the drawing yourself. You learn about device contexts, scrolling, and more in Chapter 11, "Drawing on the Screen."
Persistence and File I/O  Some good things are meant to be only temporary, like the display of a calculator or an online chat window. But most programs can save their documents to a file, and open and load that file to recreate a document that has been stored. MFC makes this remarkably easy by using archives and extending the use of the stream I/O operators >> and <<. You learn all about reading and writing to files in Chapter 12, "Persistence and File I/O."
Sockets and MAPI  Microsoft recognizes that distributed computing, in which work is shared among two or more different computers, is becoming more and more common. Programs need to talk to each other, people need to send messages across a LAN or around the world, and MFC has classes that support these kinds of communication. Sockets allow your programs to use the WinSock API as easily as the 32-bit Windows API to communicate over the Internet or other kinds of networks. MAPI concentrates on messaging, whether it's e-mail, fax, or a bulletin board system. The ISAPI extensions are used on systems that host World Wide Web pages. You see which class does what in Chapter 13, "Sockets, MAPI, and the Internet."
Database Access  Database programming just keeps getting easier. ODBC, Microsoft's Open DataBase Connectivity package, allows your code to call API functions that access a huge variety of database files-Oracle, dBase, an Excel spreadsheet, a plain text file, old legacy mainframe systems using SQL, whatever! You call a standardly-named function and the API provided by the database vendor or a third party handles the translation. The DAO SDK, Data Access Objects Software Developers Kit, gives you access to the power of Jet, that database engine in Microsoft Access, Visual Basic, and Visual C++. If your program is using an Access database, you can use either ODBC or DAO, but will get better performance from DAO. For all other databases, use ODBC. The details are in Chapter 14, "Database Access."

From Here…

Windows programming in C was hard to learn: new programmers tended to copy a lot of sample code, sometime without understanding it all. The MFC classes are designed to hide the details from you and save your work. That's the whole purpose of a class library, after all: to keep you from reinventing the wheel. After reading the rest of the chapters in this part, you'll probably be ready to start building some applications. But if you count along as classes are introduced, you won't get to 216. That's because some are saved for after you've seen application development in action. If you can't wait to find out where the rest of the MFC classes are introduced, here's what's in store: