Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
13 views

MFC Class Library Overview

Uploaded by

bennetcharles
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

MFC Class Library Overview

Uploaded by

bennetcharles
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 6

MFC Class Library Overview

 The Application Framework


 Creating an MFC Application with MFC AppWizard
 The CWinApp Class
 The CWinThread Class
 The CCmdTarget Class
 The CObject Class
 Runtime Type Information
 Debugging Support

The Application Framework


The MFC, as the name suggests, provides a set of reusable classes designed to simplify
Windows programming. Like any class library, MFC provides classes for certain basic
objects, such as strings, files, and collections, that are used in everyday programming. It
also provides classes that wrap common Windows APIs and data structures, such as
windows, controls, and device contexts. In addition, MFC provides an application
framework, including the classes that make up the application architecture hierarchy.

The application framework helps get your application running by providing program
initialization, passing Windows messages to the appropriate places, and cleaning it all up
when your application exits. The framework also provides a solid foundation for more
advanced features, such as ActiveX and document view processing.

Classes Created by MFC AppWizard

As you may have already noticed, MFC AppWizard has created a handful of classes for
you, based on your application's name. These fall into the basic classes of Application
classes, Document classes, View classes, and Frame classes. Each class is based on one
of the classes in the application architecture hierarchy

The CWinApp Class


Every true MFC application has a class derived from CWinApp. You can see how MFC
AppWizard has done this for you by looking at the following sample from HiMom.h:

class CHiMomApp : public CWinApp


{
public:
CHiMomApp();
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CHiMomApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CHiMomApp)
afx_msg void OnAppAbout();
// NOTE: ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

The first line shown here is what gives your application the real power of the MFC
application framework. This is where the CHiMomApp class is derived from CWinApp.
Apart from that, you will notice an awful lot of strange comments. These comments give
the ClassWizard landmarks to find the pieces of the code that it manipulates.

The CWinThread Class


One of the major differences between Windows 3.1 and the 32-bit versions of Windows,
such as Windows 98 and Windows 2000 is the use of preemptive multitasking. Although
Windows 3.1 supports multiple tasks, only one task could be running at one time, and
nobody else could run without the running task giving up the processor. Win32 supports
true multitasking through the use of threads, which allow more than one thing to be going
on at the same time. True, most processors really execute only one instruction stream at a
time, but as far as this chapter is concerned, all threads run at the same time.

When your application first starts, it has one—and only one—thread, known as the
primary thread. This thread is encapsulated by the CWinApp class that you have derived.
This means that a pointer to your CWinApp object is also a pointer to the CWinThread
object for the primary thread. Once your application gets going, it can create as many
new threads as it wants to manage various tasks.

Note

Even though you may not be using more than one thread, the MFC libraries always
expect to link with multithreaded runtime libraries. You should select the appropriate
multithreaded libraries in the Build Settings under the C/C++ Code Generation options.
(This is not the default for new application workspaces that are not created with MFC
AppWizard.)

The CCmdTarget Class


All window classes under MFC that accept user input, including OLE classes, are derived
at some level from CCmdTarget. This class is used as a base class for so many other
classes because it allows your class to handle Windows messages.

Windows programs are based on an event-driven model. This means that they run in the
traditional sense for only a short time at startup, then spend the rest of their lives waiting
around for messages, reacting to them, and waiting again for more messages. These
messages can be generated by simply moving the mouse, clicking on a button, or
selecting a menu command.

In C programs, these messages were generally handled by large switch blocks involving
case statements for each message that your application wanted to process. Because the
processing of these messages was often dependent on several other variables, most
applications ended up with a massive web of nested switch and if blocks.

To remedy this situation, and to allow you to use the power of C++ freely, MFC has
implemented message maps to allow your classes to handle Windows messages in a
much cleaner fashion. Any class derived from CCmdTarget may have its own message
map, allowing each class to handle the messages it is interested in however it chooses,
while leaving other messages to be handled higher in the class hierarchy.

The CObject Class


At least as far as MFC is concerned, CObject is the mother of all classes (well, most of
them anyway). Almost all the classes in MFC are derived from CObject—with a few
notable exceptions, such as CString. Deriving a class from CObject provides several
very important features, including serialization, runtime type information, and some very
important debugging features.

Serialization

Many features of Windows programming with MFC require the capability of serializing
the data in your objects. Perhaps the simplest example of this is saving an object to a file.
You need to have a way to convert your object to a series of bytes that can be written to
disk and brought back later to restore your object to its previous state.

To implement serialization in your classes, you first derive them, either directly or
indirectly, from CObject. Then you can implement the Serialize member function for
your class to serialize its data. To see just how to do this, let's start by looking at a few
macros MFC provides to help.
The DECLARE_SERIAL and IMPLEMENT_SERIAL Macros

To help implement serialization in your class, MFC provides a pair of macros:


DECLARE_SERIAL for use in your class declaration (usually in an h file), and
IMPLEMENT_SERIAL for use in your class implementation (a cpp file).

The DECLARE_SERIAL macro takes only one parameter: the name of your class. Placing
this in your class declaration provides prototypes for the serialization functions and some
special handling for the insertion operator. You can see how this is used in the following
class declaration:

Class CEmployee : public CObject


{
public:
DECLARE_SERIAL(CEmployee)
void Serialize(CArchive& ar);
private:
int m_EmpNo;
CString m_Name;
float m_Salary;
};

Serialize()

Notice that this example declares a Serialize() function, which takes as a parameter a
reference to a CArchive object, which provides a context for the serialization. Before
calling the Serialize() function, the MFC framework has prepared the CArchive object
either to read from or write to objects of your class. You must implement the specific
behavior for the Serialize() function in each class that you intend to serialize.

As mentioned previously, the same Serialize() function implements both loading and
storing, based on the CArchive context. You can use the CArchive::IsLoading() or
CArchive::IsStoring() function to determine the direction of serialization. The
implementation for the CEmployee class declared earlier might look like this:

IMPLEMENT_SERIAL(CEmployee, CObject, 0x200)


void CEmployee::Serialize(CArchive& ar)
{
// call base class Serialize() first
CObject::Serialize(ar);
// then serialize the data for this class
if(ar.IsLoading())
{
ar >> m_EmpNo;
ar >> m_Name;
ar >> m_Salary;
}
else
{
ar << m_EmpNo;
ar << m_Name;
ar << m_Salary;
}
} // end CEmployee::Serialize

There are many interesting things that you should notice in this example, beginning with
the use of the IMPLEMENT_SERIAL macro, which takes three parameters: the class name,
the base class it is derived from, and a schema number, which you will learn about in just
a bit.

Next, you should notice that you call the Serialize member of the base class. Every
implementation of Serialize() must call the Serialize() function of the base class to
allow it to serialize its data first, before you serialize the data for your class.

Serialization Operators

Notice that the serialization is performed by the overloaded insertion and extraction
operators. These are predefined for the CArchive class for the following data types:

 BYTE
 WORD
 DWORD
 LONG
 double
 float
 CObject*

The insertion and extraction operators are also defined for any class that implements
serialization. You can thank the DECLARE_SERIAL and IMPLEMENT_SERIAL macros for
this. If you need to use any other data types, you have to create your own override
functions or use macros or type casts to use the supported types.

Serializing Different Versions

Earlier, you learned that the IMPLEMENT_SERIAL macro takes a schema number for its
third parameter. This can be any number in the valid range of type UINT, with the
exception of -1, which is reserved for use by MFC. The schema number effectively
allows you to embed a version number in your serialized data; if the schema number you
specify in IMPLEMENT_SERIAL does not match the schema number in the file you are
reading, MFC will fail when attempting to read the file. In debug builds, MFC will
display a failed assertion dialog box.

If MFC just fails when presented with out-of-date file information, how can it support
multiple versions? This is where the VERSIONABLE_SCHEMA macro comes in. If you
combine your current schema number and the VERSIONABLE_SCHEMA macro by using the
OR operator (|), your Serialize() routine will write your data with the current schema
number, but can read any schema. This is handled by use of the
CArchive::GetObjectSchema() function, as you will see in the following example.
Here, you assume that the previous version of CEmployee did not implement the m_Name
member:

IMPLEMENT_SERIAL(CEmployee, CObject, VERSIONABLE_SCHEMA|0x200)


void CEmployee::Serialize(CArchive& ar)
{
// first, call base class Serialize function
CObject::Serialize(ar);

// Now we do our stuff


if(ar.IsStoring())
{
// We are writing our class data,
// so we don't care about the schema
ar << m_EmpNo;
ar << m_Name;
ar << m_Salary;
}
else
{
// we are loading, so check the schema
UINT nSchema = ar.GetObjectSchema();
switch(nSchema)
{
case 0x100:
// Old schema, default m_Name
ar >> m_EmpNo;
m_Name = ""Dilbert";
ar >> m_Salary;
break;
case 0x200:
// current version
ar >> m_EmpNo;
ar >> m_Name;
ar >> m_Salary;
break;
default:
// Unknown Version, do nothing
break;
} // end switch
} // end if
} // end CCLient::Serialize()

As you can see, you should provide reasonable defaults for data that cannot be retrieved
from the archive. On the other hand, you probably should provide some sort of
mechanism to report unknown cases to the user, instead of doing nothing, as I did here.

You might also like