Main Index Screen
 Microsoft Foundation Classes Technical Notes

A technical note is a document written for programmers by programmers. Select one of the following technical notes for information on the indicated topic.

Number	Title

1	Window Class Registration

2	Persistent Object Data Format

3	Mapping of Windows Handles to Objects

4	C++ Template Tool

6	Message Maps

7	Debugging Trace Options

8	MFC OLE Support

11	Using MFC as Part of a DLL

12	Using Windows 3.1 Robustness Features

14	Custom Controls

15	Windows for Pen

16	Using C++ Multiple Inheritance with MFC

17	Destroying Window Objects

18	Migrating OLE Applications From MFC 1.0 to MFC 2.0

19	Migrating MFC 1.0 Applications to MFC 2.0

20	ID Naming and Numbering Conventions

21	Command and Message Routing

22	Standard Commands Implementation

23	Standard MFC Resources

24	MFC-Defined Messages and Resources

25	Document, View, and Frame Creation

26	DDX and DDV Routines

27	Emulation Support for Visual Basic Custom Controls

28	Context-Sensitive Help Support

29	Splitter Windows

30	Print Preview

31	Control Bars

32	MFC Exception Mechanism

33	DLL Version of MFC

34	Writing a Windows 3.0 Compatible MFC Application

35	Using Multiple Resource Files and Header Files with App Studio

36	Using CFormView with AppWizard and ClassWizard

37	Multithreaded MFC 2.1 Applications

The numbering of technical notes is chronological.

Technical notes 1 to 17 are general topics that apply to MFC 1.0 and 2.0.

Technical notes 18 and 19 focus on migration of applications from MFC 1.0 to MFC 2.0.

Technical notes 20 and above are topics specific to MFC 2.0.

Gaps in the Technical Note numbering are due to MFC 1.0 technical notes that are now obsolete. More complete information can now be found in the printed MFC documentation.


Copyright Notice

Information in this online document is subject to change without notice and does not represent a commitment on the part of Microsoft Corporation. The software, including any databases and online documents, described in this document are furnished under a license agreement or nondisclosure agreement. The software, including any databases and online documents, may be used or copied only in accordance with the terms of the agreement. It is against the law to copy the software on any medium except as specifically allowed in the license or nondisclosure agreement. The purchaser may make one copy of the software for backup purposes. No part of this online document and/or databases may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or information storage and retrieval systems, for any purpose other than the purchaser's personal use, without the express written permission of Microsoft Corporation.

© 1993 Microsoft Corporation. All rights reserved.

Microsoft, MS, MS-DOS, and CodeView are registered trademarks and Visual Basic, Visual C++, and Windows are trademarks of Microsoft Corporation.


AFX is the name of the product group at Microsoft that provides object-oriented solutions to help you to write Windows applications in C++. AFX products include App Studio, AppWizard, ClassWizard and MFC.


Each technical note describes a problem or feature that is beyond the scope of the printed MFC documentation. The technical notes supplied reflect requests for information from users, as well as specialized information that the MFC developers anticipate advanced users will want.


Window Class Registration
Technical Note 1: Window Class Registration

This note describes the MFC routines that register the special WNDCLASSes needed by Microsoft Windows. Specific WNDCLASS attributes used by MFC and Windows are discussed.

The Problem

The attributes of a CWnd object, like an HWND in Windows, are stored in two places: the window object and the WNDCLASS. A WNDCLASS is different than a C++ class. The name of the WNDCLASS is passed to general window creation functions such as CWnd::Create and CFrameWnd::Create in the lpszClassName parameter.

This WNDCLASS must be registered via one of four means:

 implicitly by MFC provided WNDCLASSES

 implicitly by subclassing a Windows control (or some other control)

 explicitly by calling the MFC AfxRegisterWndClass function

 explicitly by calling the Windows routine RegisterClass

WNDCLASSes and MFC

The WNDCLASS structure consists of various fields that describe a window class. Following are the fields and how they are used in an MFC application.

Style	Style of window: see below
lpfnWndProc	window proc, must be AfxWndProc
cbClsExtra	not used
cbWndExtra	not used
hInstance	automatically filled with AfxGetInstanceHandle
hIcon	icon for frame windows, see below
hCursor	cursor for when mouse is over window, see below
hbrBackground	background color, see below
lpszMenuName	not used
lpszClassName	class name, see below
Provided WNDCLASSes

MFC provides several default window classes that are registered when the first instance of an MFC application is initialized. When the lpszClassName parameter is NULL, these default WNDCLASSes are used. If the lpszClassName parameter is not NULL, the specific WNDCLASS named by that string is used.

The following shows the classes that are registered and their attributes:

"AfxWnd" is used for all child windows created with CWnd::Create.

class style : CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW

no icon

arrow cursor

no background color

"AfxFrameOrView" is used for frame windows and views (including stand-alone CFrameWnds and CMDIChildWnds).

class style : CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;

icon AFX_IDI_STD_FRAME

arrow cursor

COLOR_WINDOW background color

"AfxMDIFrame" is used for the MDI frame window (that is, the parent) created with CMDIFrameWnd::Create.

class style : CS_DBLCLKS [ reduces flash during resizing ]

icon AFX_IDI_STD_MDIFRAME

arrow cursor

no background color

"AfxControlBar" is used for the standard control bar implementation.

class style : 0 [ reduces flash during resizing, no double clicks ]

no icon

arrow cursor

gray background color (COLOR_BTNFACE)

If the application provides a resource with the specified resource ID (for example, AFX_IDI_STD_FRAME) ID, MFC will use that resource. Otherwise the default resource is used. For the icon, the standard application icon is used, and for the cursor, the standard arrow cursor is used.

There are two icons that support MDI applications with single document types (one icon for the main application, the other icon for iconic document/MDIChild windows). For multiple document types with different icons, you must register additional WNDCLASSes or use the CFrameWnd::LoadFrame function.

CFrameWnd::LoadFrame will automatically register a WNDCLASS using the standard "AfxFrameOrView" attributes but using the icon ID you specify as the first parameter to LoadFrame.

The values for background color and cursor for the MDIFrameWnd are not used since the client area of the MDIFrameWnd is completely covered BY the "MDICLIENT" window. We do not support subclassing the "MDICLIENT" window so use the standard colors and cursor types.

Subclassing controls

If you subclass or superclass a Windows control (for example, CButton) then your class automatically gets the WNDCLASS attributes provided in the Windows implementation of that control.

The AfxRegisterWndClass Function

MFC provides a helper routine for registering a window class. Given a set of attributes (window class style, cursor, background brush, and icon), a synthetic name is generated, and the resulting window class is registered. For example,

const char* AfxRegisterWndClass(UINT nClassStyle, HCURSOR hCursor, HBRUSH hbrBackground, HICON hIcon);

This function returns a temporary string of the generated registered window class name. See the Class Libraries Reference for more details.

The string returned is a temporary pointer to a static string buffer which is valid until the next call to AfxRegisterWndClass. If you want to keep this string around, store it in a CString variable. For example,

CString strWndClass = AfxRegisterWndClass(CS_DBLCLK, ...);

...

CWnd* pWnd = new CWnd;

pWnd->Create(strWndClass, ...);

...

AfxRegisterWndClass will throw a CResourceException if the window class failed to register (either because of bad parameters, or out of Windows memory).

RegisterClass

If you want to do anything more sophisticated than what AfxRegisterWndClass provides, you may call the Windows API RegisterClass. The CWnd, CFrameWnd and CMDIChildWnd Create functions take a lpszClassName string name for the window class as the first parameter. Any window class name can be used, regardless of how it was registered.


Persistant Object Data Format
Technical Note 2: Persistent Object Data Format

This note describes the MFC routines that support persistent C++ objects and the format of the object data when it is stored in a file. This only applies to classes with the DECLARE_SERIAL and IMPLEMENT_SERIAL macros.

The Problem

The MFC implementation for persistent data relies on a compact binary format for saving the data for many objects in a single contiguous part of a file. This binary format provides the structure for how the data is stored, but it is the object's Serialize member function that provides the actual data saved by the object.

The MFC solves the structuring problem by using the class CArchive. A CArchive object provides a context for persistence that lasts from the time the archive is created until the CArchive::Close member function is called, either explicitly by the programmer or implicitly by the destructor when the scope containing the CArchive is exited.

This note describes the implementation of the CArchive members ReadObject and WriteObject. ReadObject and WriteObject are not called directly by instead are used by class-specific type-safe insertion and extraction operators generated automatically by the DECLARE_SERIAL and IMPLEMENT_SERIAL macros.

class CFoo : public CObject

{

    DECLARE_SERIAL(CFoo)

};

IMPLEMENT_SERIAL(CFoo, CObject, 1)

// example usage (ar is a CArchive&)

CFoo* pFoo;

CArchive& ar;

ar << pFoo;        // calls ar.WriteObject(pFoo)

ar >> pFoo;        // calls ar.ReadObject(RUNTIME_CLASS(CFoo))

This note describes code located in the MFC source file ARCOBJ.CPP. The main CArchive implementation can be found in ARCCORE.CPP.

Saving Objects to the Store (CArchive::WriteObject)

The member function CArchive::WriteObject writes header data used to reconstruct the object. This data consists of two parts: the type of the object and the state of the object. This member function is also responsible for maintaining the identity of the object being written out, so that only a single copy is saved, regardless of the number of pointers to that object (including circular pointers).

Saving (inserting) and restoring (extracting) objects relies on several manifest constants.  These are values that are stored in binary and provide important information to the archive (note the "w" prefix indicates 16-bit quantities):

wNullTag	used for NULL object pointers (0)
wNewClassTag	indicates class description that follows is new to this archive context(-1)
wOldClassTag	indicates class of the object being read has been seen in this context (0x8000)
When storing objects, the archive maintains a CMapPtrToWord (the m_pStoreMap) which is a mapping from a stored object to a 16-bit persistent identifier (PID). A PID is assigned to every unique object and every unique class name that is saved in the context of the archive. These PIDs are handed out sequentially starting at 1. It is important to note that these PIDs have no significance outside the scope of the archive and, in particular, are not to be confused with record number or other identity items.

When a request is made to save an object to an archive (usually through the global insertion operator), a check is made for a NULL CObject pointer; if the pointer is NULL, the wNullTag is inserted into the archive stream.

If we have a real object pointer that is capable of being serialized (the class is a DECLARE_SERIAL class), we then check the m_pStoreMap to see if the object has been saved already. If it has, we insert the 16-bit PID associated with that object.

If the object has not been saved before, there are two possibilities we must take into account: either both the object and the exact type (that is, class) of the object are new to this archive context, or the object is of an exact type already seen. To determine if the type has been seen we query the m_pStoreMap for a CRuntimeClass object that matches the CRuntimeClass object associated with the object we are saving. If we have seen this class before, WriteObject inserts out a 16-bit tag that is the bit-wise OR'ing of wOldClassTag and this index. If the CRuntimeClass is new to this archive context, then WriteObject assigns a new PID to that class and insert it into the archive, preceded by the wNewClassTag value.

The descriptor for this class is then inserted into the archive using the CRuntimeClass member function Store. CRuntimeClass::Store inserts the schema number of the class (see below) and the ASCII text name of the class. Note that the use of the ASCII text name does not guarantee uniqueness of the archive across applications, thus it is advisable to tag your data files to prevent corruption. Following the insertion of the class information, the archive places the object into the m_pStoreMap and then calls the Serialize member function to insert class-specific data into the archive. Placing the object into the m_pStoreMap before calling Serialize prevents multiple copies of the object from being saved to the store.

When returning to the initial caller (usually the root of the network of objects), it is important to Close the archive. If other CFile operations are going to be done, the CArchive member function Flush MUST be called. Failure to do so will result in a corrupt archive.

Note that this implementation imposes a hard limit of 32766 indices per archive context. This number represents the maximum number of unique objects and classes that can be saved in a single archive, but note that a single disk file can have an unlimited number of archive contexts.

Loading Objects from the Store (CArchive::ReadObject)

Loading (extracting) objects uses the CArchive::ReadObject member function and is the converse of WriteObject. As with WriteObject, ReadObject is not called directly by user code; user code should call the type-safe extraction operator which calls ReadObject with the expected CRuntimeClass. This insures the type integrity of the extract operation.

Since the WriteObject implementation assigned increasing PIDs, starting with 1 (0 is predefined as the NULL object), the ReadObject implementation can use an array to maintain the state of the archive context. When a PID is read from the store, if the PID is greater than the current upper bound of the m_pLoadArray, then ReadObject knows that a new object (or class description) follows.

Schema Numbers

The schema number, which is assigned to the class when the class' IMPLEMENT_SERIAL is encountered, is the "version" of the class implementation. The schema refers to the implementation of the class, not to the number of times a given object has been made persistent (usually referred to as the object version).

If you intend to maintain several different implementations of the same class over time, incrementing the schema as you revise your object's Serialize member function implementation will enable you to write code that can load objects stored using older versions of the implementation.

The CArchive::ReadObject member function will throw a CArchiveException when it encounters a schema number in the persistent store that differs from the schema number of the class description in memory. If your implementation of Serialize for a class with multiple schemas catches this exception, you will be able to continue the extraction operation taking into account the differences in the implementation of the Serialize member function.

Calling Serialize Directly

There are many cases where the overhead of the general object archive scheme of WriteObject and ReadObject is not necessary or desired. This is the common case of serializing the data into a CDocument. In this case the Serialize member function of the CDocument is called directly, not with the extract or insert operators. The contents of the document may in turn use the more general object archive scheme.

Calling Serialize directly has the following advantages and disadvantages:

 No extra bytes are added to the archive before or after the object is serialized. This not only makes the saved data smaller, but allows you to implement Serialize routines that can handle any file formats.

 The MFC is tuned so the WriteObject and ReadObject implementations and related collections will not be linked into your application unless you need the more general object archive scheme for some other purpose.

 Your code does not need to recover from old schema numbers. This makes your document serialization code responsible for encoding schema numbers, file format version numbers or whatever magic numbers desired at the start of your data files.


Mapping of Windows Handles to Objects
Technical Note 3: Mapping of Windows Handles to Objects

This note describes the MFC routines that support mapping Windows object handles to C++ objects.

The Problem

Windows objects are normally represented by HANDLEs. The MFC classes wrap Windows object handles with C++ objects. The handle wrapping functions of the MFC class library provide a way to find the C++ object that is wrapping the Windows object with a particular handle. There are times when a Windows object does not have a C++ wrapper object, however, and at these times a temporary object is created to act as the C++ wrapper.

The Windows objects that use handle maps are:

HWND (CWnd and CWnd-derived classes)

HDC (CDC and CDC-derived classes)

HMENU (CMenu)

HPEN (CGdiObject)

HBRUSH (CGdiObject)

HFONT (CGdiObject)

HBITMAP (CGdiObject)

HPALETTE (CGdiObject)

HRGN (CGdiObject)

Given a handle to any of these objects, you can find the MFC object that wraps the handle by calling the static member function FromHandle. For example, given an HWND called hWnd:

CWnd::FromHandle(hWnd)

will return a pointer to the CWnd that wraps the hWnd. If that hWnd does not have a specific wrapper object, then a temporary CWnd is created to wrap the hWnd. This makes it possible to get a valid C++ object from any handle.

Once you have a wrapper object, you can get to its handle through a public member variable. In the case of an CWnd, m_hWnd contains the HWND for that object.

Attaching Handles to MFC Objects

Given a newly created handle-wrapper object and a handle to a Windows object, you can associate the two by calling Attach. For example:

CWnd myWnd;

myWnd.Attach(hWnd);

This makes an entry in the permanent map associating myWnd and hWnd. Calling CWnd::FromHandle(hWnd) will now return a pointer to myWnd. When myWnd is deleted, the destructor will automatically destroy the hWnd by calling the Windows DestroyWindow function. If this is not desired, the hWnd must be detached from myWnd  before the myWnd object is destroyed (normally when leaving the scope at which myWnd was defined). The Detach member function does this.

myWnd.Detach();

More About Temporary Objects

Temporary objects are created whenever FromHandle is given a handle that does not already have a wrapper object. These temporary objects are detached from their handle and deleted by the DeleteTempMap functions. The default OnIdle processing in CWinApp automatically calls DeleteTempMap for each class that supports temporary handle maps. This means that you cannot assume a pointer to a temporary object will be valid past the point of exit from the function where the pointer was obtained, as the temporary object will be deleted during the Windows message-loop idle time.


C++ Template Tool
Technical Note 4: C++ Template Tool

This note describes MFC template-class issues and the MFC TEMPLDEF template expansion tool.

The Problem

MFC uses template classes to implement the standard collection classes. Template classes are classes that can be expanded based on a type argument specified by the user. For the MFC standard collection classes, the classes in AFXCOLL.H and their implementations are already pre-expanded for you. You only need to worry about templates if you want to expand a template for a different type or build your own.

For additional MFC template-based collection classes, you can specify lists or arrays of any C++ class or maps between any two classes you like.

The TEMPLDEF tool

The TEMPLDEF tool is a template expander. TEMPLDEF uses a syntax similar to the ANSI language syntax. You do not need to build the TEMPLDEF.EXE itself, since it is already provided in the \MSVCNT\BIN directory.

A good way to get started is by examining four .CTT template classes provided with MFC (found in MFC\SAMPLES\TEMPLDEF subdirectory). The MKCOLL.BAT batch file gives examples of the command line syntax of the TEMPLDEF tool. The general form of the command line syntax is modeled after a C++ typedef of the equivalent template class. For example,

typedef CLinkedList<CFoo> CFooLinkedList;

becomes

TEMPLDEF "CLinkedList<CFoo> CFooLinkedList" LINKLIST.CTT FOO.H FOO.INL FOO.CPP

The MFC template classes are slightly more complicated than this example, taking a number of boolean-constant template parameters that allow several slightly different variations of collections to be expanded. Please refer to the TEMPLDEF sample for more details on the TEMPLDEF program and the command line options for using the provided template classes.

Developing New Template Classes

You can develop your own template classes, such as the four standard collection classes mentioned above, using one of them as a model.

However, before you use TEMPLDEF to expand your template class, you should first write and completely debug a non-template version of the class. For example, first write a linked-list-of-CFoos class. Then, only after you have it working, generalize it to a linked-list-of-whatever template class. By taking this approach, you introduce the complicating factor of "template <class CXyz>" syntax only after you have an already working linked-list class.

The recommended extension for a source template file is ".CTT". This source file is divided into four sections.

The first section, which precedes the //$DECLARE_TEMPLATE comment, consists of comments, usage instructions, copyright notices and the like. This first section is stripped by TEMPLDEF and is not emitted.

The second section starts with a special directive:

//$DECLARE_TEMPLATE

TEMPLDEF processes this section to produce .H header file for the class.

The third section starts with:

//$IMPLEMENT_TEMPLATE_INLINES

It is followed by any inline member function definitions required by the template class. TEMPLDEF processes this section to produce the .INL inline file for the class.

The fourth section starts with:

//$IMPLEMENT_TEMPLATE

It is followed by the implementations for member functions of the template class. TEMPLDEF processes this section to produce the .CPP source file for the class.

Friend functions, static member definitions, and the like can be included as needed in these sections. Other than #if/#else/#endif sections expanded on a template parameter (explained below), preprocessor commands are left unexpanded in the output files. This permits additional processing by the native compiler preprocessor.

MFC 1.0 NOTE:  The MFC 1.0 version of TEMPLDEF only supported three section of a template file and did not support the IMPLEMENT_TEMPLATE_INLINES section.

One restriction of the TEMPLDEF implementation is that the first occurrence of a template declaration (typically the template class declaration) defines all name correspondences between template formal parameters and actual parameters throughout the template class file. Thus, template parameter names must be used consistently throughout all template declarations and definitions in the template class file. This approach is consistent because you are expanding only one particular template.

TEMPLDEF template parameters can either be types or compile-time constant expressions. In particular, if a template parameter is 0 or 1, then TEMPLDEF will evaluate only #if/#else/#endif statements based on that constant parameter and emit only those lines of code corresponding to the true condition. See MKCOLL.BAT and the Microsoft Foundation template classes for examples of how to use this feature.

The #if/#else/#endif expansion depends on having the manifest constant exactly 0 or 1 as a template parameter. For example, if you were to say "TRUE" or "FALSE" instead, then TEMPLDEF would leave in the #if/#else/#endif expressions for the preprocessing phase of the compiler to expand later. Also, this TEMPLDEF evaluation does not take into account nested #if/#else statements [image] either make sure your expansion corresponds to an inner-most nesting and use 0 or 1 as your boolean-constant template parameter, or say FALSE or TRUE and let the compiler's preprocessing phase do the #if/#else/#endif expansion later. Note that TEMPLDEF expects #if/#else/#endif statements to be written without intervening space after the '#' sign.


Message Maps
Technical Note 6: Message Maps

This note describes the MFC message map facility .

The Problem

Microsoft Windows implements what are essentially virtual functions in window classes using its messaging facility. Due to the large number of messages involved, providing a separate virtual function for each Windows message results in a prohibitively large vtable.

Also, since the number of system-defined Windows messages changes over time, and since a specific application may want to define some Windows messages of its own, the message-map mechanism provides a level of indirection that prevents interface changes from breaking existing code.

Overview

MFC provides an alternative to the switch statement used in traditional Windows programs to handle messages sent to a window. A mapping from messages to member-functions may be defined so that when a message is to be handled by a window, the appropriate member function is called automatically. This message-map facility is designed to be similar to virtual functions but has additional benefits not possible with C++ virtual functions.

Defining a Message Map

The DECLARE_MESSAGE_MAP macro declares three members for a class.

  A private array of AFX_MSGMAP_ENTRY entries called _messageEntries,

  A protected AFX_MSGMAP structure called messageMap, that points to the _messageEntries array

  A protected virtual function called GetMessageMap that returns the address of messageMap.

This macro should be placed in the declaration of any class using message maps. By convention, it is at the end of the class declaration. For example:

class CMyWnd : public CMyParentWndClass

{

// my stuff...

protected:

//{{AFX_MSG(CMyWnd)

afx_msg void OnPaint();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

This is the format generated by AppWizard and ClassWizard when they create new classes. The //{{ and //}} brackets are needed for ClassWizard.

The message map's table is defined with a set of macros that expand to message map entries. A table begins with a BEGIN_MESSAGE_MAP macro call, which defines the class that is handled by this message map and the parent class to which unhandled messages are passed. The table ends with the END_MESSAGE_MAP macro call.

Between these two macro calls is an entry for each message to be handled by this message map. Every standard Windows message has a macro of the form ON_WM_xxx (where xxx is the name of the message) that generates an entry for that message.

A standard function signature has been defined for unpacking the parameters of each Windows message and providing type safety. These signatures may be found in the file AFXWIN.H in the declaration of CWnd. Each one is marked with the word afx_msg for easy identification.

Note: ClassWizard requires that you use the afx_msg keyword in your message map handler declarations.

These function signatures were derived using a simple convention. The name of the function always starts with "On". This is followed by the name of the Windows message with the WM_ removed and only the first letter of each word capitalized. The ordering of the parameters is wParam followed by LOWORD(lParam) then HIWORD(lParam). Unused parameters are not passed. Any handles that are wrapped by MFC classes are converted to pointers to the appropriate MFC objects. The following example shows how to handle the WM_PAINT message and cause the CMyWnd::OnPaint function to be called:

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)

//{{AFX_MSG_MAP(CMyWnd)

ON_WM_PAINT()

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

The message map table must be defined outside the scope of any function or class definition. It should not be placed within an extern "C" block.

Note: ClassWizard will edit the message map entries that are found between the //{{ and //}} comment bracket.

User Defined Windows Messages

User-defined messages may be included in a message map by using the ON_MESSAGE macro. This macro accepts a message number and a member function of the form:

    // inside the class declaration

    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);

For example:

    #define WM_MYMESSAGE (WM_USER + 100)

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)

ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)

END_MESSAGE_MAP()

In this example, we establish a handler for a custom message with a Windows message ID derived from the standard WM_USER base for user-defined messages. You might invoke this handler with code such as:

CWnd* pWnd = ...;

pWnd->SendMessage(WM_MYMESSAGE);

The range of user defined messages using this approach must be in the range WM_USER to 0x7fff.

Note: ClassWizard does not support entering ON_MESSAGE handler routines from the ClassWizard user interface: you must manually enter them from the Visual Workbench editor. Once entered, ClassWizard will parse these entries and allow you to browse them just like any other message-map entries.

Registered Windows Messages

The ::RegisterWindowMessage function is used to define a new window message that is guaranteed to be unique throughout the system. The macro ON_REGISTERED_MESSAGE is used to handle these messages. This macro accepts a the name of a near UINT variable that contains the registered windows message ID. For example

class CMyWnd : public CMyParentWndClass

{

public:

    CMyWnd();

    //{{AFX_MSG(CMyWnd)

    afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

};

static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)

    //{{AFX_MSG_MAP(CMyWnd)

    ON_REGISTERED_MESSAGE(WM_FIND, OnFind)

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

The registered Windows message ID variable (WM_FIND in the example above) must be a NEAR variable because of the way ON_REGISTERED_MESSAGE is implemented.

The range of user defined messages using this approach will be in the range 0xC000 to 0xFFFF.

Note: ClassWizard does not support entering ON_REGISTERED_MESSAGE handler routines from the ClassWizard user interface, you must manually enter them from the Visual Workbench. Once entered, ClassWizard will parse these entries and allow you to browse them just like any other message-map entries.

Command Messages

Command messages from menus and accelerators are handled in message maps with the ON_COMMAND macro. This macro accepts a command ID as well as a member function. Only the specific WM_COMMAND message with a wParam equal to the specified command ID is handled by the member function specified in the message-map entry. Command handler member functions take no parameters and return void. The macro has the form:

ON_COMMAND(id, memberFxn)

Command update messages are routed through the same mechanism as ON_COMMAND handlers. The ON_UPDATE_COMMAND_UI macro is used instead. Command update handler member functions take a single parameter, a pointer to a CCmdUI object, and return void. The macro has the form

ON_UPDATE_COMMAND_UI(id, memberFxn)

An extended form of command message handlers is available for advanced uses. The ON_COMMAND_EX macro is used instead and provides a superset of the ON_COMMAND functionality. Extended command-handler member functions take a single parameter, a UINT containing the command ID, and return a BOOL. The BOOL return should be TRUE to indicate that the command has been handled, otherwise routing will continue to other command target objects.

Examples of the above forms:

  inside RESOURCE.H (usually generated by App Studio)

#define    ID_MYCMD      100

#define    ID_COMPLEX    101

  inside the class declaration

afx_msg void OnMyCommand();

afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);

afx_msg BOOL OnComplexCommand(UINT nID);

  inside the message map definition

ON_COMMAND(ID_MYCMD, OnMyCommand)

ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)

ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)

  in the implementation file

void CMyClass::OnMyCommand()

{

    // handle the command

}

void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI)

{

    // set the UI state with pCmdUI

}

BOOL CMyClass::OnComplexCommand(UINT nID)

{

    // handle the command

    return TRUE;

}

Note: ClassWizard supports creating ON_COMMAND and ON_UPDATE_COMMAND_UI handlers, but it does not support creating ON_COMMAND_EX handlers. However, Class Wizard will parse and allow you to browse all three command handler variants.

Control Notification Messages

Messages that are sent from child controls to a window have an extra bit of information in their message map entry: the control's ID. The message handler specified in a message map entry is called only if (1) the control notification code (high word of lParam), such as BN_CLICKED, matches the notification code specified in the message-map entry and (2) control ID (wParam) matches the control ID specified in the message-map entry.

Custom control notification messages may use the ON_CONTROL macro to define a message map entry with a custom notification code. This macro has the form

ON_CONTROL(wNotificationCode, id, memberFxn)

ClassWizard does not support creating an ON_CONTROL handler in the user interface; you must manually enter them with the Visual Workbench editor. Once entered, ClassWizard will parse these entries and allow you to browse them just like any other message map entries.


Debugging Trace Options
Technical Note 7: Debugging Trace Options

To help debug windows programs, MFC provides a TRACE output mechanism. This will display, to a debugging output window or console, messages about the internal operation of the MFC library as well as warnings and errors if something goes wrong in your application.

Turning on TRACE output is strongly recommended, since it will sometimes give you advanced warning of problems that are about to occur or give more verbose reasons why problems do occur.

How to Turn on TRACE Output

By default TRACE message output is disabled. In order for trace output to be seen, you must perform the following steps:

 Compile your program with the _DEBUG symbol defined and link with a debug version of the MFC library. Debugging and trace options are only available in the _DEBUG version of the library.

 Enable the afxTraceEnabled flag. There are several ways to do this, but using the TRACER.EXE utility is highly recommended.

 Customize the afxTraceFlags to the level of detail you would like to see in trace messages. Use of the TRACER.EXE utility is highly recommended.

Where the Output Goes

When afxTraceEnabled is TRUE, then TRACE output (and default afxDump output) will go to the output window if present. When afxTraceEnable is FALSE, TRACE output and afxDump output will not be displayed.

If a debugger is present, then TRACE output will go to the debugger's output window. If no debugger is present, then TRACE output will not be seen.

For console applications, the debug output will go to 'stderr'.

Using TRACER.EXE

TRACER.EXE is a small MFC program that allows you to set the options in AFX.INI. TRACER is installed in your \MSVCNT\BIN directory by the Visual C++ setup. The source code for TRACER is provided as well, so you can rebuild it and customize it as you wish.

A sample AFX.INI file is provided in the \MSVCNT\MFC\SRC subdirectory. This .INI file turns on diagnostic messages and uses the standard options. You should place this AFX.INI file in your Windows directory or run the TRACER.EXE program to create a new AFX.INI file and set options in it using a dialog box.

Any changes to AFX.INI will take effect in any debug MFC application launched after the changes are saved.

The global integer afxTraceFlags is used to turn on the built-in reporting features of the MFC and to store all flags. It can be set under program control or with the debugger. The global integer afxTraceFlags uses each bit to select a trace reporting option.

You can turn any bit on or off as desired. Try playing with them to get a flavor of the report information they generate. See the AFXWIN.H header for the numeric values of the different options.

// example under program control

afxTraceFlags = 4 + 8;        // windows message dumping

Ease of Reading

For ease of reading, certain frequent messages are not reported. These include:

mouse move messages (non-client and client)

WM_NCHITTEST

WM_SETCURSOR

WM_ENTERIDLE

WM_CTLCOLOR

These messages are sent every time the mouse moves or during idle processing or other common dialog processing.


MFC OLE Support
Technical Note 8: MFC OLE Support

This note gives additional details about the Object Linking and Embedding (OLE), version 1.0, support provided by the MFC library. You should read the OLE overview documentation in Chapter 18 of the Class Library Users Guide as well as the Class Library Reference for the OLE classes.

If you are trying to upgrade an MFC 1.0 application that uses OLE to the current version of MFC, please refer to Technical Note 18.

The first section gives an overview of terminology and describes how the MFC/OLE classes provide an interface to the system level OLE libraries. The second section gives additional information for building MFC/OLE client applications. The third section gives additional information for building MFC server applications.

MFC/OLE Terminology

MFC/OLE uses the term "item" to indicate an OLE object that supports the "StdFileEditing" protocol (to avoid confusion with the C++ and MFC term "object"). Terminology specific to Microsoft Foundation Class Library applications include:

 Compound Document: a document containing native data as well as embedded or linked OLE items.

 Client Application: an application that accepts, displays, and stores OLE items. The items themselves can contain any kind of data.

 Server Application: an application that can edit or otherwise manipulate an OLE item in response to a request from a client application.

 Mini-Server Application: an OLE server application that supports editing embedded OLE items only. It typically has an SDI user interface and supports multiple instances.

 Full-Server Application: an OLE server application that supports editing and storing embedded OLE items, and usually supports linked OLE items. A full server typically has an Multiple Document Interface (MDI) user interface.

MFC and OLE

OLE Applications use three dynamic-link libraries (DLL), OLECLI32.DLL, OLESVR32.DLL, and SHELL32.DLL, to implement OLE. Object Linking and Embedding is supported by OLECLI32.DLL (for clients) and OLESVR32.DLL (for servers). The system registration database is supported by SHELL32.DLL.

The MFC library support for OLE makes some simplifying assumptions to make the common OLE services easier to implement. For those familiar with the existing C-based OLE API, please read the next section, which describes the differences between MFC/OLE and the system OLE interface.

Here are the basic differences between MFC/OLE and the system OLE API:

Language:

MFC/OLE	C++

OLE API	C

Library Interface:

MFC/OLE	C++ classes

OLE API	Handles, and manually built jump tables (_VTBLs)

Server requests:

MFC/OLE	Synchronous
(that is, MFC/OLE automatically handles OLE_WAIT_FOR_RELEASE)

OLE API	Mostly synchronous, but may be asynchronous

Special case handling for errors, retry:

MFC/OLE	Provided by library

OLE API	Provided by your program

Return codes:

MFC/OLE	As with standard MFC/Windows API, return useful values
Throw exceptions for abnormal conditions.
OLE API	Return OLESTATUS all the time.

User Interface Support:

MFC/OLE	"Insert New Object" dialog,
"Links" dialog, and
"Object" menu item.

OLE API	none.

Other notes

MFC/OLE does not support "Object Handlers" (that is, DLLs that are more efficient than launching a server application)

MFC/OLE assumes there is one OLECLIENT for each OLEOBJECT. (This is part of the OLE API design, but wasn't reflected in old source code examples.)

Users of MFC/OLE do not need to build manual VTBLs or call MakeProcInstance as described by the OLE API. (The MFC libraries do all that low-level binding for you.)

MFC/OLE does not currently support the "Paste Special" dialog.

MFC/OLE does not provide C++ interfaces to all of the OLE API functions, just the most useful ones.

MFC/OLE is built on top of the OLE API and DLLs. It is easy to call additional OLE APIs or use the underlying OLE API if needed.

Since MFC/OLE is built on top of the OLE API, the underlying transport mechanism is the same as all other OLE Applications; that is, dynamic data exchange (DDE is still the communication standard). MFC/OLE apps work with any other OLE applications.

MFC/OLE Classes

MFC/OLE splits the OLE API functionality into two disjoint sets of classes: one set for client programs and the other set for server programs. If a program wants to be both a client and a server, it will use all the OLE classes. The interface to all MFC/OLE classes is contained in the one C++ header file AFXOLE.H that includes the standard OLE API C header file OLE.H.

With the exception of COleException and COleTemplateServer, you must derive your own classes from each of these MFC/OLE base classes in order to build a working application.

Many of the member functions in the MFC/OLE classes are protected, and some are pure virtual. You are responsible for implementing some overridable member functions even though you will rarely if ever call them directly. Please refer to the Class Library Reference for details about specific member functions.

The following shows the relationship between the library classes and your derived classes:

 for all OLE applications:

COleException (use directly)

 for a client application:

COleClientDoc

<< your client document class >>

COleClientItem

<< your client item class >>

 for a mini-server application:

COleServer

<< your server class >>

COleServerDoc

<< your server document class >>

COleServerItem

<< your server item class >>

 for a full-server application:

COleTemplateServer (use directly)

COleServerDoc

<< your server document class >>

COleServerItem

<< your server item class >>

The following diagram shows the relationships between MFC/OLE objects:

     CLIENT APPLICATION              SERVER APPLICATION

    client application               COleServer (or COleTemplateServer)

        \                                     /

        COleClientDoc                  COleServerDoc

           \                             /

            COleClientItem < ----- > COleServerItem

The relationship of objects in a client application is as follows:

 A client application can have zero or more COleClientDocs.

 Each COleClientDoc can have zero or more COleClientItems.

 Each COleClientItem represents an OLE embedded, linked or static item.

The relationship of objects in either type of server application (mini or full) is as follows:

 While being edited by a server application, an embedded OLE item is represented by one COleServerDoc paired with one COleServerItem. It is the COleServerItem that will be serialized/saved with the client's data.

The relationship of objects for a mini-server application is as follows:

 Each instance of a mini-server application will have one COleServer that will create one COleServerDoc paired with one COleServerItem. It is the COleServerItem that will be serialized/saved with the client's data.

The relationship of objects for a full-server application is as follows:

 A full-server application will have one COleTemplateServer object for each document type supported.

 The class COleTemplateServer is typically used for each document type although any class derived from COleServer can be used.

 Each COleTemplateServer can have zero or more currently opened COleServerDoc objects.

 When a full-server application edits an embedded OLE item, it will have one COleServerDoc paired with one COleServerItem. It is the COleServerItem that will be serialized/saved with the client's data.

 Each COleServerDoc can have zero or more COleServerItems. In the case of editing an embedded OLE item, there will be one COleServerItem paired with the COleServerDoc.

 For a COleServerDoc supporting links, each active OLE link will have one COleServerItem.

 COleServerItems, or CDocItems in general, can be used for non-OLE purposes or for server data that is not currently communicating with a client application.

Note: The connection between COleClientItem and COleServerItem in the above diagram is for illustrative purposes only. The actual connection is through a Dynamic Data Exchange (DDE) link that is established and managed by the OLE system DLLs. A client application should never directly call a member function of a OLE Server class (COleServer, COleServerDoc or COleServerItem). Similarly, a server application should never directly call a member function of an OLE client class (COleClientDoc or COleClientItem).

Notes for Building Client Applications

Additional Features

There are many advanced features of an OLE client application that you can implement on top of MFC/OLE. These features are minimally supported in the MFC/OLE classes and are not well documented or illustrated in the existing sample programs. Such features will be added into future versions of MFC/OLE.

Additional information on the following features can be found in the OLE documentation.

 Paste Special dialog [image] (all the interfaces are there, but no canned dialog exists to make this easy to implement).

 Full support for UNDO [image] (COleClientItem::CreateCloneFrom is provided, but a client application must still do a lot of extra work.

 Full range of item-creation options (for example creating from template, or creating from file. Easy to add in your class derived from COleClientItem if needed, but this functionality is usually done with the MFC CFile and CArchive classes.

 Clients using the server data for more than pictures. In general a client will create an embedded/linked item with the standard draw option (olerender_draw). This means the client will just draw the embedded/linked item in a rectangle on the screen. Other render options are available.

 After loading a document which has manual links, the client application should prompt users to see if they want to update links now.

Checking Errors and Catching Exceptions

Abnormal conditions, like out of memory or can't launch server, will result in a COleException being thrown. A COleException object can be examined to see what OLESTATUS error code caused the problem.

If a set of conditions are expected, the MFC/OLE member function will return a BOOL. If the BOOL returns FALSE, a more detailed error report can be determined by COleClientItem::GetLastStatus, which returns the OLESTATUS of the last OLE API function call.

Asynchronous Requests

The member function COleClientItem::WaitForServer is used to synchronize the client with the server. If a server request can't be finished immediately, the client application will wait inside of WaitForServer until the operation is complete (successful or not). The client code calling this sees only a simple synchronous call.

While the client application is waiting, it will continue to dispatch Windows messages. The static member function COleClientItem::InWaitForServer can be used to see if the client application is already nested in a server request. See the OCLIENT sample application for an example of the use of InWaitForServer.

Advanced users may wish to override WaitForServer and customize its behavior.

Building

If you are already building a normal MFC Windows application, you must take the following additional steps:

 Include <afxole.h> in those source files using MFC/OLE.

 Link with the appropriate MFC library as usual. You will also need to be sure link with the following additional libraries:

OLECLI32.LIB   - interfaces to the client-side OLE APIs

SHELL32.LIB    - interfaces to the system registration API

COMDLG32.LIB  - interfaces to the common dialog API

 Include the standard client resources by including "afxolecl.rc" in your client application's .RC file. This includes the dialog templates and standard strings for the User-Interface parts of the OLE support.

See Technical Note 23 for more information on standard MFC resources.

Debugging

See Technical Note 7 for a description of the afxTraceFlags options for doing advanced debugging. The multi-application debugging option (option 1) is extremely useful, especially if you are debugging a client and a server at the same time. The "Main Message Pump" TRACE output is very useful when you are trying to figure out which messages are being dispatched.

The "OLE Tracing" TRACE option is provided specifically for OLE debugging, and turns on some rather verbose reporting messages informing you what is going on in the MFC/OLE classes.

Sample Code

The OCLIENT sample is a simple sample OLE client application. It exploits all of the user interfaces provided by MFC/OLE. It is an MDI application that supports one document and one view type. OLE items are stored in the document, are drawn in the view's client area, and can be moved and resized with the mouse.

Notes for Building Server Applications

Additional Features

There are many other advanced features of an OLE Server you may wish to consider:

 The ability to support multiple data formats. MFC/OLE defaults to supporting "native" format and drawing as a metafile (CF_METAFILEPICT). Text format (CF_TEXT) is supported if your COleServerItem derived class overrides OnGetTextData to return TRUE. Override one or all of COleServerItem::OnSetData, COleServerItem::OnGetData or COleServerItem::OnEnumFormats to customize this behavior.

 OLE servers do not have to be visual or have a complete user interface. For example, Note-it and sound players are simple servers that support embedding. You will want to override COleServerItem::OnDoVerb to customize this behavior.

 OLE servers can support multiple types. For example the Microsoft Excel application supports charts and spreadsheets. Add additional CMultiDocTemplate and COleTemplateServer objects for each document type.

 OLE servers may also support execution of generic DDE execute commands. Override COleServerDoc::OnExecute or COleServer::OnExecute to customize this behavior.

 It is possible to support links but not embedding, although this is not very typical.

 MFC/OLE does not support OLE Object Handler DLLs (that is, DLLs for doing more efficient server operations). OLE Object Handlers DLLs can be written using the OLE API and may talk to an OLE server application written with MFC/OLE.

Checking Errors and Catching Exceptions

Some callbacks from the server or server document or server items will return OLESTATUS codes. These should follow the conventions of the OLE API (that is, return OLE_OK if valid and some other OLESTATUS code on error, for example,: OLE_ERROR_GENERIC).

Other callbacks that return more useful information may throw exceptions in the server or if possible just return a NULL value (for example, for OnCreateDoc in the COleServer).

Asynchronous Requests

When inside a callback from a server or server document or server item you should not do any modal operation or anything that will cause your application to call GetMessage or PeekMessage. The debugging version of the MFC CWinApp class has special asserts that will catch scenarios where server code is calling the main message pump when it should not.

Building

If you are already building a normal MFC Windows application, you must take the following additional steps:

 Include <afxole.h> in those source files using MFC/OLE.

 Link with the appropriate MFC library as usual. You will also need to be sure link with the following additional libraries:

OLESVR32.LIB    - interfaces to the server side OLE APIs

SHELL32.LIB     - interfaces to the system registration database

 For full-servers, you must include the standard server resources by including "afxolesv.rc" in your server application's .RC file. This includes the standard strings for the User Interface parts of the OLE support.

See Technical Note 23 for more information on standard MFC resources.

Debugging

See above for information about debugging OLE Client applications and  Technical Note 7 for general tips on debugging MFC applications.

Sample Code

The MINSVR sample provides an example of an OLE mini-server. This is a migrate version of the MFC 1.0 MINSVR sample and shows a simple circle/oval containing text.

The HIERSVR sample provides an example of an OLE full-server. It is an MDI application that displays hierarchy diagrams, and it can be used for simple organization charts, or class hierarchy diagrams. It uses the COleTemplateServer class and provides support for linked and embedded items.


Using MFC as Part of a DLL
Technical Note 11: Using MFC as Part of a DLL

This note describes how you can use the MFC library as part of a Windows dynamic-link library (DLL). It assumes you are familiar with Windows DLLs and how to build them.

Interfaces

This note describes simplified DLL support that assumes interfaces between the application and the DLL are specified in normal C-like functions or explicitly exported classes. MFC class interfaces are not automatically exported.

If both a DLL and an application want to use MFC, then they will both have a copy of the MFC library statically linked into them. In fact, the versions of the libraries will be different. The application uses one of the standard versions of the MFC library (depending on the memory model, say 'nafxcw.lib'), while the DLL uses a special version of the MFC ('nafxdwd.lib').

This has several advantages:

 the application using the DLL does not have to use MFC or, for that matter, it does not have to be a Visual C++ application.

 the memory model of the application can be different from the DLL. For example, MFC user DLLs (_USRDLL) are large model, but the application using the DLL can be medium model.

 the size of the DLL depends only on those MFC and C runtime routines that are used and linked by the linker. Therefore, the size of a _USRDLL version DLL is only slightly bigger than the exact same code in a large model application.

 there are no problems with classes changing underneath you. Your DLL design exports only those interfaces you wish it to.

 if both DLL and application use MFC, there are no problems with the application wanting a different version of MFC than the DLL (or vice versa). Since the MFC library is statically linked into each DLL or EXE, there is no question about which version you have.

API Limitations

Some of the C runtime functions are not supported in DLLs. As a result, the following MFC APIs are not available in DLL-compatible libraries:

 CTime::Format

 CTime::FormatGmt

 CTimeSpan::Format

The MFC classes assume large model for the _USRDLL version. As with any DLL, it also assumes "SS != DS" (SS points to the application's stack segment, while DS points to the DLL's data segment.)

Building your DLL

When compiling your Windows DLL, the symbols "_USRDLL" and "_WINDLL" must be defined. Your DLL code must also be compiled with the following compiler switches:

/ALw	large model, SS!=DS

/GD	signifies you are a Windows DLL (and causes the compiler to automatically define _WINDLL)

/D_USRDLL	specifies you are building a DLL using MFC

The interfaces between the application and the DLL must be explicitly exported. It is recommended that you define your interfaces to be low bandwidth, sticking to C interfaces where possible. More direct C interfaces are easier to maintain than more complex C++ classes.

Place your interfaces in a separate header that can be included by both C and C++ files (that way you won't limit your DLL customers to C++ programmers). See the header TRACEAPI.H in the DLLTRACE sample program for an example.

Make sure these interfaces are declared as "FAR PASCAL _export" routines. If you want your interfaces to work for mixed-model client applications, add explicit "FAR" keywords for data pointers.

WinMain->LibMain

The MFC library defines the standard Windows LibMain entry point that initializes your CWinApp derived object as in a normal MFC application. Place all DLL-specific initialization in the InitInstance member function as in a normal MFC application.

Note that the CWinApp::Run mechanism doesn't apply to a DLL, since the application owns the main message pump. If your DLL brings up modeless dialogs or has a main frame window of its own, your application's main message pump must call a DLL-exported routine that calls CWinApp: PreTranslateMessage.

See the DLLTRACE sample for use of this function.

The ExitInstance member function of your CWinApp derived class will be called from the MFC provided _WEP function (which is called from the C++ runtime library when WEP is called) just as in a DLL running under Windows.

What to do to Link it all Together

You must do a one-time build of the _USRDLL version of the MFC library. The USRDLL version of the MFC library is built with the command line:

nmake MODEL=N TARGET=W DLL=1 DEBUG=1

for the debugging version of the library 'nafxdwd.lib, or

nmake MODEL=N TARGET=W DLL=1 DEBUG=0

for the retail version of the library 'nafxdw.lib.'

You must link your DLL with this library (nafxdwd or nafxdw) along with the version of the C runtimes called 'libc.lib'.

Sample Code

Please see the MFC sample program directory DLLTRACE for a complete sample. This includes a simple DLL called 'TRACER.DLL' that implements the AFX Trace flags dialog (see Technical Note 7). It also has a simple HELLO application that calls the DLL to use the dialog.

Several interesting thing to note:

 The memory model and compiler flags of the DLL and the application are very different.

 The link lines and .DEF files for the DLL and the application are also very different.

 Even though MFC DLLs must be large model, the application that uses them can be any memory model you like.

 The application using the DLL doesn't even have to be in C++.

 The interface between the application and the DLL is a "C"-like API with the compiler _export keyword set [image] see TRACEAPI.H in the DLLTRACE sample program.

The following extract from TRACEAPI.H illustrates what is needed for one API that is defined in a _USRDLL DLL:

#ifdef __cplusplus

extern "C" {

#endif  /* __cplusplus */

struct TracerData

{

    BOOL    bEnabled;

    UINT    flags;

};

BOOL FAR PASCAL _export PromptTraceFlags(TracerData FAR* lpData);

#ifdef __cplusplus

}

#endif

 In this example, the declaration is enclosed in an 'extern "C" { }' block for C++ users. This has several advantages. First, it makes your DLL APIs usable by non-C++ client applications. Second, it reduces DLL overhead since C++ name mangling will not be applied to the exported name. Lastly, it makes it easier to explicitly add to a .DEF file (for exporting by ordinal) without having to worry about name mangling.

 All API functions are "FAR PASCAL _export". This will generate the correct prologue/epilogue sequence for the DLL entry points when the /GD compiler switch is used to build the DLL. There is no need for any archaic MakeProcInstances or _loadds entry points.

 The structures used by the API are not derived from MFC classes and are defined completely in the API header. This reduces the complexity of the interface between the DLL and the application and, once again, makes the DLL usable by C programs as well.

 Any data pointers used in the API are explicit FAR pointers. Since the DLL is compiled large model, data pointers are already FAR pointers by default. Adding the extra FAR keyword will allow your client applications to be small or medium model without having to change your header. Never have NEAR pointers in an interface for a DLL.


Using MFC with Windows 3.1 Robustness Features
Technical Note 12: Using MFC with Windows 3.1 Robustness Features

Note:

This Technical Note was written for Windows 3.1.  Windows NT implements most of these features.

Windows 3.1 is a major improvement over Windows 3.0 in the area of robust application development. Windows 3.1 includes a number of new features that enhance the reliability of a Windows application. This technical note describes the use of these features within the MFC library.

These features include the debug kernel, STRICT type checking, diagnostics and memory management, and the WINDOWSX.H enhancements. The Professional Edition file \MSVCNT\WINDOWS.TXT includes additional information on these features.

Windows 3.1 Debug Kernel

Note: This section applies only to the Professional Edition of Microsoft Visual C++. The Standard Edition does not include the Debug Kernel.

Testing your MFC application with the debug system executables is probably the best thing you can do to make sure your applications are robust and reliable. The debugging versions of the system executables perform all sorts of useful error checking for you, informing you of any problems that arise with debug output messages.

The best way to use the debug system is with two machines: a machine for testing and debugging that has the debug system installed and a machine for development. One machine, your test machine, should always run with the debug kernel. The other machine, your development machine, should run with the non-debug kernel. The output from the debug kernel can be sent to the primary machine over a null modem line. If you have only a single machine, then you should be sure to run the debug kernel (there is a slight performance degradation). The output from the debug kernel can be routed to DBWIN, a tool included with the Professional Edition of Microsoft Visual C++. In addition, the Visual Workbench's Output window will receive output when running under the debugger.

A useful trick for single-machine debugging is to place copies of the system and debug binaries and symbols in a separate directory and to have batch files that copy the appropriate files to your Windows System directory. This way you can exit Windows and switch back and forth quickly between debug and non-debug. The Professional Edition installation program will set this up, then you can switch between debug and nondebug version of Windows with the D2N.BAT and N2D.BAT batch files.

If you aren't running with a debugger or a debug terminal, you should run the DBWIN application so you can see the error and warning messages produced by the debug system. This application is included with the Professional Edition.

Below are some common programming errors that appear frequently in shipped Windows applications. Many of these problems can cause random system UAEs and other problems under Windows 3.0. The debug system binaries will help you track down problems, such as:

Passing invalid parameters of all shapes and sizes.

Accessing nonexistent window words. In Windows 3.0, a SetWindowWord or SetWindowLong call past the end of the allocated window words (as defined with RegisterClass) would trash internal window manager data structures!

Using handles after they've been deleted or destroyed.

Using a device context (DC) after it has been released.

Deleting Graphics Device Interface (GDI) objects before they're deselected from a DC.

Forgetting to delete GDI bitmaps, brushes, pens, or other GDI or USER objects when an application terminates.

Writing past the end of an allocated memory block.

Reading or writing through a memory pointer after it has been freed.

Forgetting to export window procedures and other callbacks.

Forgetting to use MakeProcInstance with dialog procs and other callbacks. (The suggested technique is to make use of Microsoft C/C++ feature of marking these functions as EXPORT in their definition.)

Shipping an application without running it with the debugging KERNEL, USER, and GDI.

MFC Diagnostics

In addition, the Microsoft Foundation Classes ship with a set of robustness features that are compiled and linked only in the debug build of the library (those library variants ending with a 'd'). Use of these features in the applications you write and in the classes you design will greatly improve both the runtime and compile time error trapping of your application. These functions are outlined below, but all are documented in the Class Library Reference manual.

Every class in MFC implements a Dump member function, which permits you to view the state of an object in an ASCII format. This function can be called from CodeView or placed within #ifdef _DEBUG /#endif portions of your code. A helper function AfxDump is included in the debug library just for this purpose. It is called with a single parameter, a CObject*. You can call this function from CodeView to print out the argument. You should supply a Dump member for classes that you implement. As with AssertValid, you should first explicitly call your base class Dump member function. The output of Dump is routed to the standard AFX CDumpContext, afxDump, which by default goes to the CodeView output window or to your debug terminal. You can also use the DBWIN program to view the output of afxDump. The source file \MSVCNT\MFC\SRC\DUMPINIT.CPP includes information on how to route afxDump to another destination.

TRACE, a macro that behaves much like printf, only routes output to the afxDump location. You should use TRACE statements to indicate tricky or exceptional places in your code. As with other robustness features, TRACE is only meaningful in the debug library, and has no effect in the retail build. The MFC library includes a number of built in TRACE statements for tracking the flow of messages. Please see Technical Note 7 for more information on debug trace.

ASSERT is a run-time check for the validity of a statement. You should use ASSERTs liberally throughout your program. Any place you have a comment to the effect:

// lpStr should be NULL at this point

you should replace that with a run-time assert:

ASSERT(lpStr == NULL);

The compiler cannot understand the comment, but it can evaluate the expression in the assertion macro. ASSERT statements have no effect in retails builds. If you need the information from ASSERT in the retail build, then use the VERIFY macro.

MFC also includes an extensive diagnostic memory allocator. You use the diagnostic memory allocator to check that you free all memory resources before your program exits. The diagnostic allocator will track the source file and line number of an allocation, so if you use the CMemoryState::DumpAllObjectsSince API, you can locate any allocations that remain.

When your application terminates, MFC will, by default, print out a list of all objects that have not been deallocated.

Windows 3.1 STRICT Type Checking

STRICT type checking is an option available with the WINDOWS.H 3.1 header file. MFC uses these STRICT types by default, and you must use them if you are building an MFC application. If you do not wish to use the STRICT types, then you must rebuild the MFC libraries that you need with the additional compiler option /DNOSTRICT. For example, if you wanted a non-STRICT medium model debug library, you would use the following command line (be sure that your INCLUDE, LIB, and PATH environment variables are set correctly):

NMAKE MODEL=M DEBUG=1 OPT=/DNOSTRICT

Typesafe Linkage and STRICT

In C++ you are permitted to have many functions with the same name, as long as these functions have different formal parameter lists. In order to have unique link symbols, the C++ compiler will "decorate" these names using an algorithm that encodes information about a function such as the name, number, and type of formal parameters, calling convention, etc.

This newly generated name is used as the external link symbol for the function. This is known as typesafe linkage and is a big benefit of C++. This name decoration does not apply to functions within an extern "C" block, and that is why all APIs in WINDOWS.H are in such a block.

STRICT type checking in WINDOWS.H enhances type safety for Windows programs by using distinct types to represent all the different HANDLES in Windows. So for example, STRICT prevents you from mistakenly passing an HPEN to a routine expecting an HBITMAP.

Since the Windows APIs are all within extern "C" { } blocks, they are not decorated in the manner described above. STRICT changes the types of the various Windows typedefs to make them unique (specifically it uses different pointer types to represent HANDLEs, which cannot be freely converted without an explicit cast).

As you can see, if you have STRICT type checking enabled in one file, but not in another, the C++ compiler will generate different external link symbols for a single function. This will result in link-time errors. Therefore, it is recommended that you use STRICT type checking only for C modules (those that end in .C). Additionally, STRICT is a compile-time only option, so once you successfully compile your code, the benefits of STRICT are completely realized.

If you are mixing STRICT and non-STRICT code, you must be aware of linkage inconsistencies. In general, all MFC programming and all C++ should be done with STRICT. If you have legacy C code, then not using STRICT is acceptable.

Windows 3.1 WINDOWSX.H Header File

New with Windows 3.1 and Windows NT is the WINDOWSX.H header file that supports various extensions to the C-coding style for Windows programmers using C. These macro APIs, message crackers, and control APIs are defined in the file WINDOWSX.H.

This syntax is primarily designed for C programmers. MFC 2.0 supports the use of WINDOWSX.H, so if you have existing code that relies on these tensions, use this code unmodified in MFC. You will find, however, that MFC has comparable idioms for all of the features of WINDOWSX.H, and uses the C++ language to accomplish these tasks with more semantic and architectural safety.

To use WINDOWSX.H, be sure to #include it before you have included AFXWIN.H (or STDAFX.H if you are using the AppWizard structure).

The only caveat is that there are two WINDOWSX.H APIs that collide with the MFC C++ APIs. The two APIs SubclassWindow and CopyRgn are not available for use within MFC. You will need to recode these to use either the MFC API (and classes) or to call the Windows API directly. You can also code your own macro as long as it has a different name.


Custom Controls
Technical Note 14: Custom Controls

This note describes the MFC Support for custom and self-drawing controls. Dynamic subclassing is also described. General advice on ownership of CWnd objects vs. HWNDs is presented.

The MFC sample application CTRLTEST illustrates many of these features. Please refer to the source code for that sample (in \MSVCNT\MFC\SAMPLES\CTRLTEST) and online help.

Owner-Draw Controls/Menus

Windows provides support for "owner draw" controls and menus. These are Windows messages sent to a parent window of a control or menu that allow you to customize the visual appearance and behavior of the control or menu.

MFC directly supports owner draw with the message map entries:

CWnd::OnDrawItem

CWnd::OnMeasureItem

CWnd::OnCompareItem

CWnd::OnDeleteItem

You can override these in your CWnd-derived class (usually a dialog or main frame window) to implement the owner-draw behavior.

This approach does not lead to reusable code. If you have two similar controls in two different dialogs, you must implement the custom control behavior in two places. The MFC-supported self-drawing control architecture solves this problem.

Self-Drawing Controls and Menus

MFC provides a default implementation (in CWnd and CMenu) for the standard owner-draw messages. This default implementation will decode the owner-draw parameters and delegate the owner-draw messages to the controls or menu. This is called "self-draw" since the drawing (/measuring/comparing) code is in the class of the control or menu, not in the owner window.

This allows you to build reusable control classes that display the control using "owner draw" semantics. The code for drawing the control, not the owner of the control, is in the control class. This is an object-oriented approach to custom control programming.

For self-draw buttons:

CButton:DrawItem(LPDRAWITEMSTRUCT);

        // draw this button

For self-draw menus:

CMenu:MeasureItem(LPMEASUREITEMSTRUCT);

        // measure the size of an item in this menu

CMenu:DrawItem(LPDRAWITEMSTRUCT);

        // draw an item in this menu

For self-draw list boxes:

CListBox:MeasureItem(LPMEASUREITEMSTRUCT);

        // measure the size of an item in this list box

CListBox:DrawItem(LPDRAWITEMSTRUCT);

        // draw an item in this list box

CListBox:CompareItem(LPCOMPAREITEMSTRUCT);

        // compare two items in this list box if LBS_SORT

CListBox:DeleteItem(LPDELETEITEMSTRUCT);

        // delete an item from this list box

For self-draw combo boxes:

CComboBox:MeasureItem(LPMEASUREITEMSTRUCT);

        // measure the size of an item in this combo box

CComboBox:DrawItem(LPDRAWITEMSTRUCT);

        // draw an item in this combo box

CComboBox:CompareItem(LPCOMPAREITEMSTRUCT);

        // compare two items in this combo box if CBS_SORT

CComboBox:DeleteItem(LPDELETEITEMSTRUCT);

        // delete an item from this combo box

For details on the owner-draw structures (DRAWITEMSTRUCT, MEASUREITEMSTRUCT, COMPAREITEMSTRUCT, and DELETEITEMSTRUCT) refer to the MFC documentation for CWnd::OnDrawItem, CWnd::OnMeasureItem, CWnd::OnCompareItem, and CWnd::OnDeleteItem respectively.

Some notes on using self-drawing controls and menus:

For self-drawing menus, you must override both MeasureItem and DrawItem member functions.

For self-drawing list boxes and combo boxes, you must override MeasureItem and DrawItem. You must specify the OWNERDRAWVARIABLE style in the dialog template (LBS_OWNERDRAWVARIABLE and CBS_OWNERDRAWVARIABLE respectively). The OWNERDRAWFIXED style will not work with self-drawing items since the fixed item height is determined before self-drawing controls are attached to the list box. (The Win 3.1 member functions CListBox::SetItemHeight and CComboBox::SetItemHeight can be used to get around this limitation.)

In addition, note that switching to an OWNERDRAWVARIABLE style will affect the NOINTEGRALHEIGHT style. Because the control can not calculate an integral height with variable sized items, the default style of INTEGRALHEIGHT is ignored and the control is always NOINTEGRALHEIGHT. If your items are fixed height, you can prevent partial items from being drawn by specifying the control size to be an integral multiplier of the item size.

For self-drawing list boxes and combo boxes with the SORT style (LBS_SORT and CBS_SORT respectively), you must override the CompareItem member function.

For self-drawing list boxes and combo boxes, DeleteItem is not normally overridden. DeleteItem can be overridden if additional memory or other resources are stored with each list box or combo box item.

Examples of Self-Drawing Controls/Menus

The CTRLTEST sample application (\MSVCNT\MFC\SAMPLES\CTRLTEST) provides samples of a self-draw menu (showing colors) and a self-draw list box (also showing colors).

The most typical example of a self-drawing button is a bitmap button (a button that shows one, two, or three bitmap images for the different states). This is provided in the MFC class CBitmapButton.

Dynamic Subclassing

Subclassing is the Windows term for replacing the WndProc of a window with a different WndProc and calling the old WndProc for default (superclass) functionality.

This should not be confused with C++ class derivation (C++ terminology uses the words "base" and "derived" while the Windows object model uses "super" and "sub"). C++ derivation with MFC and Windows subclassing are functionally very similar, except C++ does not support a feature similar to dynamic subclassing.

The CWnd class provides the connection between a C++ object (derived from CWnd) and a Windows window object (also known as an HWND).

There are three common ways these are related:

CWnd creates the HWND. The behavior can be modified in a derived class. "Class derivation" is done by creating a class derived from CWnd and created with calls to Create.

CWnd gets attached to an existing HWND. The behavior of the existing window is not modified. This is a case of "delegation" and is made possible by calling Attach to alias an existing HWND to a CWnd C++ object.

CWnd gets attached to an existing HWND and you can modify the behavior in a derived class. This is called "dynamic subclassing," since we are changing the behavior (and hence the class) of a Windows object at run time.

This last case is done with the member functions:

CWnd::SubclassWindow

CWnd::SubclassDlgItem.

Both routines attach a CWnd object to an existing Windows HWND. SubclassWindow takes the HWND directly, and SubclassDlgItem is a helper that takes a control ID and the parent window (usually a dialog). SubclassDlgItem is designed for attaching C++ objects to dialog controls created from a dialog template.

Please refer to the CTRLTEST example for several examples of when to use SubclassWindow and SubclassDlgItem.


Using C++ Multiple Inheritance with MFC
Technical Note 16: Using C++ Multiple Inheritance with MFC

This note describes how to use Multiple Inheritance (MI) with the Microsoft Foundation Classes.

Why Multiple Inheritance ?

There is an ongoing debate in the C++ and object-oriented communities over the value of MI.  The Visual C++ compiler and development environment fully supports MI.

The MFC class library has been designed so that you do not need to understand MI to use MFC. MI is not used in any of the MFC classes. We have found that MI is not required to write a class library, nor is it required for writing serious applications. To use MI or not can be a personal decision, so we leave that decision to you.

So You Want to use MI?

If you already understand how to use MI, understand the performance trade-offs, and want to use MFC, this technote will tell you what you must do. Some of the restrictions are general C++ restrictions, others are imposed by the MFC architecture.

The following describes some of the technical issues of how MI affect the use of common MFC idioms. At the end of this technical note a complete MFC application using MI is included which you can extract and compile.

CRuntimeClass

The persistence and dynamic object creation mechanisms of MFC use the CRuntimeClass data structure to uniquely identify classes. MFC associates one structure of this type with each dynamic and/or serializable class in the application. These structures are initialized at application startup time using a special static object of type AFX_CLASSINIT. You need not concern yourself with the implementation of this information, as it is likely to change between revisions of MFC.

The current implementation of CRuntimeClass does not support Multiple Inheritance runtime type information. This does not mean you cannot use MI in your MFC application, but if you do, you will have certain responsibilities when working with objects that have more than one base class.

The CObject::IsKindOf member function will not correctly determine the type of an object if it has multiple base classes. Therefore, you cannot use CObject as a virtual base class, and all calls to CObject member functions such as Serialize and operator new will need to have scope qualifiers so that C++ can disambiguate the appropriate function call. If you do find the need to use MI within MFC, then you should be sure to make the class containing the CObject base class the left-most class in the list of base classes.

For advice on the uses and abuses of MI, see Advanced C++ Programming Styles and Idioms by James O. Coplien (Addison Wesley, 1992).

CObject - The Root of all Classes

As you know, all significant classes derive directly or indirectly from class CObject. CObject does not have any member data, but does have some default functionality. When using MI, it will be common to inherit from two or more CObject-derived classes, for example, a CFrameWnd and a CObList:

class CListWnd : public CFrameWnd, public CObList

{

 ...

};

CListWnd myListWnd;

In this case CObject is included twice, which leads to two problems:

Any reference to CObject member functions must be disambiguated.

myListWnd.Dump(afxDump);

// compile time error, CFrameWnd::Dump or CObList::Dump ?

Static member functions, including 'operator new' and 'operator delete' must also be disambiguated.

Recommended Steps

When creating a new class with two or more CObject derived base classes, reimplement those CObject member that you expect people to use. Operators new and delete are mandatory, Dump is recommended. For example:

class CListWnd : public CFrameWnd, public CObList

{

public:

void* operator new(size_t nSize)

{ return CFrameWnd::operator new(nSize); }

void operator delete(void* p)

{ CFrameWnd::operator delete(p); }

void Dump(CDumpContent& dc)

{ CFrameWnd::Dump(dc);

  CObList::Dump(dc); }

 ...

};

Virtual inheritance of CObject ?

You may ask, "If you inherit CObject virtually, won't all of the ambiguity problems go away?".

Even in the efficient Microsoft Object Model, virtual inheritance is not as efficient as non-virtual inheritance (just as Multiple Inheritance is not as efficient as single inheritance in certain cases). Since there is no member data in CObject, virtual inheritance is not needed to prevent multiple copies of a base class's member data.

The real answer is no, virtual inheritance will not solve the ambiguity problems illustrated above. For example: the Dump virtual member function is still ambiguous (since CFrameWnd and CObList implement it differently).

Therefore we recommend following the steps above to provide disambiguation:

CObject::IsKindOf and Run-time Typing

The runtime typing mechanism supported by MFC in CObject uses the macros DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL and IMPLEMENT_SERIAL. These give the ability to do a run-time type check to allow for safe cast-downs.

These macros only support a single base class and will work in a limited way for multiply inherited classes. The base class you specify in IMPLEMENT_DYNAMIC or IMPLEMENT_SERIAL should be the first (or left-most) base class. For example,

class CListWnd : public CFrameWnd, public CObList

{

    DECLARE_DYNAMIC(CListWnd)

    ...

};

IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)

This will allow you to do type checking for the left-most base class only. The run-time type system will know nothing about additional bases (CObList in this case).

CWnd and Message Maps

In order for the MFC message map system to work correctly, there are two additional requirements:

There must be only one CWnd-derived base class.

The CWnd-derived base class must be the first (or left-most) base class.

In the example above, CFrameWnd is the first base class.

Some examples that will not work:

class CTwoWindows : public CFrameWnd, public CEdit

{ ... };

// error : two copies of CWnd

class CListEdit : public CObList, public CEdit

{ ... };

// error : CEdit (derived from CWnd) must be first

A Sample Program using MI

The following sample is a stand-alone application that consists of one class derived from CFrameWnd and CWinApp. This way of structuring an application is not a recommended, but this is an example of the smallest MFC application with one class.

You can cut the following program and copy it on top of the single-inheritance application HELLOAPP.CPP in \MSVCNT\MFC\SAMPLES\HELLOAPP. Then build the program as you would normally.

BEGIN >>>

#include <afxwin.h>

class CHelloAppAndFrame : public CFrameWnd, public CWinApp

{

public:

CHelloAppAndFrame()

{ }

// Necessary evil for MI disambiguity

void* operator new(size_t nSize)

{ return CFrameWnd::operator new(nSize); }

void operator delete(void* p)

{ CFrameWnd::operator delete(p); }

// Implementation

// CWinApp overrides

virtual BOOL InitInstance();

// CFrameWnd overrides

virtual void PostNcDestroy();

afx_msg void OnPaint();

DECLARE_MESSAGE_MAP()

};

BEGIN_MESSAGE_MAP(CHelloAppAndFrame, CFrameWnd)

ON_WM_PAINT()

END_MESSAGE_MAP()

// since the frame window is not allocated on the heap, we must

// override PostNCDestroy not to delete the frame object

void CHelloAppAndFrame::PostNcDestroy()

{

// do nothing (do not call base class)

}

void CHelloAppAndFrame::OnPaint()

{

CPaintDC dc(this);

CRect rect;

GetClientRect(rect);

CString s = "Hello, Windows!";

dc.SetTextAlign(TA_BASELINE | TA_CENTER);

dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));

dc.SetBkMode(TRANSPARENT);

dc.TextOut(rect.right / 2, rect.bottom / 2, s);

}

// Application initialization

BOOL CHelloAppAndFrame::InitInstance()

{

// first create the main frame

if (!CFrameWnd::Create(NULL, "Multiple Inheritance Sample",

WS_OVERLAPPEDWINDOW, rectDefault))

return FALSE;

m_pMainWnd = this;	// the application object is also a frame window

ShowWindow(m_nCmdShow);

return TRUE;

}

CHelloAppAndFrame theHelloAppAndFrame;

<<< END


Destroying Window Objects
Technical Note 17: Destroying Window Objects

This note describes the use of the CWnd::PostNcDestroy member function. Use this function if you wish to do customized allocation of CWnd-derived objects.

This note also explains some of the reasons for the cardinal rule:

To destroy a C++ Windows object, use DestroyWindow not "delete".

This is important. If you follow the guidelines below, you will have few cleanup problems (such as forgetting to delete/free C++ memory, forgetting to free system resources like HWNDs, or freeing objects too many times).

The Problem

Windows objects (objects of classes derived from CWnd) represent both a C++ object (allocated in the application's heap) and an HWND (allocated in system resources by the window manager). Since there are several ways to destroy a window object, we must provide a set of rules that prevent system resource or application memory leaks and that prevent objects and Windows handles from being destroyed more than once.

This is more than a memory management problem. The presence of a Windows window has user-interface impact: a window drawn on the screen; once it is destroyed there is also an impact on system resources. Leaking C++ memory in your application address space is not as bad as leaking system resources.

Destroying Windows

The two permitted ways to destroy a Windows object are

Calling CWnd::DestroyWindow or the Windows API ::DestroyWindow.

Explicit deletion with the delete operator.

The first case is by far the most common. This case applies even if DestroyWindow is not called directly by your code. The is the case when the user directly closes a frame window (the default WM_CLOSE behavior is to call DestroyWindow), and when a parent window is destroyed, Windows calls DestroyWindow for all the children.

The second case, the use of the delete operator on Windows objects, should be very rare and only in the cases outlined below.

Auto Cleanup with CWnd::PostNcDestroy

When destroying a Windows window, the last Windows message sent to the window is WM_NCDESTROY. The default CWnd handler for that message (CWnd::OnNcDestroy) will detach the HWND from the C++ object and call the virtual function PostNcDestroy. Some classes override this function to delete the C++ object.

The default implementation of CWnd::PostNcDestroy does nothing which is appropriate for window objects allocated on the stack frame or embedded in other objects. This is not appropriate for window objects that are designed to be allocated by themselves on the heap (not embedded in other C++ object).

Those classes that are designed to be allocated by themselves on the heap override the PostNcDestroy member function to perform a "delete this". This statement will free any C++ memory associated with the C++ object. Even though the default CWnd destructor calls DestroyWindow if m_hWnd is non-NULL, this does not lead to infinite recursion since the handle will be detached and NULL during the cleanup phase.

Note: CWnd::PostNcDestroy is normally called after the Windows WM_NCDESTROY message is processed, as part of window destruction, and the HWND and the C++ window object are no longer attached. CWnd::PostNcDestroy will also be called in the implementation of most Create calls if failure occurs (see below for auto cleanup rules).

Auto Cleanup Classes

The following classes are not designed for auto-cleanup. They are normally embedded in other C++ object or on the stack:

All the standard Windows controls (CStatic, CEdit, CListBox, and so on).

Any child windows derived directly from CWnd (for example, custom controls)

Splitter windows (CSplitterWnd)

Default control bars (classes derived from CControlBar, see  Technical Note 31 for enabling auto-delete for control bar objects)

Dialogs (CDialog) - designed for modal dialogs on the stack frame

All the standard dialogs except CFindReplaceDialog

The default dialogs created by ClassWizard

The following classes are designed for auto-cleanup. They are normally allocated by themselves on the heap:

Main frame windows (derived directly or indirectly from CFrameWnd)

View windows (derived directly or indirectly from CView)

If you wish to break any of these rules, you must override the PostNcDestroy member function in your derived class. To add auto-cleanup to your class, simply call your base class and then do a delete this. To remove auto-cleanup from your class, call CWnd::PostNcDestroy directly instead of the PostNcDestroy member in your direct base class.

The most common use of the above is to create a modeless dialog that can be allocated on the heap.

When to Call 'delete'

The recommended way to destroy a Windows object is to call DestroyWindow, either the C++ member function or the global ::DestroyWindow API.

Do not call the global ::DestroyWindow API to destroy an MDI Child window, use the virtual member function CWnd::DestroyWindow instead.

For C++ Window objects that don't perform auto-cleanup, using DestroyWindow instead of delete avoids problems of having to call DestroyWindow in the CWnd::~CWnd destructor where the VTBL is not pointing to the correctly derived class. This can lead to subtle bugs so the diagnostic (debug) version of MFC will warn you with

Warning: calling DestroyWindow in CWnd::~CWnd

   OnDestroy or PostNcDestroy in derived class will not be called

In the case of C++ Windows objects that do perform auto-cleanup, you must call DestroyWindow. If you use operator delete directly, the MFC diagnostic memory allocator will alert you that you are freeing memory twice (the first call to delete as well as the indirect call to "delete this" in the auto-cleanup implementation of PostNcDestroy).

After calling DestroyWindow on a non-auto-cleanup object, the C++ object will still be around, but m_hWnd will be NULL. After calling DestroyWindow on an auto-cleanup object, the C++ object will be gone, freed by the C++ delete operator in the auto-cleanup implementation of PostNcDestroy..


Migrating OLE Applications from MFC 1.0 to MFC 2.0
Technical Note 18: Migrating OLE Applications from MFC 1.0 to MFC 2.0

This note provides guidelines for migrating MFC version 1 applications that use MFC/OLE to MFC version 2. MFC 2.0, like its predecessor, supports OLE 1.

The changes in the class interfaces made between 1 and 2 for most of the Microsoft Foundation classes are relatively minor. For details on how to migrate the non-OLE portions of your application or a non-OLE MFC version 1 application please refer to Technical Note 19.

The OLE Classes have been significantly changed to make it easier to write new MFC/OLE applications. The changes add many new services. You have the option, however, to preserve most of your MFC 1.0 OLE code.

The last section of this note anticipates future versions of OLE (OLE 2).

The changes to the MFC/OLE classes for version 2 of MFC include:

Printed overview and reference documentation

Improved samples

Bug fixes

Integration with the MFC 2.0 document and view architecture

Standard implementations of more UI commands

Some of the more complex details moved out of public interface into implementation details

Easier to write full-servers using COleTemplateServer

OLE now supported in DLL versions. OLE is now available in the _USRDLL packaging options. See Technical Note 11 for more details on the DLL version of MFC.

Please refer to the OLE overview documentation in Chapter 18 of the Class Library User's Guide as well as the Class Library Reference for the OLE classes.

Samples

The MFC version 1 sample applications have been replaced by three more complete samples:

OCLIENT - a sample OLE client application

This is similar to the MFC 1.0 OCLIENT sample in functionality and user interface but it is a much cleaner and smaller implementation with more features. It is now a multiple document interface (MDI) application that supports multiple views, printing, and print preview (in MM_TEXT mode). It is also more efficient since it does not create a Windows window for each OLE item.

MINSVR - a sample OLE mini-server.

This is an updated version of the MFC 1.0 MINSVR sample. The functionality has not changed other than forcing a specific-bounds rectangle so the mini-server can be used by MS-Write.

HIERSVR - a sample OLE full-server.

This is a new sample that illustrates the COleTemplateServer class, how to write an OLE full-server and other topics. It is an MDI application that displays hierarchy diagrams, and it can be used for simple organization charts or class hierarchy diagrams. It supports links, multiple views, and many other features.

The OLE test drivers samples have been discontinued. In their place you can use the test drivers and diagnostics tools available in the OLE Developer's Toolkit, which is available on CompuServe in the Microsoft Developers Forum (GO WINSDK).

Resources Changes

The MFC version 1 OLE client resources have changed to conform to the standards of MFC version 2. This will impact client applications only.

 The header file AFXOLEUI.H is now obsolete, since the needed symbol definitions are now included in the standard 'AFXRES.H'.

 The resource file 'AFXOLEUI.RC' has been replaced with the new resource file 'AFXOLECL.RC' and only contains client resources.

An additional resource file 'AFXOLESV.RC' has been created for shared resources used by MFC version 2 implementations of full servers.

See Technical Note 23 for more information on standard MFC resources.

Changes in MFC/OLE Classes

The names, functionality, relationships, and responsibilities of the MFC/OLE classes have not changed. The following lists the major changes for each MFC 1.0 OLE class.

COleClientItem:

Was derived directly from CObject; is now derived from the new class CDocItem.

COleClientItem::GetSize has been fixed. It was returning a SIZE incorrectly; now it returns a DWORD which is the count of bytes required to serialize the item.

COleClientDoc:

Was derived directly from CObject; is now derived from the new class COleDocument. Inherits CDocument support as well as being a general CDocItem container (inherited from COleDocument).

Is now integrated with MFC 2.0 document/view architecture.

Now has a command handler for the Edit Links command. Just add a menu item with an ID of ID_OLE_EDIT_LINKS to your menu to enable this dialog.

Now has update command handlers of the following standard commands: ID_EDIT_PASTE, ID_EDIT_PASTE_LINK, ID_OLE_EDIT_LINKS.

Now has a special update command handler that deals with the OLE "Object" menu. Just add a menu item with an ID of ID_OLE_VERB_FIRST to your menu to enable this feature.

Some member functions are renamed (see below for details).

Item enumeration has changed (see below).

COleServerItem:

Was derived directly from CObject; is now derived from the new class CDocItem.

Constructor now requires additional parameter (see below).

Had a member variable m_hPalette which was incorrectly used; it has now been removed.

Some member functions are renamed (see below for details).

COleServerItem and COleServerDoc now support a default implementation of OnGetLinkedItem (was OnGetItem) using SetItemName, you can give each COleServerItem a unique name.

Now has helper function CopyToClipboard.

COleServerItem now supports an implementation of OnShow using the MFC 2.0 document/view architecture so this member function is no longer pure virtual.

COleServerDoc:

Was derived directly from CObject; is now derived from the new class COleDocument. Inherits CDocument support as well as being a general CDocItem container (inherited from COleDocument).

Has special implementations of CDocument member functions to do proper OLE implementations including adjusting the File menu for the Save and Update menu item text.

Item enumeration has changed (see below).

COleServer:

There are no significant changes in this class.

One member function has been renamed (see below for details).

You should use the new derived class COleTemplateServer for new full-server applications.

COleException:

No changes have been made.

CSharedFile:

This is still an implementation class. It has been moved out of the public 'AFXOLE.H' header into the private 'AFXPRIV.H' header.

For information on the three new MFC 2.0 classes, COleDocument, CDocItem and COleTemplateServer, please refer to the Class Library User's Guide and the Class Library Reference.

Mandatory API Changes

The following lists the significant API changes that will cause existing MFC 1.0 OLE applications to either fail to compile or fail to run correctly when using the MFC 2.0 libraries. These are above and beyond the normal upgrading of non-OLE APIs, including such things as STRICT conformance. Please refer to Technical Note 19 for more information on non-OLE API changes.

For both Clients and Servers, the item enumeration interface and implementation has changed to be more general. COleClientDoc::GetNextItem and COleServerDoc::GetNextItem has been replaced by the more general item enumeration inherited from COleDocument. The old GetNextItem member function is no longer pure virtual, so you do not have to implement it if you are happy with the default behavior.

If possible, you should use the default CDocItem implementation inherited from COleDocument, which stores the CDocItems in a CPtrList collection, in which case you can completely remove your old implementation of GetNextItem.

If you want to override the new interface, be sure you get the correct return type (GetNextItem returns a CDocItem* now). You should also override the virtual function COleDocument::GetStartPosition to return the appropriate starting POSITION. The old MFC 1.0 implementation always started with a NULL POSITION, but you can customize this in your class to return an appropriate starting position.

Client Applications:

Calls to COleClientDoc::Register should be replaced by RegisterClientDoc, inherited from COleDocument.

Calls to COleClientDoc::IsOpen should be replaced by IsOpenClientDoc, inherited from COleDocument.

All calls to COleClientItem::GetSize should be scrutinized, since the MFC 1.0 API was incorrect and it has been replaced with a correct interface that returns a DWORD. See the Class Library Reference for the correct interface.

Server Applications:

If you were relying on the MFC 1.0 implementation of OnSetColorScheme, scrutinize that code since the MFC 1.0 implementation was incorrect and has been removed. This includes any code using the m_hPalette member variable of the COleServerItem and COleServerDoc classes.

The COleServerItem::COleServerItem constructor must now be passed the pointer to the COleServerDoc that will contain the new item.

COleServerItem::BeginRevoke has been changed to Revoke and made synchronous. This is necessary to make sure the client connection is truly revoked before the COleServerItem is destroyed.

COleServerItem::OnDraw now takes a general purpose CDC* instead of a CMetaFileDC*. Drawing code should still normally not rely on Attribute DC functions, but you can use SetAttribDC to make your COleServerItem drawing code more general.

Old code:

BOOL CMyItem::OnDraw(CMetaFileDC* pDC)

{ ... }

New code:

BOOL CMyItem::OnDraw(CDC* pDC)

{ ... }

COleServerItem::OnGetTextData is now a const member function. It will be called during Clipboard format enumeration. You should override this member function and return TRUE if your server item data can be represented in CF_TEXT format.

Calls to COleServerDoc::Register should be replaced by RegisterServerDoc, inherited from COleDocument.

Calls to COleServerDoc::IsOpen should be replaced by IsOpenServerDoc, inherited from COleDocument.

COleServerDoc::GetDocument has been renamed to GetEmbeddedItem. Change the name in your overridden version.

COleServerDoc::GetItem has been renamed to GetLinkedItem. Change the name in your overriden version if you application supports links.

COleServer::OnCreateDocFromTemplate has been renamed to OnCreateDocFromTemplateFile to distinguish it from CDocTemplates. This function has nothing to do with document template architecture in MFC 2.0, but refers to the file to use as a 'template' for creating new objects.

Non-Mandatory API Changes

The followings lists the MFC 1.0 APIs that should not be used by your application. These are officially "obsolete", are no longer part of the public API and are not documented in the Class Library Reference. You should not be using these APIs in new code, Even though these APIs are still supported and available in the 'AFXOLE.H' header for backward compatibility, we strongly recommend against using them for new implementations.

For all classes, there are a variety of typedef and const changes to some of the headers as part of STRICT. MFC 2.0 supports STRICT by default while MFC 1.0 only supported STRICT as a nondefault option. Changing your code to be STRICT compliant is highly recommended, and details can be found in Technical Note 12.

COleClientItem::

GetLinkFormatData - an implementation detail

FreezeLink - an implementation detail of the Links dialog

OnRelease - dangerous to override

ClientCallBack - dangerous to override

CheckCreate, CheckAsync, CheckGeneral - helpers for OLESTATUS error code checking; be careful if you need to override

WaitForServer - be careful if you need to override

ReportError - advanced overridable; behavior subject to future change

COleClientDoc::

CheckGeneral - helper for OLESTATUS error code checking; be careful if you need to override

COleServerItem::

OnRelease - dangerous to override

GetMetafileData, GetNativeData - use CopyToClipboard instead

OnQueryProtocol - MFC 1.0 implementation was of dubious value

OnSetColorScheme - now takes a const pointer to a LOGPALETTE

NotifyClient - use NotifyChanged instead for change notification

COleServerDoc::

 NotifyAllClients - use NotifyChanged and NotifyClosed instead

 OnRelease - dangerous to override

 OnSave - never used in MFC 1.0 (or OLE 1)

 CheckAsync - helper for OLESTATUS error code checking; be careful if you need to override

COleServer::

 OnRelease - dangerous to override

Other more Subtle Implementation Changes

The following implementation changes may impact your code depending on how extensively your applications uses OLE. Most MFC/OLE V1 applications will be unaffected by these changes, but they are recorded here for your information.

Client implementation bug fixes:

The logic in the COleClientItem::~COleClientItem has been fixed to not wait for the server when destroying a static OLE item.

Server implementation bug fixes:

The implementation of COleServer::OnRelease has been fixed and improved to correctly terminate a server application that is run embedded. The logic no longer relies on the visibility of the main frame window.

The order and logic of Clipboard format enumeration in COleServerItem has changed. The CF_TEXT format is only returned if the server item overrides OnGetTextData. See the implementation of COleServerItem::OnEnumFormats for more details.

Future OLE Changes

Even though MFC version 2 only supports the OLE 1 standard, OLE 1 applications will continue to run under OLE 2. Future versions of MFC will exploit the new features made available in the upcoming OLE 2 standard. The framework features of MFC 2.0 are keeping an eye to the future to ensure applications written today will meet future standards.

MFC version 2 currently supports the OLE 1 standard. OLE 2, available in the near future, is an improved and extended API for linking and embedding that supports many additional features and improvements over OLE 1.

Many of the improvements that MFC/OLE has made over OLE 1 are going into OLE 2. OLE 2 supports a synchronous communication model and will provide more standards for UI components. The one-client object one-server object relationship of MFC/OLE is extended and exploited in OLE 2. OLE 2 also supports C++ VTables and general programming in C++ more naturally than OLE 1.

OLE 2 defines standards for sharing components that are not present in OLE 1, but are available in non-OLE implementations in the current MFC 2.0. OLE 2 defines standards for sharing menu bars, toolbars and other control bars between the client and server application to allow in situ editing of OLE items. OLE 2 defines a standard for remote execution of commands, as well as a more data/document centric architecture.


Migrating MFC 1.0 Applications to MFC 2.0
Technical Note 19: Migrating MFC 1.0 Applications to MFC 2.0

This note provides guidelines for migrating MFC version 1 applications to the MFC version 2 libraries and tools.

IMPORTANT: To understand and evaluate the two approaches presented below, you must be familiar with MFC 2.0 concepts, such as the Document and View architecture, and the tools. We suggest you at least work through the SCRIBBLE tutorial in the Class Library User's Guide before beginning any migration of existing code.

There are two basic approaches to migrating existing applications from MFC version 1, released with Microsoft C7, to MFC version 2.

Minimal Migration

Using the "Minimal Migration" method, you make only the minimum changes required, so that:

Your existing MFC 1.0 application compiles and links successfully with the MFC 2.0 libraries.

Your resources can be successfully edited using App Studio.

Your message handler functions and dialog data are recognized by ClassWizard.

This is the easiest approach, but it does not take full advantage of the rich features in the MFC 2.0 library. Even if you chose the "Full Migration" method, you will need to understand and exercise techniques for minimal migration.

Full Migration

If you perform a "Full Migration", you can take full advantage of the MFC 2.0 library. Using the minimal migration method, you will be able to edit your application using App Studio and ClassWizard. By using the full migration method, you gain the following MFC 2.0 support:

Standard File menu and dialog user interfaces. You get features such as standard file open and save dialogs, file manager, drag-and-drop, most-recently-used File menu interface, and so on.

Printing. You supply the view code; the library takes care of getting the printer DC, putting up the abort dialog, running the print loop, and so on.

Print Preview. Usually, you will have to do no extra coding to have Print Preview functionality in your application.

Toolbars and status bars. They are now easy to add to your application using App Studio and MFC 2.0.

Splitter windows. If you structure your display code into view classes, then you'll be able to display them in splitter windows at very little additional cost.

The overall full migration method is basically to emulate developing an MFC 2.0 application from scratch, starting with AppWizard. The difference from developing an MFC 2.0 application from scratch, of course, is that you will borrow as much of the MFC 1.0 code you wrote as makes sense.

Minimal Migration

The following subsections present detailed guidelines for performing a minimal migration.

Conforming to Windows 3.1 STRICT typedefs

The default builds of the MFC 2.0 library adhere to the Windows 3.1 STRICT typedefs which are explained in the Windows 3.1 SDK. MFC 1.0 had STRICT turned off, but now MFC 2.0 has STRICT turned on by default. This follows MFC's commitment to track the industry standard Windows API and to foster development practices that make developing robust applications easier. Just as the STRICT typedefs were helpful to the developers of the MFC 2.0 classes to produce robust software, so will that be true for you in developing your application code.

When using STRICT type checking for the first time, many compilation errors will typically result. Modifying your MFC 1.0 application to conform to Windows 3.1 STRICT typedefs may very well represent the bulk of your minimal migration effort.

Although we strongly recommend that you take advantage of Windows 3.1 STRICT types, you may chose to postpone converting your application to STRICT. To exercise this option, you will need to compile the MFC library and your application with the /DNOSTRICT option, as illustrated:

nmake MODEL=? DEBUG=? TARGET=W OPT=/DNOSTRICT

where "?" specifies the memory model and debug option of the library build. These library build switches are documented in Appendix B of the Class Library User's Guide.

Once your application complies with (or disables) STRICT, you may be able to compile an executable with no further changes. For example, the ABOUT2 and FILEVIEW MFC 1.0 samples compile with no additional changes. They were already STRICT compliant and did not used any changed MFC APIs.

MFC API Changes

Beyond conforming to STRICT, most of the effort of doing a minimal migration is to identify and change your application code to conform to the relatively few MFC 2.0 API changes.

Out of over 1800 MFC 1.0 APIs, only 20 of the APIs that were changed result in compile-time errors. These changes require only trivial modifications to existing MFC 1.0 applications. The most extensive changes are the architectural restructuring of the OLE classes. These changes are covered in Technical Note 18.

To anticipate which changes you'll need to make, see the section "Alphabetical API Changes" at the end of this technote. It provides a useful, brief summary of which MFC 1.0 APIs were modified in MFC 2.0.

If you do not make all the changes necessary to deal with MFC 2.0 code, you will get various compilation and linking errors. These errors are almost always easy to diagnose. To aid your diagnosis, we provide some guidelines in the section "Compiler Errors" at the end of this technote.

The following MFC APIs have been removed in MFC 2.0. We recommend alternative APIs where appropriate. This list does not include changes to undocumented implementation APIs.

   CDC::GetDCOrg

GetDCOrg is not available in Windows NT.  For Windows 3.x only applications, just call the Windows API ::GetDCOrg directly.

   CRuntimeClass::m_pszClassName

This member variable is now an LPSTR rather than the memory model-dependent (char*). It is named m_lpszClassName in MFC 2.0.

   CMDIChildWnd::m_pMDIFrameWnd

In MFC 1.0, this member variable pointed to the class's MDIFrame parent. This member variable has been replaced with a member function CMDIChildWnd::GetMDIFrame. If you are using multiple document interface (MDI) in MFC 2.0, most uses of CMDIChildWnd::m_pMDIFrameWnd (or GetMDIFrame) are no longer necessary since the default MDI support handles all of the standard MDI Windows menu commands.

   CFrameWnd::GetChildFrame

Use CMDIFrameWnd::MDIGetActive for MDI frames instead.

The following API has been left in MFC 2.0 to support 1 compatibility but is obsolete. It will be removed from future versions of MFC.

   CMDIFrameWnd::CreateClient

This functionality has been replaced by the more general OnCreateClient mechanism that supports view creation and the improved MFC 2.0 MDI support. The original CreateClient can still be used for MDI applications that manage their own MDI frame window's menu bar (by using CMDIFrame::MDISetMenu). The MFC 2.0 MDI support will automatically switch the MDI frame window's menu bar to the menu for the currently active MDI child window.

Other API-related Changes

Two MFC classes have been moved from the afxwin.h to the afxext.h header file:

CBitmapButton

CMetaFileDC

In your .cpp files that reference these classes add the following:

#include <afxext.h>

Many APIs have been changed so that they are stricter about the use of the 'const' modifier. These changes result in a more consistent use of the LPCSTR type name and the new  LPCRECT type name. Note, there is no compile time issue with these changes, since any type can be promoted to a const version of that type when used as an argument. Like the STRICT change, this leads to more robust code when your code uses const data pointers.

The window Create functions listed below now have an additional parameter, but since the last parameter has a default value of NULL, existing code will work without modification. These functions are

CWnd::Create

CFrameWnd::Create

CMDIFrameWnd::Create

CMDIChildWnd::Create

The following functions were virtual in MFC 1.0 but are now nonvirtual in MFC 2.0:

CFile::GetStatus

CFrameWnd::GetParentFrame

If a derived class of your MFC 1.0 application overrides either of these functions, it is unlikely that the function in your derived class will be called in MFC 2.0. In addition, GetParentFrame was moved from CFrameWnd to CWnd to be a more generally useful API.

All static members of classes, as well as global operator/friend functions, now adhere to PASCAL calling conventions. All global functions are AFXAPI (PASCAL). Again, this is not a compile time issue but leads to faster and smaller generated code.

Many of the implementation-only classes and structures have been renamed to not use the 'C' prefix. For example, CExceptionContext has been renamed to AFX_EXCEPTIONCONTEXT. These classes are not documented and remain implementation details of the class library. It is unlikely that you have relied on these, and it is generally recommended that you do not rely on undocumented APIs of the class library since they are subject to change in future versions.

MFC Default Behavior Changes

Dealing with MFC API changes is easy with the aid of errors reported by the compiler and linker. Not all library changes are revealed in the library header files, however. Some changes are revealed in run-time behavior of your application. These changes are generally not difficult to deal with, as long as you anticipate them. The following information is provided to help you anticipate such behavioral changes.

CDialog and CModalDialog have been merged into a single class. CModalDialog is now considered to be an obsolete class. However, for MFC 1.0 compatibility, all references to CModalDialog are still valid through a migration macro in afxwin.h:

#define CModalDialog CDialog

For many MFC 1.0 applications, this simple #define is sufficient. However, there are cases where this #define is not sufficient.

If you implemented a modeless dialog and relied on the default "do nothing" behavior for OnOK and OnCancel, then you must override these and the default behavior, since they now call EndDialog (for modal dialog processing).

CDialog::CreateIndirect still creates a modeless dialog. To create a modal dialog use CDialog::InitModalIndirect instead of the removed CModalDialog::CreateIndirect API.

Dialog box and message box background colors can now be globally set using the CWinApp::SetDialogBkColor API. The default parameter sets the color to light gray (not COLOR_BTNFACE) to produce gray backgrounds. You may specify other colors.

If SetDialogBkColor is not called in your CWinApp-derived InitInstance function, the default Window Background color (set in the Control Panel Color applet) is used.

In MFC 1.0, if a DLL contained a CWinApp object, it was necessary to provide a _WEP that included a call to AfxWinTerm. MFC 2.0 provides this _WEP, so any additional code included in the 1 _WEP should be migrated to the DLL's CWinApp::ExitInstance member function.

CMDIChildWnd::Create now correctly uses the dwStyle parameter. You must now specify a complete window style for the MDI child window. If you specify dwStyle = 0,  you will now get an ASSERT failure in CMDIChildWnd::PreCreateWindow. To avoid this, you should specify the style WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW to be backward compatible with MFC 1.0.

MFC 2.0 supports setting different styles for MDI child windows, so you can remove some of the frame window controls, if desired.

Class CFrameWnd has a new data member, BOOL CFrameWnd::m_bAutoMenuEnable. It is set to TRUE by default. This causes menu items that don't have ON_UPDATE_COMMAND_UI or ON_COMMAND handlers to be automatically disabled. Menu items that have ON_COMMAND handlers, but no ON_UPDATE_COMMAND_UI handlers, will be automatically enabled.

This makes it easy to implement optional commands based on the current selection. Also, this greatly reduces the need for applications to write ON_UPDATE_COMMAND_UI handlers for enable/disable of menu items. For instance, an AppWizard-generated application will have Edit Cut/Copy/Paste disabled until the programmer implements handlers for them.

However, if your MFC 1.0 application is not updated to use ON_COMMAND and ON_UPDATE_COMMAND_UI handlers, then it must clear m_bAutoMenuEnable explicitly. Otherwise, menus that you disable will be re-enabled automatically.

Project (Build) Changes

You can continue to build your MFC 1.0 application using a standard makefile.

A common link error is unresolved externals to COMMDLG.DLL and SHELL.DLL APIs. Be sure to link with COMMDLG.LIB and SHELL.LIB.

You may be able to improve build times by placing #include <afxwin.h> in a precompiled header. By convention, MFC 2.0 applications specify "stdafx.h" as the precompiled header. Then module stdafx.cpp includes stdafx.h. This technique is illustrated by the code created by AppWizard, and by many of the MFC 2.0 samples.

App Studio and ClassWizard Compatibility

Even for minimal migration, we recommend that you follow the steps below so that you can use App Studio and ClassWizard to edit your application resources and code.

For any module whose message map you want to edit with ClassWizard, add the following two comment lines in your .cpp file outside of any function implementations:

//{{AFX_MSG_MAP(<class name>)

//}}AFX_MSG_MAP

<class name> is the name of your class containing the message map.

Similarly, add the following two comment lines inside of the corresponding class declaration in your .h file:

//{{AFX_MSG(<class name>)

//}}AFX_MSG

For an example of these declarations, look at the same comment lines in any application created by AppWizard. For an explanation of what these comment lines mean, see Chapter 6, "Binding Visual Objects to Code Using ClassWizard" of the Class Library User's Guide.

Your project must have a makefile named <project>.mak and a resource file named <project>.rc, where <project> is the name of your project. Both of these files must be located in the project's root directory. The .mak file may be an external makefile.

Run ClassWizard from within App Studio. When ClassWizard discovers that a .CLW file does not exist, it asks you if you want to create one. Respond 'yes'. The .CLW file is essentially an index file that ClassWizard uses to identify modules that have such //{{AFX_MSG comment lines.

Full Migration

A full migration of your existing C or MFC 1.0 application to MFC 2.0 will offer you all the advantages of MFC 2.0. For most applications, a full migration is not difficult and is well worth the effort.

A successful full migration of an application to MFC 2.0 requires essentially the same understanding of MFC 2.0 as developing a new application from scratch. You should become familiar with the MFC 2.0 Class Library, App Studio, AppWizard, and ClassWizard before you begin the full migration. You should understand what portions of your application code can be removed by deriving equivalent or improved functionality from the MFC 2.0 classes. Not only will using more of the library implementation make your source code smaller, but it will make these parts of your application better integrated with the rest of the MFC framework.

By fully migrating your application to MFC 2.0, you will be able to derive additional functionality from MFC at relatively little extra cost. For example, if your application did not have a splitter window user interface, but one would be useful to your users, then you will be able to quickly add this feature, having already ported your code to MFC 2.0's document/view architecture.

Although a full migration to MFC 2.0 may require a couple days effort for large applications, the process itself is fairly straightforward. The following general steps describe the process:

Analyze how your existing application architecture factors into document, views, and frame windows.

Do this before you start editing any code. Many programmers tend to intertwine document code with view code. Although doing so is not necessarily "bad," separation of document and view functionality is a design philosophy that the MFC framework endorses and supports particularly well. Even though MFC 1.0 did not have CDocument and CView classes, it also endorsed document/view separation. So will all future versions of the library.

Study the MFC 2.0 samples that use the CDocument and CView classes, particularly the SCRIBBLE tutorial sample. Then analyze your application to determine what the document is and what the view is. Determine whether your application has multiple document types or views.

Even if your application does not lend itself to clear document/view separation, you will still be able to fully migrate to MFC 2.0 and take essentially complete advantage of the framework. You can "fake" document/view separation by implementing CDocument- and CView-derived classes, but your document or view class may delegate most of its work to the other class. Or, your view class might rely on your CFrameWnd- or CMDIChildWnd-derived class to implement the bulk of your application's user interface. In summary, you have almost complete freedom as to how to separate your document, view, and frame window classes.

Your analysis also should determine whether your application needs multiple view classes and possibly multiple document classes. Even relatively simple applications sometimes need more than one view class. However, multiple views, such as in a splitter window, don't necessarily dictate that you have multiple CView-derived classes. For example, if each pane in the splitter window provides the same user interface as other panes in the splitter window, then they can share the same view class. In that case, each pane is simply a distinct object of the same view class. You'll probably want to design multiple view classes, if your application provides very distinct user interfaces in different windows.

Analyze what AppWizard-supported framework features your application will need.

AppWizard will create a skeleton MFC 2.0 application that supports various framework features that you select as options in AppWizard dialogs. Before you run AppWizard to create your skeleton application, you should first become familiar with the options AppWizard provides.

Then, take a little time to decide which of AppWizard's options you'll want to select. Don't attempt to do this in a few minutes the first time you run AppWizard. For example, if your application does not already support OLE, this is a major decision that you'll want to consider. If you didn't chose AppWizard's OLE option to begin with, you'll still be able to modify your application code to use MFC's OLE features. But starting with the OLE option in AppWizard to begin with will save you time.

Your analysis should determine whether your application is a single document interface (SDI) or multiple document interface (MDI) application. This particular determination should be obvious if you're familiar with these two distinct user interfaces in other Windows applications. AppWizard will create MDI applications by default since the MDI user interface is usually more functional to end users for it lets them open more than one document/file at a time. Fortunately, with the MFC 2.0 document/view architecture, supporting MDI requires no extra coding on your part.

Generate a new application using AppWizard.

Having done the above analysis, you're now ready to run AppWizard to create the skeleton code for your application.

Having analyzed how your application separates into document, views, and frame windows, you should have a good idea what names to give to their corresponding classes and modules. You might want to assign somewhat generic names, such as the tutorial sample's CScribDoc and CScribView, and scribdoc.cpp and scribvw.cpp. However, if your application requires multiple view classes, you'll probably want to give the first AppWizard-created view class a more specialized name, such as CDataEntryView and CReportView. See the next step for additional information on creating multiple document and view classes.

Having anticipated what additional AppWizard options you want, such as SDI or MDI, and OLE, you should now be able to select the AppWizard options and create the skeleton application in just a few minutes.

Optionally, clone second view, document, and frame window classes.

If your above analysis determines that your application should have multiple view, document, or frame window classes, then it's a good time to create the skeleton code for these classes right after you run AppWizard.

You can create the skeleton code for your additional view, document, and frame window classes by cloning the ones created by AppWizard. That is, copy the .cpp and .h files, assigning a new module name for the second document or class. Then edit the skeleton code by changing class names. Another alternative is to use the ClassWizard's Add Class functionality to create a new class automatically in the files you specify using the names you specify. You will already be familiar with the ClassWizard's ability to create new classes if you have followed the SCRIBBLE tutorial.

In either case, in your CWinApp-derived class's InitInstance function, you must register additional document template objects for any associations that you want to make between you multiple document, view, and frame window classes.

This is also a quick step. You can postpone this step if you're not committed to implementing multiple documents, views, or frame window classes in your application.

Migrate the relevant portions of your MFC 1.0 code into the classes created by AppWizard.

This step represents the bulk of the work in migrating your MFC 1.0 application to MFC 2.0. You should do this incrementally. Migrate relatively small chunks of your application at a time. As you do this, you'll learn more details about what functionality the framework provides that will allow you to discard some of your old MFC 1.0 application code.

As you migrate these chunks of code, keep in mind the guidelines presented under "Minimal Migration". Many of those guidelines apply to full migration. AppWizard will have already added the //{{AFX_MSG and //{{AFX_MSG_MAP comments to your command target classes (application, document, view, and frame window). It is not necessary for you manually add these as under the minimal migration approach. Although it is not required, we recommend that you move message-handling functions between the //{{AFX_MSG comments nested in the message maps. Also, move the declarations of these message-handling (afx_msg) functions between the //{{AFX_MSG comments in your header files. Doing so will allow you to use ClassWizard throughout the rest of your project's life cycle(s).

These recommendations regarding //{{AFX_MSG comments also apply, perhaps to a lesser degree, to dialogs. If you don't anticipate many future changes to a given dialog class, then it might not be worth your effort to make that dialog ClassWizard-aware. That's fine. We recommend, of course, that you create all new dialog classes using ClassWizard's Add Class option.

As you migrate an MFC 1.0 or Windows application, you may want to maintain compatibility with existing file formats. (The default MFC 2.0 document serialization mechanism may not be appropriate for your application.)  To do direct CFile write and read calls, or to implement a non-file based document, you will want to override CDocument::OnOpenDocument and OnSaveDocument. The DIBLOOK sample provides an example of this technique. If your current application already serializes objects, then this will not be an issue.

Alphabetical API Changes

To understand the reasons for these changes, please refer to "Reason for Changes" below.

API / Variable	MFC 2.0 Change (Reason for Change)
CMetaFileDC::Close	return type (2)
CWnd::Create	extra default param added, CWnd* const (1, 3)
CFrameWnd::Create	extra default param added, CWnd* const (1, 3)
CMDIChildWnd::Create	extra default param added, CWnd* const (1, 3)   dwStyle default is now: WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW
CWnd::CreateEx	extra default param added, CWnd* const (1, 3)
CBitmap::CreateBitmap	parameter types (4)
CDC::EnumObjects	callback prototype (2)
CTime::Format	const function (3)
CTimeSpan::Format	const function (3)
CTime::FormatGmt	const function (3)
CFile::GetStatus	nonvirtual (5)
CDC::GrayString	callback prototype and parameter type (2)
CBitmapButton::   LoadBitmaps	extra default parameter (1)
CWnd::OnActivateApp	parameter type (2)
CWnd::OnCompareItem	extra parameter (6)
CWnd::OnDeleteItem	extra parameter (6)
CWnd::OnDrawItem	extra parameter (6)
CWnd::OnDropFiles	parameter type (2)
CWnd::OnGetMinMaxInfo	parameter type (6)
CWnd::OnMeasureItem	extra parameter (6)
CWnd::OnMenuChar	return type (2)
CWnd::OnNcCalcSize	extra parameter (6)
CWnd::OnPaintClipboard	parameter type (2)
CWnd::OnParentNotify	parameter type (2)
CWnd::OnSizeClipboard	parameter type (2)
CWnd::OnSysCommand	parameter type (2)
CWnd::OnWinIniChange	parameter type (2)
CDC::PlayMetaFile	parameter type (2)
CEdit::SetSel	extra default parameter (6)
CEdit::SetTabStops	parameter type (5)
CWnd::SetTimer	callback prototype and parameter type (2)
CRuntimeClass::   m_pszClassName	renamed m_lpszClassName (5)
Deleted or Obsolete API	MFC 2.0 Change (Reason for Change)
CBitmapButton	removed ctor with 3 params - use LoadBitmaps (1)
CMDIFrameWnd::   CreateClient	use OnCreateClient (1)
GetChildFrame	use MDIGetActive (1)
GetDCOrg	use Windows API directly for 3.x (4)
m_pMDIFrameWnd	now call GetParentFrame OR GetMDIFrame (1)

Reasons for Changes

1.	Improved functionality. The MFC 2.0 version of this API provides more or improved functionality over its MFC 1.0 counterpart.

2.	STRICT conformance. The MFC 2.0 API was changed to conform with the STRICT Windows API.

3.	Const correctness. The MFC 2.0 API was fixed to correctly apply the 'const' keyword to facilitate more robust programs.

4.	NT portability. The MFC 2.0 API was changed to correctly reflect the Win32 API which facilitates easy porting of MFC applications to Windows NT.

5.	Reduced memory usage. The MFC 2.0 API was changed to provide more efficient linkage and memory usage techniques which will result in smaller applications and reduced DGROUP usage.

6.	Win 3.1 functionality. The MFC 2.0 API was changed to correctly reflect a Windows 3.1 API change. The API still remains compatible with Windows 3.0, but optional Windows 3.1 only parameters are now available.

Compiler Errors

Most changes to the MFC 2.0 APIs will generate one of a few compiler errors, or none at all if standard type conversions satisfy the compiler. The following compiler errors may be generated when compiling existing MFC 1.0 applications under MFC 2.0:

Compiler error C2039: 'identifier' : is not a member of 'class-key'

This error is caused when a member function or data member has been removed from a class, for example CFrameWnd's m_pMDIFrameWnd.

Compiler error C2501: 'identifier' : missing decl-specifiers

This error is caused when you use an unknown class name. This is usually the case when the class no longer exists or has been moved to a different header file.  For example if you get this error for CMetaFile and CBitmapButton then you must add #include "afxext.h" to the source files using those classes.

Compiler error C2248: 'member' : cannot access 'specifier' member declared in class 'class'

This error occurs if the access of a member has changed from MFC 1.0 to 2. For example, an undocumented API has been moved from public to protected member access. This should only occur in code that is using undocumented and unsupported APIs, which should be changed to use the appropriate MFC 2.0 functionality.

Compiler error C2642: cast to pointer to member must be from related pointer to member

This error occurs when the message handler function prototype differs from the one in afxwin.h. For example, a line containing the ON_WM_ACTIVATEAPP macro will emit this error if the parameters and return type of your OnActivateApp message handler match the MFC 1.0 declaration.

Compiler error C2660: 'function' : function does not take 'number' parameters

The number of parameters has changed from MFC 1.0 to MFC 2.0. For example, calling the CBitmapButton constructor with three parameters causes this error since this particular constructor has been removed and replaced by the LoadBitmaps member function.

Compiler error C2664: 'function' : cannot convert parameter 'number' from 'type1' to 'type2'

The type of a parameter has changed, and standard conversions do not satisfy the compiler. CDC::EnumObjects is an example of this. In this case, the prototype of the callback function has changed.


ID Naming and Numbering Conventions
Technical Note 20: ID Naming and Numbering Conventions

This note describes the ID naming and numbering conventions used by MFC 2.0 for resources, commands, strings, controls, and child windows.

The Problem

The MFC ID naming and numbering conventions are intended to meet the following requirements:

Provide a consistent ID-naming standard used across the MFC library and MFC applications that are supported by App Studio. This allows the programmer to more readily interpret IDs as to their type and origin.

Emphasize the strong 1-to-1 relationship between certain types of IDs. This helps to clarify MFC's application framework architecture.

Conform to already widely used standards for naming IDs in Windows.

Partition the ID-numbering space, so as to avoid unintentional duplication of ID numbers amongs those assigned by the programmer, by MFC and Windows, and by App Studio-edited applications.

Overview of ID Prefix Naming Convention

There are several categories or types of IDs in an application. The MFC ID-naming convention defines different prefixes for different resource types.

MFC uses the prefix "IDR_" to refer to a resource ID that applies to multiple resource types. For example, for a given frame window, the same "IDR_" value is used to refer to a menu, accelerator, string and icon resource all at once.

IDR_	Multiple resource types (Used for Menus, Accelerators primarily)

IDD_	for dialog template resources (for example, IDD_DIALOG1)

IDC_	for Cursor resources

IDI_	for Icon resources

IDB_	for Bitmap resources

IDS_	for String resources

Note that the IDS_ value for a string resource is the ID passed to LoadString. The actual implementation of string table resources groups together 16 strings into one segment.

Within a DIALOG resource, we follow the convention of

IDOK,IDCANCEL  for standard pushbutton IDs

IDC_  for other dialog controls

The "IDC_" prefix is also used for cursors. This naming conflict is not usually a problem since a typical application will have few cursors and a large number of dialog controls.

Within a Menu resource, we follow the convention of

IDM_	for menu items not using the MFC command architecture

ID_	for menu item commands using the MFC command architecture

Commands that follow the MFC command architecture must have an ON_COMMAND command handler and may have an ON_UPDATE_COMMAND_UI handler. If these command handlers follow the MFC command architecture, they will function correctly whether they are bound to a menu item, a toolbar button or a dialog bar button. The same ID_ is also used for a menu prompt string displayed on the program's message bar. Most of the menu items in your application should follow the MFC command convention. All of the standard command IDs (for example, ID_FILE_NEW) follow this convention.

MFC also uses "IDP_" as a specialized form of strings (that is, instead of "IDS_"). Strings with the "IDP_" prefix are "prompts," that is, strings used in message boxes. "IDP_" strings may contain "%1" and "%2" as place holders of strings determined by the program. "IDP_" strings usually have help topics, while "IDS_" strings do not. "IDP_" strings are always localized, while "IDS_" strings may or may not be localized.

The MFC library also uses the "IDW_" prefix as a specialized form of control IDs (that is, instead of "IDC_"). These IDs are assigned to child windows such as views and splitters by the framework classes. MFC implementation IDs are prefixed with "AFX_".

Overview of ID-numbering Convention

The following lists the valid ranges for the IDs of the specific types. Some of the limits are technical implementation limits while others are just conventions to prevent your IDs from colliding with Windows predefined IDs or MFC default implementations.

We strongly recommend you do not defined IDs outside the recommended ranges. Even though the lower limit of many of these ranges is 1 (0 is not used), common convention starts practical use of IDs at 100 or 101.

Prefix	Resource Type	Valid Range
IDR_	multiple	1 -> 0x6FFF
IDD_	dialog templates	1 -> 0x6FFF
IDC_,IDI_,IDB_	cursors, icons, bitmaps	1 -> 0x6FFF
IDS_, IDP_	general strings	1 -> 0x7FFF
ID_	commands	0x8000 -> 0xDFFF
IDC_	controls	8 -> 0xDFFF

Reasons for these range limits:

By convention, the ID value of 0 is not used.

Windows implementation limitations restrict true resource IDs to be less than or equal to 0x7FFF.

MFC's internal framework implementations reserve several ranges: 0xE000->0xEFFF and 0x7000->0x7FFF.

Several Windows system commands use the range of 0xF000 -> 0xFFFF.

Control IDs of 1->7 are reserved by IDOK, IDCANCEL, and so on.

The range of 0x8000->0xFFFF for strings is reserved for menu prompts for commands.


Command and Message Routing
Technical Note 21: Command and Message Routing

This note describes the command routing and dispatch architecture as well as advanced topics in general window message routing.

Please refer to the Class Library User's Guide for general details on the architectures described here, especially the distinction between Windows messages, control notifications, and commands. This note assumes you are very familiar with the issues described in the printed documentation and only addresses very advanced topics.

Command Routing and Dispatch

MFC 1.0 Functionality Evolves to MFC 2.0 Architecture

Windows has the WM_COMMAND message that is overloaded to provide notifications of menu commands, accelerator keys and dialog-control notifications.

MFC 1.0 built on that a little by allowing a command handler (for example, "OnFileNew") in a CWnd derived class to get called in response to a specific WM_COMMAND. This is glued together with a data structure called the message map, and results in a very space-efficient command mechanism.

MFC 1.0 also provided additional functionality for separating control notifications from command messages. Commands are represented by a 16 bit ID, sometimes known as a Command ID. Commands normally start from a CFrameWnd (eg: a menu select or a translated accelerator) and get routed to a variety of other windows.

MFC 1.0 used command routing in a limited sense for the implementation of Multiple Document Interface (MDI). (An MDI frame window delegate commands to its active MDI Child window.)

This functionality has been generalized and extended in MFC 2.0 to allow commands to be handled by a wider range of objects (not just window objects). It provides a more formal and extensible architecture for routing messages and reuses the command target routing for not only handling of commands, but also for updating UI objects (like menu items and toolbar buttons) to reflect the current availability of a command.

Command IDs

See the Class Library User's Guide for an explanation of the command routing and binding process. Technical Note 20 contains information on ID naming.

We use the generic prefix "ID_" for command IDs. Command IDs are >= 0x8000. The message line or status bar will show the command description string if there is a STRINGTABLE resource with the same IDs as the command ID.

In the resources of your application, a command ID can appears in several places:

 In one STRINGTABLE resource that has the same ID as the message-line prompt

 In possibly many MENU resources that are attached to menu items that invoke the same command

 (ADVANCED) in a dialog button for a GOSUB command

In the source code of your application, a command ID can appears in several places:

 In your RESOURCE.H (or other main symbol header file) to define application-specific command IDs

 PERHAPS In an ID array used to create a toolbar

 In an ON_COMMAND macro

 PERHAPS In an ON_UPDATE_COMMAND_UI macro

Currently, the only implementation in MFC that requires command IDs be >= 0x8000 is the implementation of GOSUB dialogs/commands.

GOSUB Commands, using Command Architecture in Dialogs

The command architecture of routing and enabling commands works well with frame windows, menu items, toolbar buttons, dialog bar buttons, other control bars and other user-interface elements designed to update on demand and route commands or control IDs to a main command target (usually the main frame window). That main command target may route the command or control notifications to other command target objects as appropriate.

A dialog (modal or modeless) can benefit from some of the features of the command architecture if you assign the control ID of the dialog control to the appropriate command ID. Support for dialogs is not automatic, so you may have to write some additional code.

Note that for all these features to work properly, your command IDs should be >= 0x8000. Since many dialogs could get routed to the same frame, shared commands should be >= 0x8000, while the non-shared IDC_s in a specific dialog should be <= 0x7FFF.

You can place a normal button in a normal modal dialog with the IDC of the button set to the appropriate command ID. When the user selects the button, the owner of the dialog (usually the main frame window) gets the command just like any other command. This is called a GOSUB command since it usually is used to bring up another dialog (a GOSUB of the first dialog).

You can also call the function CWnd::UpdateDialogControls on your dialog and pass it the address of your main frame window. This function will enabled or disable your dialog controls based on whether they have command handlers in the frame. This function is called automatically for you for control bars in your applications idle loop, but you must call it directly for normal dialogs that you wish to have this feature.

When ON_UPDATE_COMMAND_UI is Called

Maintaining the enabled/checked state of all a program's menu items all the time can be a computationally expensive problem. A common technique is to enable/check menu items only when the user selects the POPUP. The MFC 2.0 implementation of CFrameWnd handles the WM_INITMENUPOPUP message and uses the command routing architecture to determine the states of menus through ON_UPDATE_COMMAND_UI handlers.

CFrameWnd also handles the WM_ENTERIDLE message to describe the current menu item selected on the status bar (also known as the message line).

An application's menu structure, edited by App Studio, is used to represent the potential commands available at WM_INITMENUPOPUP time. ON_UPDATE_COMMAND_UI handlers can modify the state or text of a menu, or for advanced uses (like the File MRU list or the OLE Verbs pop-up menu), actually modify the menu structure before the menu is drawn.

The same sort of ON_UPDATE_COMMAND_UI processing is done for toolbars (and other control bars) when the application enters its idle loop. See the Class Library Reference and Technical Note 31 for more information on control bars.

Nested Popup Menus

If you are using a nested menu structure, you will notice that the ON_UPDATE_COMMAND_UI handler for the first menu item in the popup is called in two different cases.

First it is called for the popup itself. This is necessary since popup menus do not have IDs and we use the ID of the first menu item of the popup to refer to the entire popup. In this case the m_pSubMenu member variable of the CCmdUI object will be non-NULL and will point to popup menu.

Second it is called just before the menu items in the popup are to be drawn. In this case the ID refers just to the first menu item and the m_pSubMenu member variable of the CCmdUI object will be NULL.

This allows you to enable the popup distinct from its menu items, but requires that you write some menu aware code. For example, in a nested menu with the following structure:

File>
   New>
      Sheet (ID_NEW_SHEET)

      Chart (ID_NEW_CHART)

The ID_NEW_SHEET and ID_NEW_CHART commands can be independently enabled or disabled. The "New" popup menu should be enabled if either of the two is enabled.

The command handler for ID_NEW_SHEET (the first command in the popup) would look something like:

void CMyApp::OnUpdateNewSheet(CCmdUI* pCmdUI)
{

    if (pCmdUI->m_pSubMenu != NULL)
    {
        // enable entire popup for "New" sheet and chart

        pCmdUI->Enable(m_bCanCreateSheet || m_bCanCreateChart);

        return;

    }

    // otherwise just the New Sheet command

    pCmdUI->Enable(m_bCanCreateSheet);

}

The command handler for ID_NEW_CHART would be a normal update command handler and look something like:

void CMyApp::OnUpdateNewChart(CCmdUI* pCmdUI)
{

    pCmdUI->Enable(m_bCanCreateChart);

}

ON_COMMAND and ON_BN_CLICKED

The message map macros for ON_COMMAND and ON_BN_CLICKED are the same. The MFC command and control notification routing mechanism only uses the command ID to decide where to route to. Control notifications with control notification code of zero (BN_CLICKED) are interpreted as commands.

Advanced note: In actual fact, all control notification messages go through the command handler chain. Therefore it is technically possible for you to write a control notification handler for say EN_CHANGE in your document class. This is not generally advisable since the practical applications of this feature are few, the feature is not supported by ClassWizard, and use of the feature can result in fragile code.

Disabling the Automatic Disabling of Button controls

If you place a button control on a dialog bar, or in a dialog using where you are calling CWnd::UpdateDialogControls on your own, you will notice that buttons which do not have ON_COMMAND or ON_UPDATE_COMMAND_UI handlers will be automatically disabled for you by the framework. In some cases you will not need to have a handler, but you will want the button to remain enabled. The easiest way to achieve this is to add a dummy command handler (easy to do with ClassWizard) and do nothing in it.

Window Message Routing

The following describes some more advanced topics on the MFC classes and how Windows message routing and other topics impact them. The information here is only described briefly. Refer to the Class Library Reference for details about public APIs. Please refer to the MFC library source code for more information on implementation details.

Please refer to Technical Note 17 for details on Window cleanup, a very important topic for all CWnd-derived classes.

CWnd Issues

The implementation member function CWnd::OnChildNotify provides a powerful and extensible architecture for child windows (also known as controls) to hook or otherwise be informed of messages, commands, and control notifications that go to their parent (or "owner"). If the child window (/control) is a C++ CWnd object itself, the virtual function OnChildNotify is called first with the parameters from the original message (that is, a MSG structure). The child window can leave the message alone, eat it, or modify the message for the parent (rare).

The default CWnd implementation handles the following messages and uses the OnChildNotify hook to allow child windows (controls) to get first crack at the message:

 WM_MEASUREITEM and WM_DRAWITEM (for self-draw)

 WM_COMPAREITEM and WM_DELETEITEM (for self-draw)

 WM_HSCROLL and WM_VSCROLL

 WM_CTLCOLOR

 WM_PARENTNOTIFY

You will notice the OnChildNotify hook is used for changing owner-draw messages into self-draw messages.

In addition to the OnChildNotify hook, scroll messages have further routing behavior. Please see below for more details on scroll bars and sources of WM_HSCROLL and WM_VSCROLL messages.

CFrameWnd Issues

The CFrameWnd class provides most of the command routing and user-interface updating implementation. This is primarily used for the main frame window of the application (CWinApp::m_pMainWnd) but applies to all frame windows.

The main frame window is the window with the menu bar and is the parent of the status bar or message line. Please refer to the above discussion on command routing and WM_INITMENUPOPUP.

The CFrameWnd class provides management of the active view. The following messages are routed through the active view:

 All command messages (the active view gets first crack at them)

 WM_HSCROLL and WM_VSCROLL messages from sibling scroll bars (see below)

 WM_ACTIVATE (and WM_MDIACTIVATE for MDI) get turned into calls to the virtual function CView::OnActivateView

CMDIFrameWnd/CMDIChildWnd Issues

Both MDI frame window classes derive from CFrameWnd and therefore are both enabled for the same sort of command routing and user-interface updating provided in CFrameWnd. In a typical MDI application, only the main frame window (that is, the CMDIFrameWnd object) holds the menu bar and the status bar and therefore is the main source of the command routing implementation.

The general routing scheme is that the active MDI child window gets first crack at commands. The default PreTranslateMessage functions handle accelerator tables for both MDI child windows (first) and the MDI frame (second) as well as the standard MDI system-command accelerators normally handled by TranslateMDISysAccel (last).

Scroll Bar issues

When handling scroll-message (WM_HSCROLL/OnHScroll and/or WM_VSCROLL/OnVScroll ), you should try to write the handler code so it does not rely on where the scroll bar message came from. This is not only a general Windows issue, since scroll messages can come from true scroll bar controls or from WS_HSCROLL/WS_VSCROLL scroll bars which are not scroll bar controls.

MFC extends that to allow for scroll bar controls to be either child or siblings of the window being scrolled (in fact the parent/child relationship between the scroll bar and window being scrolled can be anything). This is especially important for shared scroll bars with splitter windows. Please refer to Technical Note 29 for details on the implementation of CSplitterWnd including more information on shared scroll bar issues.

On a side note, there are two CWnd Technical Note derived classes where the scroll bar styles specified at create time are trapped and not passed to Windows. When passed to a creation routine, WS_HSCROLL and WS_VSCROLL can be independently set, but after creation can not be changed. Of course, you should not directly test or set the WS_?SCROLL style bits of the window that they created.

For CMDIFrameWnd the scroll bar styles you pass in to Create or LoadFrame are used to create the MDICLIENT. If you wish to have a scrollable MDICLIENT area (like the Windows Program Manager) be sure to set both scroll bar styles (WS_HSCROLL | WS_VSCROLL) for the style used to create the CMDIFrameWnd.

For CSplitterWnd the scroll bar styles apply to the special shared scroll bars for the splitter regions. For static splitter windows, you will normally not set either scroll bar style. For dynamic splitter windows you will usually have the scroll bar style set for the direction you will split, That is, WS_HSCROLL if you can split rows, WS_VSCROLL if you can split columns.


Standard Commands Implementation
Technical Note 22: Standard Commands Implementation

This note describes the standard command implementations provided by MFC 2.0. Please be sure to read Technical Note 21 first since that describes the mechanisms used to implement many of the standard commands.

This description assumes knowledge of the MFC architectures, APIs, and common programming practice. Documented as well as undocumented "implementation only" APIs are described. This is not a place to start to learn about the features of or how to program in MFC. Please refer to the Class Library User's Guide for more general information and the Class Library Reference for details of documented APIs.

The Problem

MFC defines many standard command IDs in the header file AFXRES.H. Framework support for these commands varies. Understanding where and how the framework classes handle these commands will not only show you how the framework works internally but will provide useful information on how to customize the standard implementations and hopefully teach you a few techniques for implementing your own command handlers.

Contents of this Technical Note

Each command ID is described in three sections:

1.	The title: the symbolic name of the command ID (for example, ID_FILE_SAVE) followed by the purpose of the command (for example, "saves the current document") separated by a colon.

2.	One or more paragraphs describing which classes implement the command, and what the default implementation does

3.	A bulleted list ( [image]) of when you might want to override the default implementation and some hints as to how to do this effectively.

Most default command implementations are prewired in the framework's base class message map. There are some command implementations which require explicit wiring in your derived class. These are described under "Note". If you chose the right options in AppWizard, these default handlers will be connected for you in the generated skeleton application.

Naming Convention

Standard commands follow a simple naming convention that we recommend you use if possible. Most standard commands are located in standard places in an application's menu bar. The symbolic name of the command starts with "ID_" followed by the standard popup menu name, followed by the menu item name. The symbolic name is in upper case with underscore word-breaks. For commands that do not have standard menu item names, a logical command name is defined starting with "ID_" (for example, ID_NEXT_PANE).

We use the prefix "ID_" to indicate commands that are designed to be bound to menu items, toolbar buttons, or other command user-interface objects. Command handlers handling "ID_" commands should use the ON_COMMAND and ON_UPDATE_COMMAND_UI mechanisms of the MFC command architecture.

We recommend you use the standard "IDM_" prefix for menu items which do not follow the command architecture and need menu-specific code to enable and disable them. Of course the number of menu specific commands should be small since following the MFC command architecture not only makes command handlers more powerful (since they will work with toolbars) but makes the command handler code reusable.

ID Ranges

Please refer to Technical Note 20 for more details on the use of ID ranges in MFC.

MFC standard commands fall in the range 0xE000 to 0xEFFF. Please do not rely on the specific values of these IDs since they are subject to change in future versions of the library.

Your application should define its commands in the range 0x8000 to 0xDFFF.

Other Things

For each command ID, there is a standard message line prompt string that can be found in the file PROMPTS.RC. The string ID for that menu prompt must be the same as for the command ID.

Standard Command IDs

ID_FILE_NEW : creates a new/empty document

Note: you must connect this to your CWinApp-derived class's message map to enable this functionality.

CWinApp::OnFileNew implements this command differently depending on the number of document templates in the application. If there is only one CDocTemplate, CWinApp::OnFileNew will create a new document of that type, as well as the proper frame and view class.

If there is more than one CDocTemplate, CWinApp::OnFileNew will prompt the user with a dialog (AFX_IDD_NEWTYPEDLG) letting them select which document type to use. The selected CDocTemplate is used to create the document.

 One common customization of ID_FILE_NEW is to provide a different and more graphical choice of document types. In this case you can implement your own CMyApp::OnFileNew and place it in your message map instead of CWinApp::OnFileNew. There is no need to call the base class implementation.

 Another common customization of ID_FILE_NEW is to provide a separate command for creating a document of each type. In this case you should define new command IDs, for example ID_FILE_NEW_CHART and ID_FILE_NEW_SHEET.

ID_FILE_OPEN : opens an existing document

Note: you must connect this to your CWinApp-derived class's message map to enable this functionality.

CWinApp::OnFileOpen has a very simple implementation of calling CWinApp::DoPromptFile followed by CWinApp::OpenDocumentFile with the file or path name of the file to open. The CWinApp implementation routine DoPromptFile brings up the standard FileOpen dialog and fills it with the file extensions obtained from the current document templates.

 One common customization of ID_FILE_OPEN is to customize the FileOpen dialog or add additional file filters. The recommended way to customize this is to replace the default implementation with your own FileOpen dialog, and call CWinApp::OpenDocumentFile with the document's file or path name. There is no need to call the base class.

ID_FILE_CLOSE : closes the currently open document

CDocument::OnFileClose calls CDocument::SaveModified to prompt the user to save the document if it has been modified and then calls OnCloseDocument. All the closing logic, including destroying the document, is done in the OnCloseDocument routine.

 In general, you should not customize this implementation since this is only one way to close a document. Overriding OnCloseDocument is the correct place to add any special closing logic for the document.

Note: ID_FILE_CLOSE acts differently from a WM_CLOSE message or an SC_CLOSE system command sent to the documents frame window. Closing a window will close the document only if that is the last frame window showing the document. Closing the document with ID_FILE_CLOSE will not only close the document but will close down all frame windows showing the document.

ID_FILE_SAVE : saves the current document

The implementation uses a helper routine CDocument::DoSave which is used for both OnFileSave and OnFileSaveAs. If you save a document that has not been saved before (that is, it does not have a path name, as in the case of FileNew) or that was read from a read-only document, the OnFileSave logic will act like the ID_FILE_SAVE_AS command and ask the user to provide a new file name. The actual process of opening the file and doing the saving is done through the virtual function OnSaveDocument.

In the case of an OLE server, this command ID is also used for the File/Update command. CDocument::OnFileSave has a default implementation that assumes documents are stored in individual disk files.

COleServerDoc::OnFileSaveOrUpdate implements the logic to save a normal document data file or to update a server document representing an OLE object embedded in some other application. The routine COleServerDoc::OnUpdateFileSaveMenu will change the menu item text from "Save" (AFX_IDS_SAVE_MENU) to "Update" (AFX_IDS_UPDATE_MENU) in the later case.

 There are two common reasons to customize ID_FILE_SAVE. For documents that do not save, simply remove the ID_FILE_SAVE menu items and toolbar buttons from your user interface. Also make sure that you never dirty your document (that is, never call CDocument::SetModifiedFlag) and the framework will never cause the document to be saved. For documents that save to someplace other than a disk file, define a new command for that operation.

 In the case of a COleServerDoc, ID_FILE_SAVE is used both for file save (for normal documents) and file update (for embedded documents).

 If your document data is stored in individual disk files, but you don't want to use the default CDocument serialize implementation, you should override CDocument::OnSaveDocument instead of OnFileSave.

ID_FILE_SAVE_AS : saves the current document under a different file name

The CDocument::OnFileSaveAs implementation uses the same CDocument::DoSave helper routine as OnFileSave. The OnFileSaveAs command is handled just as ID_FILE_SAVE if the documents had no file name before the save. COleServerDoc::OnFileSaveAs implements the logic to save a normal document data file or to save a server document representing an OLE object embedded in some other application as a separate file.

In the case of an OLE server, this command ID is also used for the File/Save/CopyAs command. The routine COleServerDoc::OnUpdateFileSaveMenu will change the menu item text from "Save As..." (AFX_IDS_SAVE_AS_MENU) to "Save Copy As..." (AFX_IDS_SAVE_COPY_AS_MENU).

 If you customize the logic of ID_FILE_SAVE, you will probably want to customize ID_FILE_SAVE_AS in a similar fashion or the operation of "Save As" may not apply to your document. You can remove the menu item from your menu bar if it is not needed.

ID_FILE_PAGE_SETUP : invokes an application-specific page setup/layout dialog

Currently there is no standard for this dialog, and the framework has no default implementation of this command.

 if you choose to implement this command, we recommend you use this command ID.

ID_FILE_PRINT_SETUP : invoke the standard Print Setup dialog

Note: you must connect this to your CWinApp-derived class's message map to enable this functionality.

This command invokes the standard print setup dialog that allows the user to customize the printer and print settings for at least this document or at most all the documents in this application. You must use the Control Panel to change the default printer settings for the entire system.

CWinApp::OnFilePrintSetup has a very simple implementation creating a CPrintDialog object and calling the CWinApp::DoPrintDialog implementation function. This sets the application default printer setup.

 The common need for customizing this command is to allow for per-document printer settings, which should be stored with the document when saved. To do this you should add a message-map handler in your CDocument class that creates a CPrintDialog object, initializes it with the appropriate printer attributes (usually hDevMode and hDevNames), call the CPrintDialog::DoModal, and save the changed printer settings. For a robust implementation, you should look at the implementation of CWinApp::DoPrintDialog for detecting errors and CWinApp::UpdatePrinterSelection for dealing with sensible defaults and tracking system-wide printer changes.

ID_FILE_PRINT : standard printing of the current document

Note: you must connect this to your CView-derived class's message map to enable this functionality.

This command prints the current document, or more correctly, starts the printing process, which involves invoking the standard print dialog and running the print engine.

CView::OnFilePrint implements this command and the main print loop. It calls the virtual CView::OnPreparePrinting to prompt of the user with the print dialog. It then prepares the output DC to go to the printer, brings up the printing progress dialog (AFX_IDD_PRINTDLG), and sends the StartDoc escape to the printer. CView::OnFilePrint also contains the main page-oriented print loop. For each page, it calls the virtual CView::OnPrepareDC followed by a StartPage escape and calling the virtual CView::OnPrint for that page. When complete, the virtual CView::OnEndPrinting is called, and the printing progress dialog is closed.

 The MFC printing architecture is designed to hook in many different ways for printing and print preview. You will normally find the various CView overridable functions adequate for any page-oriented printing tasks. Only in the case of an application that uses the printer for non-page oriented output, should you find the need to replace the ID_FILE_PRINT implementation.

ID_FILE_PRINT_PREVIEW : enter print-preview mode for the current document

Note: you must connect this to your CView-derived class's message map to enable this functionality.

CView::OnFilePrintPreview starts the print preview mode by calling the documented helper function CView::DoPrintPreview. CView::DoPrintPreview is the main engine for the print preview loop, just as OnFilePrint is the main engine for the printing loop.

 The print preview operation can be customized in a variety of ways by passing different parameters to DoPrintPreview. Please refer to Technical Note 30, which discusses some of the details of print preview and how to customize it.

ID_FILE_MRU_FILE1...FILE4 : a range of command IDs for the File MRU list

CWinApp::OnUpdateRecentFileMenu is a update command UI handler that is one of the more advanced uses of the ON_UPDATE_COMMAND_UI mechanism. In your menu resource, you must need only define a single menu item with ID ID_FILE_MRU_FILE1. That menu item remains initially disabled.

As the MRU list grows, more menu items are added to the list. The standard CWinApp implementation assumes the standard limit of the four most recently used files. The MRU list is stored in the application's .INI file. The list is loaded in your application's InitInstance function if you call LoadStdProfileSettings, and is saved when your application exits. The MRU update command UI handler also will convert absolute paths to relative paths for display on the file menu.

CWinApp::OnOpenRecentFile is the ON_COMMAND handler that performs the actual command. It simply gets the file name from the MRU list and calls CWinApp::OpenDocumentFile, which does all the work of opening the file and updating the MRU list.

 Customization of this command handler is not recommended.

ID_EDIT_CLEAR : clears the current selection

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command using CEdit::Clear. The command is disabled if there is no current selection.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_CLEAR_ALL : clears the entire document

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

 If you choose to implement this command, we recommend you use this command ID. See the SCRIBBLE tutorial for an example implementation.

ID_EDIT_COPY : copies the current selection to the Clipboard

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command, which copies the currently selected text to the Clipboard as CF_TEXT using CEdit::Copy. The command is disabled if there is no current selection.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_CUT : cuts the current selection to the Clipboard

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command, which cuts the currently selected text to the Clipboard as CF_TEXT using CEdit::Cut. The command is disabled if there is no current selection.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_FIND : begin the find operation, bring up the modeless find dialog

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command, which calls the implementation helper function OnEditFindReplace to use and store the previous find/replace settings in private implementation variables. The CFindReplaceDialog class is used to manage the modeless dialog for prompting the user.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_PASTE : insert the current Clipboard contents

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command, which copies the current Clipboard data replacing the selected text using CEdit::Paste. The command is disabled if there is no CF_TEXT in the Clipboard.

COleClientDoc just provides a update command UI handler for this command. If the Clipboard does not contain an embeddable OLE item/object, the command will be disabled. You are responsible for writing the handler for the actual command to do the actual pasting. If your OLE application can also paste other formats, you should provide your own update command UI handler in your view or document (that is, somewhere before COleClientDoc in the command target routing).

 If you choose to implement this command, we recommend you use this command ID.

 For replacing the standard OLE implementation, use COleClientItem::CanPaste.

ID_EDIT_PASTE_LINK : insert a link from the current Clipboard contents

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

COleClientDoc just provides a update command UI handler for this command. If the Clipboard does not contain linkable OLE item/object, the command will be disabled. You are responsible for writing the handler for the actual command to do the actual pasting. If your OLE application can also paste other formats, you should provide your own update command UI handler in your view or document (that is, somewhere before COleClientDoc in the command target routing).

 If you choose to implement this command, we recommend you use this command ID.

 For replacing the standard OLE implementation, use COleClientItem::CanPasteLink.

ID_EDIT_PASTE_SPECIAL : insert the current Clipboard contents with options

Currently there is no standard implementation for this command. You must implement this for each CView-derived class. MFC does not provide this dialog.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_REPEAT : repeat the last operation

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command to repeat the last find operation. The private implementation variables for the last find are used. The command is disabled if a find can not be attempted.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_REPLACE : begin the replace operation, bring up the modeless replace dialog

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command, which calls the implementation helper function OnEditFindReplace to use and store the previous find/replace settings in private implementation variables. The CFindReplaceDialog class is used to manage the modeless dialog that prompts the user.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_SELECT_ALL : select the entire document

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command, which selects all the text in the document. The command is disabled if there is no text to select.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_UNDO : undo the last operation

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

CEditView provides an implementation of this command, using CEdit::Undo. The command is disabled if CEdit::CanUndo returns FALSE.

 If you choose to implement this command, we recommend you use this command ID.

ID_EDIT_REDO : redo the last operation

Currently there is no standard implementation for this command. You must implement this for each CView-derived class.

 If you choose to implement this command, we recommend you use this command ID.

ID_WINDOW_NEW : open another window on the active document

CMDIFrameWnd::OnWindowNew implements this powerful feature by using the document template of the current document to create another frame containing another view of the current document.

Like most multiple document interface (MDI) Window menu commands, the command is disabled if there is no active MDI child window.

 Customization of this command handler is not recommended. If you wish to provide a command that creates additional views or frame windows, you will probably be better off inventing your own command. You can clone the code from CMDIFrameWnd::OnWindowNew and modify it to the specific frame and view classes of your liking.

ID_WINDOW_ARRANGE : arrange icons at the bottom of an MDI window

CMDIFrameWnd implements this standard MDI command in an implementation helper function OnMDIWindowCmd. This helper maps command IDs to MDI Windows messages and can therefore share a lot of code.

Like most MDI Window menu commands, the command is disabled if there is no active MDI child window.

 Customization of this command handler is not recommended.

ID_WINDOW_CASCADE : cascade windows so they overlap

CMDIFrameWnd implements this standard MDI command in an implementation helper function OnMDIWindowCmd. This helper maps command IDs to MDI Windows messages and can therefore share a lot of code.

Like most MDI Window menu commands, the command is disabled if there is no active MDI child window.

 Customization of this command handler is not recommended.

ID_WINDOW_TILE_HORZ : tile windows horizontally

This command is implemented in CMDIFrameWnd just like ID_WINDOW_CASCADE, except a different MDI Windows message is used for the operation.

 You should pick the default tile orientation for your application. You can do this by changing the ID for the Window "Tile" menu item to either ID_WINDOW_TILE_HORZ or ID_WINDOW_TILE_VERT.

ID_WINDOW_TILE_VERT : tile windows vertically

This command is implemented in CMDIFrameWnd just like ID_WINDOW_CASCADE, except a different MDI Windows message is used for the operation.

 You should pick the default tile orientation for your application. You can do this by changing the ID for the Window "Tile" menu item to either ID_WINDOW_TILE_HORZ or ID_WINDOW_TILE_VERT.

ID_WINDOW_SPLIT : keyboard interface to splitter

CView handles this command for the CSplitterWnd implementation. If the view is part of a splitter window, this command will delegate to the implementation function CSplitterWnd::DoKeyboardSplit. This will place the splitter in a mode that will allow keyboard users to split or unsplit a splitter window.

This command is disabled if the view is not in a splitter.

 Customization of this command handler is not recommended.

ID_APP_ABOUT : invoke the About dialog box

There is no standard implementation for an application's About box. The default AppWizard-created application will create a custom dialog class for your application and use it as your About box. AppWizard will also write the trivial command handler which handles this command and invokes the dialog.

 You will almost always implement this command.

ID_APP_EXIT : exit the application

CWinApp::OnAppExit handles this command by sending a WM_CLOSE message to the application's main window. The standard shutting down of the application (prompting for dirty files and so on) is handled by the CFrameWnd implementation.

 Customization of this command handler is not recommended. Overriding CWinApp::SaveAllModified or the CFrameWnd closing logic is recommended.

 If you choose to implement this command, we recommend you use this command ID.

ID_HELP_INDEX : list Help topics from .HLP file

Note: you must connect this to your CWinApp-derived class's message map to enable this functionality.

CWinApp::OnHelpIndex handles this command by trivially calling CWinApp::WinHelp.

 Customization of this command handler is not recommended.

ID_HELP_USING : display help on how to use Help

Note: you must connect this to your CWinApp-derived class's message map to enable this functionality.

CWinApp::OnHelpUsing handles this command by trivially calling CWinApp::WinHelp.

 Customization of this command handler is not recommended.

ID_CONTEXT_HELP : enter SHIFT-F1 help mode

Note: you must connect this to your CWinApp-derived class's message map to enable this functionality.

CWinApp::OnContextHelp handles this command by setting the help mode cursor, entering a modal loop and waiting for the user to select a window to get help on. Please refer to Technical Note 28 for more details on the MFC Help implementation.

 Customization of this command handler is not recommended.

ID_HELP : give help on the current context

Note: you must connect this to your CWinApp-derived class's message map to enable this functionality.

CWinApp::OnHelp handles this command by getting the right help context for the current application context. This handles simple F1 help, help on message boxes and so on. Please refer to Technical Note 28 for more details on the MFC help implementation.

 Customization of this command handler is not recommended.

ID_DEFAULT_HELP : display default help for context

Note: you must connect this to your CWinApp-derived class's message map to enable this functionality.

This command is usually mapped to CWinApp::OnHelpIndex.

 A different command handler can be provided if a distinction between default Help and the Help index is desired.

ID_NEXT_PANE : go to next pane

CView handles this command for the CSplitterWnd implementation. If the view is part of a splitter window, this command will delegate to the implementation function CSplitterWnd::OnNextPaneCmd. This will move the active view to the next pane in the splitter.

This command is disabled if the view is not in a splitter or there is no next pane to go to.

 Customization of this command handler is not recommended.

ID_PREV_PANE : go to previous pane

CView handles this command for the CSplitterWnd implementation. If the view is part of a splitter window, this command will delegate to the implementation function CSplitterWnd::OnNextPaneCmd. This will move the active view to the previous pane in the splitter.

This command is disabled if the view is not in a splitter or there is no previous pane to go to.

 Customization of this command handler is not recommended.

ID_OLE_INSERT_NEW : insert a new OLE object

Currently there is no standard implementation for this command. You must implement this for your CView-derived class to insert a new OLE item/object at the current selection.

All OLE client applications should implement this command. AppWizard, with the OLE option, will create a skeleton implementation of OnInsertObject in your view class which you will have to complete.

 See the OCLIENT example for a complete implementation of this command.

ID_OLE_EDIT_LINKS : edit OLE links

COleClientDoc handles this command by using the MFC-provided implementation of the standard OLE links dialog. The implementation of this dialog is accessed through the public AfxOleLinksDialog function. If the current document does not contain any links, the command is disabled.

 Customization of this command handler is not recommended.

ID_OLE_VERB_FIRST...LAST : an ID range for OLE verbs

COleClientDoc uses this command ID range for the verbs supported by the currently selected OLE item/object. This must be a range since a given OLE item/object type can support zero or more custom verbs. In your application's menu, you should have one menu item with the ID of ID_OLE_VERB_FIRST. When the program is run, the menu will be updated with the appropriate menu verb description (or pop-up menu with many verbs). The management of the OLE menu is handled by AfxOleSetEditMenu, done in the update command UI handler for this command.

There are no explicit command handlers for handling each of the command ID in this range. COleClientDoc::OnCmdMsg is overridden to trap all command IDs in this range, turn them into zero-based verb numbers, and launch the server for that verb (using COleClientItem::DoVerb).

 Customization or other use of this command ID range is not recommended.

ID_VIEW_TOOLBAR : toggle the toolbar on and off

CFrameWnd handles this command and the update-command UI handler to toggle the visible state of the toolbar. The toolbar must be a child window of the frame with child window ID of AFX_IDW_TOOLBAR. The command handler actually toggles the visibility of the toolbar window. CFrameWnd::RecalcLayout is used to redraw the frame window with the toolbar in its new state. The update-command UI handler checks the menu item when the toolbar is visible.

 customization of this command handler is not recommended. If you wish to add additional toolbars, you will want to clone and modify the command handler and the update-command UI handler for this command.

ID_VIEW_STATUS_BAR : toggle the status bar on and off

This command is implemented in CFrameWnd just like ID_VIEW_TOOLBAR, except a different child window ID (AFX_IDW_STATUS_BAR) is used.

Update-only Command Handlers

There are several standard command IDs that are used as indicators in status bars. These use the same update-command UI handling mechanism to display their current visual state during application idle time. Since they can't be selected by the user (that is, you can't push a status bar pane), then it makes no sense to have an ON_COMMAND handler for these command IDs.

ID_INDICATOR_CAPS : CAP lock indicator

ID_INDICATOR_NUM : NUM lock indicator

ID_INDICATOR_SCRL : SCRL lock indicator

All three of these are implemented in CFrameWnd::OnUpdateKeyIndicator, an implementation helper that uses the command ID to map to the appropriate Virtual Key. A common implementation enables or disables (for status panes disabled = no text) the CCmdUI object depending on whether the appropriate Virtual Key is currently locked.

 Customization of this command handler is not recommended.

ID_INDICATOR_EXT : EXTended select indicator

ID_INDICATOR_OVR : OVeRstrike indicator

ID_INDICATOR_REC : RECording indicator

All three of these are implemented in CFrameWnd::OnUpdateKeyIndicator, an implementation helper that uses the command ID to map to the appropriate Virtual Key. A common implementation enables or disables (for status panes disabled = no text) the CCmdUI object depending on whether the appropriate Virtual Key is currently locked.

 Customization of this command handler is not recommended.

Currently there is no standard implementation for these indicators.

 if you choose to implement these indicators, we recommend you use these indicator IDs and maintaining the ordering of the indicators in your status bar (that is, in this order: EXT, CAP, NUM, SCRL, OVR, REC)


Standard MFC Resources
Technical Note 23: Standard MFC Resources

This note describes the standard resources provided with and needed by the MFC library.

Standard Resources

MFC offers two categories of predefined resources that you can use in your application:  clip-art resources and standard framework resources.

Clip-art resources are additional resources that the framework does not depend on, but which you might want to add to your application's user interface. The following clip-art resources are contained in the \MSVCNT\MFC\SAMPLES\APSTUDIO directory:

COMMON.RES: a single file of binary resources containing

 A large collection of icons that represent a variety of business and data-processing tasks

 Several common cursors (see also AFXRES.RC)

 A toolbar bitmap containing several toolbar buttons

 The bitmap and icon resources used by COMMDLG.DLL

INDICATE.RC: contains string resources for the status-bar key-state indicators, such as "CAP" for Caps Lock.

PROMPTS.RC: contains menu-prompt string resources for each of the predefined commands, such as "Create a new document" for ID_FILE_NEW.

COMMDLG.RC: an App Studio compatible .RC file containing the standard COMMDLG dialog templates.

Standard framework resources are resources with AFX-defined IDs that the framework depends on for internal implementations. You will rarely need to change these AFX-defined resources. If you do, you should follow the procedure outlined below.

The following framework resources are contained in the \MSVCNT\MFC\INCLUDE directory:

AFXRES.RC:  common resources used by the framework

AFXPRINT.RC:  resources specific to printing

AFXOLECL.RC:  resources specific to OLE client applications

AFXOLESV.RC:  resources specific to full OLE server applications

Using Clip-Art Resources

To use a clip-art binary resource:

1.	Open your application's resource file in App Studio.

2.	Open COMMON.RES, this contains all the binary clip-art resources.

3.	Press CTRL and drag to copy the resource(s) you want from COMMON.RES to your application's resource file.

4.	If you want, rename the resource from a string name to a symbol.

To use other clip-art resources, the steps are as above except you open the appropriate .RC file instead of COMMON.RES. Renaming the resource ID is not needed since the clip-art resources will already be assigned the appropriate symbols for you.

Note:  Be careful not to unintentionally move resources out of COMMON.RES permanently. You can avoid this by CTRL-dragging (copy) rather than straight dragging (move the resources). You can also avoid this by always responding with "No" when you're asked whether you want to save the changes to COMMON.RES.

The .RC resource files have a special TEXTINCLUDE resource in them that will prevent you from accidentally saving on top of the standard .RC files.

Customizing Standard Framework Resources

Standard framework resources are usually #include'd in your application's .RC file. AppWizard will generate a .RC file that includes the appropriate standard framework resources depending on which AppWizard options you choose. You can review, add, or remove which resources are #include'd by using App Studio's "Set Includes" command in the File menu of App Studio and looking at the "Compile-Time Directives" edit item. For example:

#include "afxres.rc"

#include "afxprint.rc"

The most common case of customizing standard framework resources is adding or removing additional includes for printing, OLE Client, and OLE Server support.

In some rare case you may wish to customize the contents of the standard framework resources for your particular application, not just add and remove the entire file.

To customize the contents of a standard resource file, please follow these steps.

Open your application's resource file in App Studio.

Using App Studio's File Set Includes command, remove the #include for the standard .rc file you want to customize (for example, to customize the print-preview toolbar, remove the #include "afxprint.rc" line).

Open the appropriate standard resources files in \MSVCNT\MFC\INCLUDE (for example, \MSVCNT\MFC\INCLUDE\AFXPRINT.RC)

Copy all of the resources from the standard .rc file to your application resource file.

This is an all or none proposition. Either you #include all of the resources from the .rc file in \MSVCNT\MFC\INCLUDE or you have a (customized) copy of these resources in your application resource file.

Modify the copy of the standard resources in your application resource file.

Note:  Be especially careful not to modify the resources directly in the standard .RC files.


MFC-Defined Messages and Resources
Technical Note 24: MFC-Defined Messages and Resources

This note describes the internal Windows messages and resource formats used by MFC. This information explains the implementation of the framework, and will assist you in debugging your application. For the adventurous, even though all this information is officially unsupported, you may use some of this information for advanced implementations.

This note contains MFC private implementation details; all the contents are subject to change in the future. MFC private Windows messages have meaning in the scope of one application only but will change in the future to contain system-wide messages.

The range of MFC private Windows messages and resource types are in the reserved "system" range set aside by Microsoft Windows. Currently not all numbers in the ranges are used and, in the future, new numbers in the range may be used. The currently used numbers may be changed.

MFC private Windows messages are in the range 0x360->0x37F.

MFC private resource types are in the range 0xF0->0xFF.

MFC Private Windows Messages

These Windows messages are used in place of C++ virtual functions where relatively loose coupling is required between window objects and where a C++ virtual function would not be appropriate.

These private Windows messages and associated parameter structures are declared in the MFC private header 'AFXPRIV.H'. Be warned that any of your code that includes this header may be relying on undocumented behavior and will likely break in future versions of MFC.

In the rare case of needing to handle one of these messages, you should use the ON_MESSAGE message map macro and handle the message in the generic LRESULT/WPARAM/LPARAM format.

WM_SIZEPARENT

This message is sent by a frame window to its immediate children during resizing (CFrameWnd::OnSize calls CFrameWnd::RecalcLayout which calls CWnd::RepositionBars) to reposition the control bars around the side of the frame. The AFX_SIZEPARENTPARAMS structure contains the current available client rectangle of the parent and a HDWP with which to call DeferWindowPos to minimize repainting.

wParam	not used

lParam	address of an AFX_SIZEPARENTPARAMS structure

returns	not used (0)

Ignoring the message indicates that the window doesn't take part in the layout.

WM_SETMESSAGESTRING

This message is sent to a frame window to ask it to update the message line in the status bar. Either a string ID or a LPCSTR can be specified (but not both).

wParam	string ID (or zero)

lParam	LPCSTR for the string (or NULL)

returns	not used (0)

WM_IDLEUPDATECMDUI

This message is sent in idle time to implement the idle-time update of update-command UI handlers. If the window (usually a control bar) handles the message, it creates a CCmdUI object (or an object of a derived class) and call CCmdUI::DoUpdate for each of the "items" in the window. This will in turn check for an ON_UPDATE_COMMAND_UI handler for the objects in the command-handler chain.

wParam	BOOL bDisableIfNoHandler

lParam	not used (0)

returns	not used (0)

bDisableIfNoHandler is non-zero to disable the UI object if there is neither an ON_UPDATE_COMMAND_UI nor an ON_COMMAND handler.

WM_INITIALUPDATE

This message is sent by the document template to all descendants of a frame window when it is safe for them to do their initial update. It maps to a call to CView::OnInitialUpdate but can be used in other CWnd-derived classes for other one-shot updating.

wParam	not used (0)

lParam	not used (0)

returns	not used (0)

WM_COMMANDHELP, WM_HELPHITTEST, WM_EXITHELPMODE

These messages are used in the implementation of context-sensitive Help. Please refer to Technical Note 28 for more information.

MFC Private Resource Formats

There is currently only one MFC private resource format defined, RT_DLGINIT.

RT_DLGINIT Resource Format

One MFC private resource format is used to store extra dialog initialization information. This includes the initial strings stored in a combo box. The format of this resource is not designed to be manually edited, but is handled by App Studio.

App Studio and this RT_DLGINIT resource are not required to use the related features of MFC since there are API alternative to using the information in the resource. Using App Studio makes it much easier to write, maintain, and translate your application in the long run.

The basic structure of a RT_DLGINIT resource is as follows:

+---------------+                    \

| Control ID    |   UINT             |

+---------------+                    |

| Message #     |   UINT             |

+---------------+                    |

|length of data |   DWORD            |

+---------------+                    | Repeated

|   Data        |   Variable Length  |   for each control

|     ...      |   and Format       |   and message

+---------------+                    /

|     0         |   BYTE

+---------------+

A repeated section contains the control ID to send the message to, the Message # to send (a normal Windows message) and a variable length of data. The Windows message is sent in a form:

SendDlgItemMessage(<Control ID>, <Message #>, 0, &<Data>);

This is a very general format, allowing any Windows messages and data content. App Studio and MFC only support a limited subset of Windows messages: CB_ADDSTRING for the initial list-choices for combo boxes (the data is a text string).


Document, View, and Frame Creation
Technical Note 25: Document, View, and Frame Creation

This note describes the creation and ownership issues for WinApps, DocTemplates, Documents, Frames and Views. You can find most of this information, and other interesting creation and ownership issues, in Chapter 2 of the Class Library Reference.

WinApp

There is one CWinApp object in the system.

It is statically constructed and initialized by the framework's internal implementation of WinMain. You must derive from CWinApp to do anything useful.

The one CWinApp object owns a list of document templates (a CPtrList). There is one or more document template per application. DocTemplates are usually loaded from the resource file (that is, a string array) in CWinApp::InitInstance.

pTemplate = new CDocTemplate;

VERIFY(pTemplate->LoadTemplate(IDR_DOC1));

AddDocTemplate(pTemplate);

The one CWinApp object owns all frame windows in the application. The main frame window for the application should be stored in CWinApp::m_pMainWnd; usually you set m_pMainWnd in the InitInstance implementation if you have not let AppWizard do it for you. For single document interface (SDI) this is one CFrameWnd that serves as the main application frame window as well as the only document frame window. For multiple document interface (MDI) this is an MDI-Frame (class CMDIFrameWnd) which serves as the main application frame window that contains all the child CFrameWnds. Each child window is of class CMDIChildWnd (derived from CFrameWnd) and serves as one of potentially many document frame windows.

DocTemplates

The CDocTemplate is the creator and manager of documents. It owns the documents which it creates. If your application uses the resource-based approach described below, it will not need to derive from CDocTemplate.

For an SDI application, the class CSingleDocTemplate keeps track of one open document. For an MDI application, the class CMultiDocTemplate keeps a list (a CPtrList) of all the currently open documents created from that template. CDocTemplate::AddDocument and CDocTemplate::RemoveDocument provide the virtual member functions for adding or removing a document from the template. CDocTemplate is a friend of CDocument so we can set the protected CDocument::m_pDocTemplate back pointer to point back to the doc template that created the document.

CWinApp handles the default OnFileOpen implementation, which will in turn query all the doc templates. The implementation includes looking for already open documents and deciding what format to open new documents in.

CDocTemplate manages the UI binding for documents and frames.

CDocTemplate keeps a count of the number of unnamed documents.

CDocument

A CDocument is owned by a CDocTemplate.

Documents have a list of currently open views (derived from CView) that are viewing the document (a CPtrList).

Documents do not create/destroy the views, but they are attached to each other after they are created. When a document is closed (that is, through File/Close), all attached views will be closed. When the last view on a document is closed (that is, Window/Close) the document will be closed.

The CDocument::AddView, RemoveView interface is used to maintain the view list. CDocument is a friend of CView so we can set the CView::m_pDocument back pointer.

CFrameWnd

A CFrameWnd (also known as a frame) plays the same role as in MFC 1.0, but now the CFrameWnd class is designed to be used in many cases without deriving a new class. The derived classes CMDIFrameWnd and CMDIChildWnd are also enhanced so many standard commands are already implemented.

The CFrameWnd is responsible for creating windows in the client area of the frame. Normally there is one main window filling the client area of the frame.

For an MDI-Frame window, the client area is filled with the "MDICLIENT" control which is in turn the parent of all the MDI-Child frame windows. For an SDI-Frame window or an MDI-Child frame window, the client area is usually filled with a CView-derived window object. In the case of CSplitterWnd, the client area of the view is filled with the CSplitterWnd window object, and the CView-derived window objects (one per split pane) are created as child windows of the CSplitterWnd.


DDX and DDV Routines
Technical Note 26: DDX and DDV Routines

This note describes the dialog data exchange (DDX) and dialog data validation (DDV) architecture. It also describes how you write a DDX_ or DDV_ procedure and how you can extend ClassWizard to use your routines.

Overview of Dialog Data Exchange

All dialog data functions are done with C++ code. There are no special resources or magic macros. The heart of the mechanism is a virtual function that is overridden in every dialog class that does dialog data exchange and validation. It is always found in this form:

void CMyDialog::DoDataExchange(CDataExchange* pDX)

{

    CDialog::DoDataExchange(pDX);    // call base class

    //{{AFX_DATA_MAP(CMyDialog)

        <data_exchange_function_call>

        [<data_validation_function_call>]

    //}}AFX_DATA_MAP

}

The special format AFX comments allow ClassWizard to locate and edit the code within this function. Code that is not compatible with ClassWizard should be placed outside of the special format comments.

In the above example, <data_exchange_function_call> is in the form:

DDX_Foo(pDX, nIDC, field);

and <data_validation_function_call> is optional and is in the form:

DDV_Bar(pDX, field, ...);

More than one DDX_/DDV_ pair may be included in each DoDataExchange function.

See 'afxdd_.h' for a list of all the dialog data exchange routines and dialog data validation routines provided with MFC.

Dialog data is just that - member data in the CMyDialog class. It is not stored in a struct or anything special like that.

Notes

 Although we call this "dialog data," all features are available in any class derived from CWnd and are not limited to just dialogs.

 Initial values of data are set in the standard C++ constructor, usually in a block with //{{AFX_DATA_INIT and //}}AFX_DATA_INIT comments.

 CWnd::UpdateData is the operation that does the initialization and error handling around the call to DoDataExchange.

 You can call CWnd::UpdateData at any time to perform data exchange and validation. By default UpdateData(TRUE) is called in the default CDialog::OnOK handler and UpdateData(FALSE) is called in the default CDialog::OnInitDialog.

 The DDV_ routine should immediately follow the DDX_ routine for that field.

How does it Work?

You do not need to understand the following in order to use dialog data. However, understanding how this works behind the scenes will help you write your own exchange or validation procedure.

The DoDataExchange member function is much like the Serialize member function - it is responsible for getting or setting data to/from an external form (in this case controls in a dialog) from/to member data in the class. The pDX parameter is the context for doing data exchange and is similar to the CArchive parameter to CObject::Serialize. The pDX (a CDataExchange object) has a direction flag much like CArchive has a direction flag:

if !m_bSaveAndValidate, then load the data state into the controls

if m_bSaveAndValidate, then set the data state from the controls

Validation only occurs when m_bSaveAndValidate is set. The value of m_bSaveAndValidate is determined by the BOOL parameter to CWnd::UpdateData.

There are three other interesting CDataExchange members:

m_pDlgWnd:
The window (usually a dialog) that contains the controls. This is to prevent callers of the DDX_ and DDV_ global functions from having to pass 'this' to every DDX/DDV routine.

PrepareCtrl, and PrepareEditCtrl:
Prepares a dialog control for data exchange. Stores that control's handle for setting the focus if a validation fails. PrepareCtrl is used for nonedit controls and PrepareEditCtrl is used for edit controls.

Fail:
Called after bringing up a message box alerting the user to the input error. This routine will restore the focus to the last control (the last call to PrepareCtrl/PrepareEditCtrl) and throw an exception. This member function may be called from both DDX_ and DDV_ routines.

User Extensions

There are several ways to extend the default DDX/DDV mechanism. You can

add new data types.

CTime

add new exchange procedures (DDX_???).

void PASCAL DDX_Time(CDataExchange* pDX, int nIDC, CTime& tm);

add new validation procedures (DDV_???).

void PASCAL DDV_TimeFuture(CDataExchange* pDX, CTime tm, BOOL bFuture);

// make sure time is in the future or past

pass arbitrary expressions to the validation procedures.

DDV_MinMax(pDX, age, 0, m_maxAge);

NOTE: Such arbitrary expressions cannot be edited by ClassWizard and therefore should be moved outside of the special format comments (//{{AFX_DATA_MAP(CMyClass))

Have the DoDialogExchange member function include conditionals or any other valid C++ statements with intermixed exchange and validation procs.

DDX_Check(pDX, IDC_SEX, m_bFemale);

DDX_Text(pDX, IDC_EDIT1, m_age);

if (m_bFemale)

    DDV_MinMax(pDX, age, 0, m_maxFemaleAge);

else

    DDV_MinMax(pDX, age, 0, m_maxMaleAge);

NOTE: Again, such code can not be edited by ClassWizard and should be used only outside of the special format comments.

ClassWizard Support

ClassWizard supports a subset of DDX/DDV customizations by allowing you to integrate your own DDX_ and DDV_ routines into the ClassWizard user interface. To do this, special entries are made in APSTUDIO.INI or in your project .CLW file. Doing this is only cost benificial if you plan to reuse particular DDX and DDV routines time and time again in a project or in many projects.

The special entries can be entered either in the [General Info] section of your project's .CLW file or in the [ExtraDDX] section in APSTUDIO.INI file. If you plan to use the custom DDX_/DDV_ routines only in a certain project, add the entries to the [General Info] section of your project .CLW file instead. If you plan to use the routines on many projects, add the entries to the [ExtraDDX] section of your APSTUDIO.INI file.

The general format of these special entries is:

ExtraDDXCount=n

; where n is the number of ExtraDDX? lines to follow

ExtraDDX?=<keys>;<prompt>;<type>;<initValue>;<DDX_Proc>[;<DDV_Proc>;<prompt1>;<arg1>;[<prompt2>;<fmt2>]]

; Where ? is a number 1-n indicating which DDX type in the list that is being defined.

Each field is delimited by a ';' character. The fields and their purpose are described below.

<keys> = list of single characters indicating for which dialog controls this variable type is allowed.

E = edit

C = two-state check box

c = tri-state check box

R = first radio button in a group

L = non-sorted list box

l = sorted list box

M = combo box (with edit item)

N = non-sorted drop list

n = sorted drop list

1 = if the DDX insert should be added to head of list (default is add to tail)  This is generally used for DDX routines that transfer the 'Control' property.

<prompt> = string to place in the "Property" combo box (no quotes)

<type> = single identifier for type to emit in the header file.

In our example above with DDX_Time, this would be set to CTime.

<initValue> = initial value - 0 or blank

If it is blank, then no initialization line will be written in the //{{AFX_DATA_INIT section of the implementation file. A blank entry should be used for C++ objects (such as CString, CTime, and so on) that have constructors that guarantee correct initialization.

<DDX_Proc> = single identifier for the DDX_ procedure

The C++ function name must start with "DDX_", but don't include "DDX_" in the <DDX_Proc> identifier. In the example above, the <DDX_Proc> identifier would be Time. When ClassWizard writes the function call to the implementation file in the {{AFX_DATA_MAP section, it appends this name to DDX_, thus arriving at DDX_Time.

<comment> = comment to show in dialog for variable with this DDX. Place any text you would like here, usually provide something that describes the operation performed by the DDX/DDV pair.

The DDV portion of the entry is optional. Not all DDX routines have corresponding DDV routines. Often, it is more convenient to include the validation phase as an integral part of the transfer. This is often the case when your DDV routine doesn't require any parameters, since ClassWizard doesn't support DDV routines without any parameters.

<DDV_Proc> = single identifier for the DDV_ procedure

The C++ function name must start with "DDV_" but don't include "DDX_" in the <DDX_Proc> identifier.

followed by 1 or 2 DDV args:

<promptX> = string to place above the edit item (with & for accelerator)

<fmtX> = format character for the arg type, one of

d = int

u = unsigned

D = long int (that is, long)

U = long unsigned (that is, DWORD)

f = float

F = double

s = string

An Example of Custom DDX

An example of custom DDX with validation can be found in the CHKBOOK sample application. See DDX_DollarsCents in dollcent.cpp for an sample implementation of a custom DDX routine and CHKBOOK.CLW for the corresponding example ExtraDDXCount and ExtraDDX1 entries in the [General Info] section of the .CLW file.


Reporting Problems with VBX Controls

Microsoft tested and verified that a large number of VB version 1.0 VBX custom controls work with App Studio and MFC 2.0, but the VBX you are using may not be one of them, especially if it is one not widely distributed.

IMPORTANT: If you experience any of the problems identified below, please contact the developer of the VBX control to obtain information about a possible update of the control. Microsoft is actively working with VBX control vendors to ensure that VBX controls work successfully with AppStudio and MFC 2.0.

Possible symptoms of a control that does not work with App Studio and MFC 2.0 are:

repaint problems (not painting or painting too much)

setting property values is not reflected in the control

inability to resize the control

control does not reflect tab order/grouping property accurately

control does not function under App Studio test mode

control cannot be properly initialized if programmatically created because it assumes that it is being loaded from a form

unrecoverable application error (UAE)

general protection fault

debug kernel fatal exit


Standard VBX Events

The following are events that may set sent by a VBX control:

Click

DblClick

DragDrop

DragOver

GotFocus

KeyDown, KeyUp

KeyPress

LostFocus

MouseDown, MouseUp

MouseMove


Standard VBX Properties

The following are standard properties that are defined for VBX controls:

BackColor	FontName	MousePointer
BorderStyle	FontSize	Parent
Caption	FontStrikethru	TableIndex
CtlName	FontUnderline	TabStop
DragIcon	ForeColor	Tag
DragMode	Height	Text
Enabled	hWnd	Top
FontBold	Index	Visible
FontItalic	Left	Width


CVBControl::m_nError Values

The CVBControl::m_nError public data member variable holds the custom control error value when a property access member function,  such as CVBControl::GetNumProperty or SetNumProperty, generates an error. (The value of m_nError is equivalent to the Visual Basic run-time error code.)

The following table lists the general error code values that m_nError can have.

m_nError	Meaning
7	Out of memory.
61	Disk full.
62	Input past end of file.
380	Invalid property value.
420	Invalid object reference.
422	Property item not found.
Custom controls may also generate error codes that are specific to the control. Refer to the individual custom control description for any specific error codes.


VBX Event-Handling Function Parameters

In order to use VBX control events, you need to provide a linkage between the controls events and event-handling functions in your application. Visual C++ provides the ClassWizard tool to help you link events to functions. You can use ClassWizard to:

Create a member variable that points to the control. This pointer is placed in the declaration of the class (usually derived from CDialog) that contains the VBX control.

Register events (messages) for the control.

Create a message map that associates the controls events to the member functions that handle the events.

Create event-handling functions for VBX control notification in your implementation file.

For more information on how to use ClassWizard with custom controls, refer to Chapter 17, "Programming with VBX Controls," of the Class Library Users Guide.

When the ClassWizard creates an event-handling function template, the function is called with four arguments. For example, the DIALOG.CPP file in the VBCIRCLE sample application contains the following line:

void CCircleDialog::OnClickinCircle(UINT, int, CWnd*, LPVOID)

The four parameters that are passed to OnClickinCircle are not named since they are not used in the function itself. To use a function parameter you need to name it. Normally, the first two parameters are not used. The following table lists the naming convention and the description for each parameter:

Parameter	Type	Description
uCode	UINT	Notification code (normally not used).
nIndex	int	Index of event in event table (normally not used).
pWnd	CWnd*	Pointer to control.
lpParams	LPVOID	Event parameter structure.
Accessing VBX Event Parameters

Some VBX controls pass event parameters when an event is generated. For example, the multimedia MCI control passes a notification code when the Done event is generated. This means that the VBX event-handling function needs to extract the parameters from the lpParams argument.

It is recommended that the following four macro definitions be placed in a global header, such as STDAFX.H. These macro functions extract a data value from the lpParams argument.

#define AFX_NUM_EVENTPARAM(type, lpParams) \

(type FAR&)(**(type FAR* FAR*)lpParams)

#define AFX_HLSTR_EVENTPARAM(lpParams) \

(HLSTR FAR&)(*(HLSTR FAR*)lpParams)

#define AFX_NUM_EVENTPARAMINDEX(type, lpParams, index) \

(short FAR&)(**((type FAR* FAR*)(lpParams) + index))

#define AFX_HLSTR_EVENTPARAMINDEX(lpParams, index) \

(HLSTR FAR&)(*((HLSTR FAR*)(lpParams) + index))

The first two macros, AFX_NUM_EVENTPARAM and AFX_HLSTR_EVENTPARAM, provide access to a parameter when there is only one event parameter. For example, the standard KeyPress event would use (in Visual Basic syntax) the event handler:

Sub Control_KeyPress(KeyAscii As Integer)

Class Wizard will provide the event handler function:

void CMyDialog::OnControlKeyPress(UINT, int, CWnd*, LPVOID)

You need to add a name for the LPVOID parameter:

void CMyDialog::OnControlKeyPress(UINT, int, CWnd*, LPVOID lpParams)

Then, to reference the KeyAscii parameter, you use the AFX_NUM_EVENTPARAM macro:

short KeyAscii = AFX_NUM_EVENTPARAM(short, lpParams);  // Get param

AFX_NUM_EVENTPARAM(short, lpParams) = NewKey;  // Set parameter

The last two macros, AFX_NUM_EVENTPARAMINDEX and AFX_HLSTR_EVENTPARAMINDEX, provide access when there is more than one parameter. When using the indexed versions of the macros, the rightmost parameter is always index 0 with each parameter to the left being one higher. For example, the standard KeyUp event uses the Visual Basic handler:

Sub Control_KeyUp(KeyCode As Integer, Shift As Integer)

The Visual C++ event handler would be

void CMyDialog::OnControlKeyUp(UINT, int, CWnd*, LPVOID lpParams)

And references to the parameters would be:

short KeyCode = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 1);

short Shift = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 0);

Note that the macro functions return references, so they can be used on the left side of an assignment operator. The types that should be used for each of the possible Visual Basic event parameter types are:

Data type of lpParams	Usage
Integer (Boolean, Enum)	AFX_NUM_EVENTPARAM(short, lpParams)
Long	AFX_NUM_EVENTPARAM(LONG, lpParams)
Single	AFX_NUM_EVENTPARAM(float, lpParams)
Double	AFX_NUM_EVENTPARAM(double, lpParams)
String	AFX_HLSTR_EVENTPARAM(lpParams)
For example, if you want to extract the notification code of the Done event that the multimedia control passes to the event-handling function, you could write:

void CVideoDialog::OnDoneMmcontrol1(UINT, int, CWnd*, LPVOID lpParams)

{

LONG lCode = AFX_NUM_EVENTPARAM(LONG, lpParams);

...

}

Accessing the VBX Control Pointer

The third argument passed to VBX event-handling functions is the pointer to the VBX custom control that generated the event. If you give the argument a name (for example, pWnd), you can use any of the standard CWnd class functionality. However, since pWnd is actually a CVBControl class, you can typecast pWnd to be a CVBControl pointer.

void CCircleDialog::OnClickinCircle(UINT, int, CWnd* pWnd, LPVOID)

{

CVBControl* pCircle = (CVBControl *)pWnd;

LPCSTR lpClass = pCircle->GetVBXClass();

...

}


How to Manage VBX Picture Properties in MFC

Some VBX controls have properties of the Picture type. In MFC, the value of these properties are HPIC handles. The HPIC handle type is declared in AFXEXT.H.

HPIC handles represent an object that can contain an HBITMAP, HICON, or HMETAFILE. HPIC handles are reference counted and are automatically deleted when the reference count drops to 0. Once the reference count drops to 0, references to the HPIC will result in program errors.

Although you cannot gain access to the internal MFC structure represented by the HPIC, you may get the information in a PIC structure by calling the global function AfxGetPict, described at the end of this section. Calling AfxGetPict to get picture information is roughly requivalent to calling GetObject to get information about a GDI object.

The PIC structure is a tagged union structure:

#pragma pack(1)

struct NEAR PIC

{

    BYTE    picType;

    union

    {

        struct

        {

            HBITMAP   hbitmap;   // bitmap

        } bmp;

        struct

        {

            HMETAFILE hmeta;     // Metafile

            int     xExt, yExt;  // extent

        } wmf;

        struct

        {

            HICON     hicon;     // Icon

        } icon;

    } picData;

    // Implementation

    WORD    nRefCount;

    BYTE    picReserved[2];

};

#pragma pack()

picType can have one of the following four values defined in AFXEXT.H:

PICTYPE_NONE

PICTYPE_BITMAP

PICTYPE_METAFILE

PICTYPE_ICON

The following steps illustrate how to create an HPIC and place a picture in a VBX control. This illustration uses a bitmap, that is assumed to be in a resource identified by IDBM_MY_PICTURE.

Create a picture that is a bitmap, metafile, or icon.

HBITMAP hBmp = LoadBitmap(IDBM_MY_PICTURE);

Place the handle in the appropriate portion of the structure and indicate the type of the picture.

PIC picture;

picture.picData.bmp.hbitmap = hBmp;

picture.picType = PICTYPE_BITMAP;

Call AfxSetPict to create the HPIC.

HPIC hPic = AfxSetPict(NULL, &picture);

Passing NULL for the first parameter causes a new HPIC to be created. (Pass a valid HPIC as the first parameter to place the picture into an existing HPIC.)  AfxSetPict sets an initial reference count on the picture. See the end of this section for a complete description of AfxSetPict.

Place the picture in a VBX control that has a picture property:

// pVBXControl points to a CVBControl that has a Picture property

pVBXControl->SetPictureProperty("Picture", hPic);

AfxReferencePict(hPic, FALSE);	// get rid of extra reference

AfxSetPict passes ownership of the GDI object (hBmp) to the framework's internal manager of picture properties. You should not delete the GDI object, nor keep references to it.

If you intend to hold an HPIC value in anything other than a temporary location, then you should increment the reference count. For example:

HPIC hPic = pVBXControl->GetPictureProperty("Picture");

AfxReferencePict(hPic, TRUE);	// increment the reference count

When you no longer need to refer to the HPIC, unreference it. For example:

AfxReferencePict(hPic, FALSE);	// decrement the reference count

See the end of this section for a complete description of AfxReferencePict.

AfxSetPict -- Function Description

HPIC AFXAPI AfxSetPict(HPIC hPic, const PIC FAR* lpPic);

hPic	Picture to set. Any data previously contained will be deleted. If hPic is NULL, a new HPIC is created.

lpPic	Points to a PIC structure containing the new picture value. If lpPic is NULL, the picture will be cleared.

If both hPic and lpPic are NULL, a new, empty HPIC will be created.

If a new HPIC is created, the HPIC returned will have a reference count of 1. If hPic was not NULL, the reference count will be unchanged. 	Note that the lpPic->nRefCount is not transfered to the hPic.

NOTE:  Since HPICs are shared, changing the data will affect all uses of the HPIC.

AfxGetPict -- Function Description

void AFXAPI AfxGetPict(HPIC hPic, LPPIC lpPic);

hPic	HPIC that you want to retrieve the data from.

lpPic	Pointer to the PIC structure that will receive the data.

AFXGetPict fills the PIC structure pointed to by lpPic with the picture information represented by the hPic.

AfxReferencePict -- Function Description

void AFXAPI AfxReferencePict(HPIC hPic, BOOL bReference);

hPic	HPIC whose reference count is to be changed.

bReference	TRUE to increment the reference count. FALSE to decrement the reference count and to destroy the HPIC if the count drops to 0.

AfxReferencePict destroy the picture if the reference count decrements to zero. Once the picture is destroyed, any reference to the HPIC will cause a program error.


Differences Between Visual Basic VBX Support and MFC VBX Support

MFC 2.0 emulates portions of Visual Basic to enable MFC applications to use Visual Basic Custom Controls. MFC 2.0 is designed to be compatible with the Visual Basic version 1.0 API set. MFC does not provide support for any Visual Basic version 2.0 APIs except for the VBGetVersion call.

This section of Technical Note 27 focuses on the differences between the Visual Basic 1.0 and the MFC 2.0 environments for Visual Basic Custom (VBX) Controls. This information is particularly important to developers of VBX controls, but may also be useful to users of VBX controls in the MFC 2.0 environment.

A great deal of care has been taken to ensure a high degree of compatibility between the two environments, and most controls that do not rely on internal or undocumented features of Visual Basic should run without problem within an MFC application.

There are some differences in the run-time and design-time environments for VBX controls.  VBX controls that rely on run-time environment features where there are such differences will behave in undefined, and possibly undesirable, ways. Therefore, you should become familiar with these differences ideally before you use VBX controls. Typically, however, you can use a VBX control without running into a problems related to the differences between the MFC and Visual Basic run-time environments. You might prefer to become familiar with the differences described below only if you need help in diagnosing unexpected VBX control behavior or run-time errors.

Differences between the MFC and Visual Basic run-time environments fall into the following categories:

 	No Visual Basic Forms

 	Standard Methods Supported by MFC

 	Standard Property Differences

 	Event Routing

 	PF_fNoRuntimeW Property Flag

 	No Drag/Drop Support

 	No Control Arrays

 	Manual Allocation of Heaps

 	DT_XPOS, DT_YPOS, DT_XSIZE, and DT_YSIZE property types

 	Default Font

 	SetErrorMode setting different

 	Undocumented APIs

No Visual Basic Forms

A VBX control used in an MFC Application may be contained in any window. Only VBX controls will have HCTLs. Therefore, the parent of a control does NOT necessarily have an HCTL, and VBGetHwndControl::GetParent(hWndCtrl) may very well return NULL. Within Visual Basic, you are guaranteed that the parent of a control will have an HCTL. This also means that it is not possible to query the properties of a Form, since they do not exist.

Within Visual Basic, a VBX control can be a container for other controls. The Windows Dialog Manager does not allow child controls to be created with nested child controls in an automatic fashion. However, by manually creating controls in MFC, they may be made children of any window. Therefore, with respect to container relationships, the MFC environment is both more and less constrained than Visual Basic.

Standard Methods Supported by MFC

MFC emulates the standard Visual Basic control methods listed below. MFC does not emulate any of the other Visual Basic control methods, such as Drag, PrintForm, and SetFocus.

Method	Description
AddItem	Adds items to a list managed by a list box or combo box control.
Move	Moves a control to a specified location and resizes the control at the same time.
Refresh	Updates a control to reflect changes that have been made to the control or to the environment.
RemoveItem	Removes an item from a list managed by a control.
Standard Property Differences

Some of the Standard Properties (those properties for which Visual Basic provides the implementations) are stored in different forms or have slightly different meanings under MFC.

 CtlName--This property is not treated specially by the MFC application. It is essentially a second Tag string under MFC.

 Index--This property is not implemented and always returns 0.

 DragMode, DragIcon--These properties do nothing.

 Left, Top, Width, Height--These properties are stored in the Window rect and are the same values one would obtain from the following code executed for a CWnd-derived object:

CRect rect;

GetWindowRect(&rect);

GetParent()->ScreenToClient(&rect);

Most notably, the values are always in pixels (since the container does not have the VB Form property ScaleMode), and changes of less than a pixel (for example, TWIPS) are not possible.

Event Routing

Within Visual Basic, events are always routed to the Form containing the control, rather than to the control's immediate parent. Thus a Form may contain controls that contain other controls and yet receive events from all levels of the window tree below it.

In MFC, the events are sent directly to the parent of the control. Events are routed the same way as commands, so if the parent is a CFrameWnd or CView, standard command routing will allow the Event to be caught by the frame, view, document, coc template, or the application. Normally the parent of a VBX control is a dialog (a CDialog-derived class) in which case VBX controls will notify the parent dialog, and can be handled with message map entries in the CDialog-derived class.

PF_fNoRuntimeW Property Flag

Within Visual Basic, it is not possible (at run time) to create a control without having a template of initialized property values. This template is referred to as a Form File. In MFC, however, you may create VBX controls arbitrarily at run time. In such cases, it is necessary to be able to write to all properties of a control. Some controls behave poorly when properties flagged as NoRuntimeWrite are changed at runtime, others take the correct actions. Under _DEBUG builds, a TRACE warning will be emitted when one of these properties is changed. Check such cases carefully to be sure that unintended behavior does not occur. Some controls may actually cause this message to be emitted more than once for a single property set.

No Drag/Drop Support

The Drag/Drop paradigm present in Visual Basic is not supported in MFC 2.0. Therefore, anything having to do with Drag and Drop support is not used under MFC. DragMode and DragIcon are simply placeholders and the Standard DragDrop and DragOver Events will never be generated. The Drag Method is not implemented.

No Control Arrays

Control arrays in Visual Basic are sets of controls that share the same CtlName property and have Index numbers assigned by Visual Basic. Events generated by these controls also share common event handlers in the Form and are identified to the event handlers by their Index values.

Within an MFC application, controls are identified by their HWND values (and their associated CVBControl objects). Since these are always unique, the concept of a Visual Basic control array is not supported under MFC.

Manual Allocation of Heaps

Within Visual Basic, each Form owns a heap (up to 64K in size) managed by the Windows local heap manager. All controls in a form share this heap for the storage of their custom property structures, and often HSZ type strings are allocated here. This heap is identified by the selector (HIWORD) of the HCTL. Each Form that is created has its own heap automatically. The VBX controls also share this heap with any Form Variables created within the Visual Basic program subroutines defined in that Form.

Within MFC, the allocation of these heaps is managed by the application programmer. By default, all VBX controls in an application share the same heap (although this heap may change if all VBX controls are deleted and a new VBX control is then created.)

This default behavior will be sufficient for most VBX control situations. However, you may signal to stop using the current heap before you create new VBX controls. The CVBControl::BeginNewVBHeap static member function signals the emulation system to create a new heap when the next VBX control is allocated. There is no way to reactivate a heap once this function has been called.

Be aware that only VBX control allocations are reference counted against the heap. When all of the VBX controls that were allocated in a heap are destroyed, the heap is destroyed. Therefore, any other allocations in that heap (via VBCreateHSZ for instance) are only valid as long as there is a VBX control still alive in that heap.

DT_XPOS, DT_YPOS, DT_XSIZE, and DT_YSIZE Property Types

Properties of these types are stored in the custom property structure as TWIPS. MFC is here the same as Visual Basic.

In Visual Basic, however, these values are converted into the ScaleMode of their container when read either by a VB program or by calling a VBGetControlProperty. They are converted from that coordinate system to TWIPS when set by either of those mechanisms.

In MFC, the ScaleMode is always assumed to be pixels. Thus, any time these values are assigned or retrieved via the Set/Get property mechanisms, their values will be in pixels. The Windows rect properties are a special case of this as described above.

Default Font

In Visual Basic, the default Font for all controls is Helv 8. This is the font that all newly created controls have in the Design Mode Form Editor.

In MFC, when a control is created without a form file, or when it is initially created in App Studio, the System Font is the default. Depending on your display, this may cause some controls to be improperly sized when first created, and some text may be clipped.

SetErrorMode Setting Different

In MFC, during execution of the LibMain routine, the Windows Error Mode has the SEM_NOOPENFILEERRORBOX flag set (in addition to any other flags set by the application programmer). This is opposite the setting in Visual Basic. After this call completes, the Error Mode is set back to that defined by the application programmer. Since the value of this flag is determined by each application, a control must not make any assumptions about its value except for that one circumstance.

VBSetErrorMessage

The VBSetErrorMessage routine does not do anything with the string passed. This is invisible to the VBX control. The MFC application has no way to use this string.

Undocumented APIs

There are some entry points in the Visual Basic version 1.0 API that provide undocumented hooks into Visual Basic. To our knowledge, no commercial VBX control uses these undocumented APIs. It is outside of the MFC mission to try to emulate these unnamed, undocumented functions. In other words, "If you use them, your control won't work with MFC."


Context-Sensitive Help Support
Technical Note 28: Context-Sensitive Help Support

This note describes the rules for assigning Help contexts IDs (that is, topic numbers) and other help issues in MFC 2.0. Context sensitive help support requires the help compiler which is available in Visual C++ Professional.

Types of Help Supported

There are two types of context-sensitive help implemented in Windows applications. The first, referred to as "F1 Help" involves launching WinHelp with the appropriate context based on the currently active object. The second is SHIFT+F1 mode. In this mode, the mouse cursor changes to the help cursor (a combination arrow + question mark), and the user proceeds to click on the object they'd like help on. At that point, WinHelp is launched giving help for the object on which the user clicked.

The Microsoft Foundation classes implement both of these forms of help. In addition, the framework supports two simple help commands, Help Index and Using Help.

Help Files

The Microsoft Foundation classes assume a single Help file. That Help file must have the same name and path as the application (.EXE -> .HLP).

This is a public CWinApp member variable named m_pszHelpFilePath that the user can change if desired.

Help Context Ranges

0x00000000 - 0x0000FFFF : user defined

0x00010000 - 0x0001FFFF : commands (menus/command buttons)

0x00010000 + ID_

(note: 0x18000-> 0x1FFFF is practical range since command IDs are >=0x8000)

0x00020000 - 0x0002FFFF : windows and dialogs

0x00020000 + IDR_

(note: 0x20000-> 0x27FFF is practical range since IDRs are <= 0x7FFF)

0x00030000 - 0x0003FFFF : error messages (based on error string ID)

0x00030000 + IDP_

0x00040000 - 0x0004FFFF : special purpose (non-client areas)

0x00040000 + HitTest area

0x00050000 - 0x0005FFFF : controls (those that are not commands)

0x00040000 + IDW_

These rules are hard-coded into the default implementation of the Microsoft Foundation classes. They can be overridden by providing different implementations of the various Help-related member functions.

Simple "Help" Commands

There are two simple Help commands that are implemented by the Microsoft Foundation classes:

ID_HELP_INDEX which is implemented by CWinApp::OnHelpIndex

ID_HELP_USING which is implemented by CWinApp::OnHelpUsing

These two commands simply show the Help index for the application and show the user help on using the WinHelp program, respectively.

Context-Sensitive Help (F1 Help)

This is the first form of context-sensitive Help (usually referred to as F1 Help). The user presses F1 to get help on the task at hand (the active window or menu item). No special "help mode" is involved.

The F1 key is usually translated to a command with an ID of ID_HELP by an accelerator placed into the main window's accelerator table. The ID_HELP command may also be generated by a button with an ID of ID_HELP on the main window or dialog box. Also, when a menu or a dialog box is active and the user presses F1, the keystroke is hard-coded to translate into an ID_HELP command.

However the ID_HELP command is generated, it is routed as a normal command until it reaches a command handler (for more information on the Microsoft Foundation classes command-routing architecture, refer to Technical Note 21.)  If the application is Help enabled, the ID_HELP command will be handled by the CWinApp::OnHelp function. Since the default command routing is not adequate for determining the most specific context the command is instead always routed to the application object and then undergoes custom routing for Help.

CWinApp::OnHelp attempts to launch WinHelp in the following order:

Checks for an active AfxMessageBox call with a Help ID. If a message box is currently active, WinHelp is launched with the context appropriate to that message box.

If no message box is active, CWinApp::OnHelp sends a WM_COMMANDHELP (a message private to the Microsoft Foundation classes) to the active window. If that window does not respond by launching WinHelp, the same message is then sent to the parent of that window, until the message is processed or the current window is a top-level window (and therefore does not have a parent window).

If the message remains unprocessed, then the default Help is invoked. This is done by sending a ID_DEFAULT_HELP command to the main window. This command is generally mapped to CWinApp::OnHelpIndex.

To globally override the ID base values (0x10000 for commands, 0x20000 for resources such as dialogs, and so on), the application should override CWinApp::WinHelp. This is in fact done in the implementation of the ClassWizard and AppWizard applications themselves, for example, since both share a single Help file.

To override this functionality and the way that a Help context is determined, an application should handle the WM_COMMANDHELP message (see below). You may wish to provide more specific Help routing than the framework provides, as it only goes as deep as the current MDI child window. Or you may wish to provide more specific help for a particular window or dialog -- perhaps based on the current internal state of that object or the active control within the dialog.

WM_COMMANDHELP

afx_msg LRESULT CWnd::OnCommandHelp(WPARAM, LPARAM lParam)

WM_COMMANDHELP is an AFX private Windows message that is received by the active window when Help is requested. When the window receives this message, it may call CWinApp::WinHelp with context that matches the window's internal state.

lParam contains the currently available Help context. lParam is zero if no Help context has been determined yet. An implementation of OnCommandHelp can use the context ID in lParam to determine a "better" context or can just pass it to CWinApp::WinHelp.

wParam is not used and will be zero.

If the OnCommandHelp function calls CWinApp::WinHelp, it should return TRUE. Returning TRUE stops the routing of this command to other classes (base classes) and to other windows.

Help Mode (Shift+F1 Help)

This is the second form of context-sensitive Help. Generally, this mode is entered by pressing SHIFT+F1 or via the menu/toolbar. It is implemented as a command (ID_CONTEXT_HELP). The message filter hook is not used to translate this command while a modal dialog box or menu is active, therefore this command is only available to the user when the application is executing the main message pump (CWinApp::Run).

After entering this mode, the Help mouse cursor is displayed over all areas of the application, even if the application would normally display it's own cursor for that area (such as the sizing border around the window). The user is able to use the mouse or keyboard to select a command. Instead of executing the command, Help on that command is displayed. Also the user can click a visible object on the screen, such as a button on the toolbar, and Help will be displayed for that object. This mode of Help is provided by CWinApp::OnContextHelp.

During the execution of this loop, all keyboard input is inactive, except for keys that access the menu. Also, command translation is still performed via PreTranslateMessage to allow the user to press an accelerator key and receive help on that command.

If there are particular translations or actions taking place in the PreTranslateMessage function that shouldn't take place during SHIFT+F1 Help mode, you should check the m_bHelpMode member of CWinApp before performing those operations. The CDialog implementation of PreTranslateMessage checks this before calling IsDialogMessage, for example. This disables "dialog navigation" keys on modeless dialogs during SHIFT+F1 mode. In addition, CWinApp::OnIdle is still called during this loop.

If the user chooses a command from the menu, it is handled as help on that command (through WM_COMMANDHELP, see below). If the user clicks a visible area of the applications window, a determination is made as to whether it is a nonclient click or a client click. OnContextHelp handles mapping of nonclient clicks to client clicks automatically. If it is a client click, it then sends a WM_HELPHITTEST to the window that was clicked. If that window returns a nonzero value, that value is used as the context for help. If it returns zero, OnContextHelp tries the parent window (and failing that, its parent, and so on). If a Help context cannot be determined, the default is to send a ID_DEFAULT_HELP command to the main window, which is then (usually) mapped to CWinApp::OnHelpIndex.

WM_HELPHITTEST

afx_msg LRESULT CWnd::OnHelpHitTest(WPARAM, LPARAM lParam)

WM_HELPHITTEST is an AFX private windows message, that is received by the active window clicked during SHIFT+F1 Help mode. When the Window receives this message, it returns a DWORD Help ID for use by WinHelp.

LOWORD(lParam) contains the X-axis device coordinate where the mouse was clicked relative to the client area of the window.

HIWORD(lParam) contains the Y-axis coordinate. wParam is not used and will be zero.

If the return value is nonzero, WinHelp is called with that context. If the return value is zero, the parent window is queried for help.

See the implementation of CToolBar::OnHelpHitTest for an example of handling the WM_HELPHITTEST message.

MFC AppWizard Support and MAKEHM

AppWizard creates the necessary files to build a Help file (makehelp.bat, project.hpj, and so on). It also includes a number of prebuilt .RTF files that are accepted by the Microsoft Help Compiler. Many of the topics are complete, but some may need to be modified for your specific application.

Automatic creation of a "help mapping" file is supported by a utility called "makehm". The makehm utility can translate an application's resource.h file to a Help mapping file. For example:

#define IDD_MY_DIALOG   2000

#define ID_MY_COMMAND   150

will be translated into:

HIDD_MY_DIALOG    0x207d0

HID_MY_COMMAND    0x10096

This format is compatible with the Help compiler's facility, which maps context IDs (the numbers on the right side) with topic names (the symbols on the left side).

The MAKEHELP.BAT uses the makehm utility as part of the standard Help build process. The makehm source code is available in the \MSVCNT\MFC\SAMPLES directory.

Adding Help Support after Running AppWizard

The best way to add Help to your application is to check the "Context Sensitive Help" option in AppWizard's Options dialog before creating your application. That way AppWizard automatically adds the necessary message map entries to your CWinApp-derived class to support Help.

If you have already created your application without Help support and now wish to add it, see Chapter 10 of the Class Library User's Guide.

Help on Message Boxes

Help on Message Boxes (sometimes called alerts) is supported through the AfxMessageBox function, a wrapper for the MessageBox Windows API.

There are two versions of AfxMessageBox, one for use with a string ID and another for use with a pointer to string (LPCSTR):

int AFXAPI AfxMessageBox(LPCSTR lpszText, UINT nType, UINT nIDHelp);

int AFXAPI AfxMessageBox(UINT nIDPrompt, UINT nType, UINT nIDHelp);

In both cases, there is an optional Help ID.

In the first case, the default for nIDHelp is 0, which indicates no Help for this message box. If the user presses F1 while such as message box is active, the user will not receive Help (even if the application supports Help). If this is not desirable, a Help ID should be provided for nIDHelp.

In the second case, the default value for nIDHelp is -1, which indicates the Help ID is the same as nIDPrompt. Help will work only if the application is Help-enabled, of course). You should provide 0 for nIDHelp if you wish that the message box have no help support. Should you want the message to be Help enabled, but desire a different help ID than nIDPrompt, simply provide a positive value for nIDHelp different from that of nIDPrompt.


Splitter Windows
Technical Note 29: Splitter Windows

This note describes the AFX CSplitterWnd class, which is used to provide window splits and to manage the resizing of other Pane windows.

NOTE: please be sure to read Technical Note 20 regarding use of IDs and valid ID ranges.

Splitter Styles

A CSplitterWnd supports two different styles of splitting windows.

In "Static splitters," the panes are created when the splitter window is created, and the order and number of panes never change. Splitter bars are used to resize the different panes, and the different panes are usually of different view classes. The AppStudio graphics editor and the Windows File Manager are examples of programs that use this splitter style. Splitter boxes are not used by this style of splitter.

In "Dynamic splitters," additional panes are created and destroyed as the user splits and un-splits new views. This splitter starts out with a single view, and splitter boxes are provided to initiate splitting. If the view is split in one direction, an additional view object is dynamically created to represent the new pane. If the view is split in two directions (possible with the keyboard interface), then three new views are created to represent the three new panes. When the split is active, the splitter box is drawn as a splitter bar between the panes. Additional view objects are destroyed when the user removes a split, but the original view (row 0, column 0) remains until the splitter window itself is destroyed. Microsoft Excel or Microsoft Word are examples of the dynamic splitter style.

When creating either kind of splitter window, you must specify the maximum number of rows and columns that the splitter will manage. For a static splitter, panes must be created to fill all the rows and columns. For a dynamic splitter, the first pane is automatically created when the CSplitterWnd is created.

The following lists the limits on the maximum number of panes you can specify, and recommended configurations:

Static Splitters:

maximum limit: 16 rows x 16 columns

common configurations:

1 row x 2 columns : usually with dissimilar panes

2 rows x 1 column : usually with dissimilar panes

2 rows x 2 columns : usually with similar panes

Dynamic Splitters:

maximum limit: 2 rows x 2 columns

common configurations:

1 row x 2 columns : for columnar data

2 rows x 1 column : for textual or other data

2 rows x 2 columns : for grid or table oriented data

Splitter Examples

Many of the MFC sample programs use splitter windows directly or indirectly. Step 4 of the SCRIBBLE tutorial adds a dynamic splitter window to split the CScribView into multiple panes. As shown in this step, adding a new splitter frame window is very easy with the Add Class feature of ClassWizard. This example is a dynamic splitter window by default.

The VIEWEX sample illustrates several uses of static splitters, including how to place a splitter in a splitter.

ClassWizard will also create a new multiple document interface (MDI) Child frame window class which contains a splitter window. Please refer to chapter 8 of the Class Library User's Guide.

Terminology used by Implementation

Terminology of the parts of a CSplitterWnd and related objects.

CSplitterWnd:
This is a window that provides pane-splitting controls and scroll bars that are shared between all panes on a row or column. Rows and columns are specified with zero-based numbers [that is, the first pane is row = 0 and column = 0]

Pane:
An application-specific window that is managed by a CSplitterWnd. A pane is usually a CView-derived object, but in fact can be any CWnd object that has the appropriate child window ID.

Splitter Bar:
A control that is placed between rows and columns of panes. It may be used to adjust the sizes of rows or columns of Panes.

Splitter Box:
A small control at the top of the vertical scroll bars or to the left of the horizontal scroll bars in a dynamic CSplitterWnd. Used to create new rows or columns of panes.

Splitter Intersection:
The intersection of a vertical splitter bar and a horizontal splitter bar. May be dragged to adjust the size of a row and column of panes simultaneously.

Shared Scroll Bars

The CSplitterWnd class also supports shared scroll bars. These scroll bar controls are children of the CSplitterWnd and are shared with the different panes in the splitter.

For example, in a 1 row x 2 column window, you can specify WS_VSCROLL when creating the CSplitterWnd. A special scroll bar control will be created that is shared between the two panes.

[      ][      ][^]

[pane00][pane01][|]

[      ][      ][v]

When the user moves the scroll bar, WM_VSCROLL messages will be sent to both views. When the views set the scroll bar position, the shared scroll bar will be set.

Note that shared scroll bars are most useful with dynamic or static splits, splitting similar view objects. If you mix views of different types in a splitter, then you may have to write special code to coordinate their scroll positions. Any CView-derived class that uses the CWnd scroll bar APIs will delegate to the shared scroll bar if it exists. The CScrollView implementation is one such example of a CView class that supports shared scroll bars. Non-CView derived classes, classes that rely on noncontrol scroll bars, or classes that use standard Windows implementations (for example, CEditView) will not work with the shared scroll bar feature of CSplitterWnd.

Minimum Sizes

For each row there is a minimum row height, and similarly for each column there is a minimum column width. This minimum is used to decide if the pane is to small to be shown in complete detail.

For a static splitter window, the initial minimum row height and column width is 0. For a dynamic splitter window, the initial minimum row height and column width are set by the sizeMin parameter to the CSplitterWnd::Create function.

These minimum sizes can be changed with the SetRowInfo and SetColumnInfo APIs.

Actual vs. Ideal Sizes

The layout of the panes in the splitter window depends on the size of the containing frame (which in turn resizes the CSplitterWnd. CSplitterWnd repositions and resizes the panes so that they fit as ideally as possible).

The row height and column width sizes set by the user, or through the CSplitterWnd API, represent the ideal size. The actual size can be smaller than that ideal size (if there is not enough room to make that pane the ideal size) or larger than the ideal size (if that pane must be made larger to fill the left-over space on the right or bottom of the splitter window).

Protected Interface

The following describes some of the splitter window implementation overridables that can be used by advanced users of CSplitterWnd to customize the features and user interface of this class. These APIs are officially undocumented and are subject to change in future versions of MFC. Please refer to the implementation source code for more details on these implementation APIs.

Drawing the splitter bars, boxes and trackers:

enum ESplitType { splitBox, splitBar, splitIntersection };

virtual void OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rect);

virtual void OnInvertTracker(const CRect& rect);

These virtual function can be overridden to provide alternate imagery for the various graphical components of a splitter window. The default imagery is similar to the splitter in Microsoft Works for Windows: only intersections of splitter bars are blended together.

Creating controls and views:

virtual BOOL CreateScrollBarCtrl(DWORD dwStyle, UINT nID);

This is called to create a shared scroll bar control. It can be overridden to include extra controls next to a scroll bar. The default behavior is to just create normal Windows scroll bar controls.

virtual void DeleteView(int row, int col);

virtual BOOL SplitRow(int cyBefore);

virtual BOOL SplitColumn(int cxBefore);

virtual void DeleteRow(int row);

virtual void DeleteColumn(int row);

These functions are called to implement the logic of the dynamic splitter window (that is, if the splitter window has the SPLS_DYNAMIC_SPLIT style). They can be customized, along with the virtual function CreateView, to implement more advanced dynamic splitters.

The following are high level commands that are used by the CView class to delegate to the CSplitterWnd implementation. They are virtual so that the standard CView implementation will not require the entire CSplitterWnd implementation to be linked in. For applications that use CView but not CSplitterWnd, the CSplitterWnd implementation will not be linked with the application.

virtual BOOL CanActivateNext(BOOL bPrev = FALSE);

Checks to see if the "Next Pane" or "Previous Pane" command is currently possible.

virtual void ActivateNext(BOOL bPrev = FALSE);\

Performs the "Next Pane" or "Previous Pane" command.

virtual BOOL DoKeyboardSplit();

Performs the keyboard split command, usually "Window Split".


Print Preview
Technical Note 30: Customizing Printing and Print Preview

This note describes the process of customizing printing and print preview and describes the purposes of the callback routines used in CView and the callback routines and member functions of CPreviewView.

The Problem

MFC provides a complete solution for most printing and print preview needs. In most cases, little additional code is required to have a view able to print and preview. However, there are ways to optimize printing that require significant effort on the part of the developer, and some applications need to add UI to the print preview mode.

Efficient Printing

When an MFC application prints using the standard methods, Windows directs all Graphical Device Interface (GDI) output calls to an in-memory metafile. When EndPage is called, Windows plays the metafile once for each physical band that the printer requires to print one page. During this rendering, GDI frequently queries the Abort Procedure to determine if it should continue. Typically the Abort Procedure allows messages to be processed so that the user may abort the print job using a printing dialog.

Unfortunately, this can slow the printing process. If the printing in your application must be faster than can be achieved using the standard technique, you must implement manual banding.

Print Banding

In order to manually band, you must re implement the print loop such that OnPrint is called multiple times per page (once per band). The print loop is implemented in the OnFilePrint function in viewprnt.cpp. In your CView-derived class, you overload this function so that the message map entry for handling the print command calls your print function. Copy the OnFilePrint routine and change the print loop to implement banding. You will probably also want to pass the banding rectangle to your printing functions so that you can optimize drawing based on the section of the page being printed.

Second, you must frequently call QueryAbort while drawing the band. Otherwise, the Abort Procedure will not get called and the user will be unable to cancel the print job.

Print Preview:  Electronic Paper with UI

Print Preview, in essence, tries to turn the display into an emulation of a printer. By default, the client area of the main window is used to display one or two pages fully within the window. The user is able to zoom in on an area of the page to see it in more detail. With additional support, the user may even be allowed to edit the document in preview mode.

Customizing Print Preview

This note only deals with one aspect of modifying print preview:  Adding UI to preview mode. Other modifications are possible, but such changes are out of the scope of this discussion.

Briefly, the process of adding UI to the preview mode is to:

1. Derive a view class from CPreviewView.

2. Add command handlers for the UI aspects you desire.

3. If you are adding visual aspects to the display, override OnDraw and perform your drawing after calling CPreviewView::OnDraw.

OnFilePrintPreview

This is the command handler for print preview. Its default implementation is

void CView::OnFilePrintPreview()

{

// In derived classes, implement special window handling here

// Be sure to Unhook Frame Window close if hooked.

// must not create this on the frame. Must outlive this function

CPrintPreviewState* pState = new CPrintPreviewState;

if (!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this,

RUNTIME_CLASS(CPreviewView), pState))

{

// In derived classes, reverse special window handling

// here for Preview failure case

TRACE0("Error: DoPrintPreview failed");

AfxMessageBox(AFX_IDP_COMMAND_FAILURE);

delete pState;      // preview failed to initialize,

// delete State now

}

}

DoPrintPreview will hide the main pane of the application. Control Bars, such as the status bar, can be retained by specifying them in the pState->dwStates member (This is a bit mask and the bits for individual control bars are defined by AFX_CONTROLBAR_MASK( AFX_IDW_FOO)). The window pState->nIDMainPane is the window that will be automatically hidden and reshown. DoPrintPreview will then create a button bar for the standard Preview UI. If special window handling is needed, such as to hide or show other windows, that should be done before DoPrintPreview is called.

By default, when print preview finishes, it returns the control bars to their original states and the main pane to visible. If special handling is needed, it should be done in an override of EndPrintPreview. If DoPrintPreview fails, also provide special handling.

DoPrintPreview is called with:

 The Resource ID of the dialog template for the preview toolbar.

 A pointer to the view to perform the printing for the print preview

 The run-time class of the Preview View class. This will be dynamically created in DoPrintPreview.

 The CPrintPreviewState pointer. Note that the CPrintPreviewState structure (or the derived structure if the application needs more state preserved) must not be created on the frame. DoPrintPreview is modeless and this structure must survive until EndPrintPreview is called.

Note that if a separate view or view class is needed for printing support, a pointer to that object should be passed as the second parameter.

EndPrintPreview

This is called to terminate the print preview mode. It is often desirable to move to the page in the document that was last displayed in print preview. EndPrintPreview is the application's chance to do that. The pInfo->m_nCurPage member is the page that was last displayed (leftmost if two pages were displayed), and the pointer is a hint as to where on the page the user was interested. Since the structure of the application's view is unknown to the framework, the you must provide the code to move to the chosen point.

You should perform most actions before calling CView::EndPrintPreview. This call reverses the effects of DoPrintPreview and deletes pView, pDC, and pInfo.

// Any further cleanup should be done here.

CView::EndPrintPreview(pDC, pInfo, point, pView);

CWinApp::OnFilePrintSetup

This must be mapped for the Print Setup menu item. In most cases, it is not necessary to override the implementation.

Page Nomenclature

Another issue is that of page numbering and order. For simple word processor type applications, this is a straightforward issue. Most print preview systems assume that each printed page corresponds to one page in the document.

In trying to provide a generalized solution, there are several things to consider. Imagine a CAD system. The user has a drawing that covers several E-size sheets. On an E-size (or a smaller, scaled) plotter, page numbering would be as in the simple case. But on a laser printer, printing 16 A-size pages per sheet, what does print preview consider a "page"?

As the introductory paragraph states, Print Preview is acting like a printer. Therefore, the user will see what would come out of the particular printer that is selected. It is up to the view to determine what image is printed on each page.

The page description string in the CPrintInfo structure provides a means of displaying the page number to the user if it can be represented as one number per page (as in "Page 1" or "Pages 1-2"). This string is used by the default implementation of CPreviewView::OnDisplayPageNumber. If a different display is needed, one may override this virtual function to provide, for example, "Sheet1, Sections A, B".


Control Bars
Technical Note 31: Control Bars

This note describes the control bar classes in MFC: the general CControlBar, CStatusBar, CToolBar and CDialogBar.

CControlBar

A ControlBar is a CWnd-derived class that:

 Is aligned to the top or bottom of a frame window.

 May contain child items that are either HWND-based controls (for example, CDialogBar) or non-HWND based items (for example, CToolBar, CStatusBar).

Control bars support the additional styles:

CBRS_TOP	(the default) pin the control bar to the top

CBRS_BOTTOM	pin the control bar to the bottom

CBRS_NOALIGN	don't reposition the control bar when the parent resizes

Classes derived from CControlBar provide more interesting implementations:

CStatusBar	a status bar, items are status bar panes containing text

CToolBar	a toolbar, items are bitmap buttons aligned in a row

CDialogBar	a toolbar-like frame containing standard windows controls
(created from a dialog template resource)

All control bar objects/windows will be child windows of some parent frame window. They are usually added as a sibling to the client area of the frame (for example, an MDI Client or view). The child window ID of a control bar is important. The default layout of control bar only works for control bars with IDs in the range of AFX_IDW_CONTROLBAR_FIRST to AFX_IDW_CONTROLBAR_LAST. Note that even though there is a range of 256 control bar IDs, the first 32 of these control bar IDs are special since they are directly supported by the print preview architecture.

The CControlBar class gives standard implementation for

 Aligning the control bar to the top or bottom of the frame.

 Allocating of control item arrays.

 Supporting for implementation of derived classes.

C++ control bar objects will usually be embedded as members of a CFrameWnd derived class, and will be cleaned up when the parent HWND and object are destroyed. If you need to allocate a control bar object on the heap, you can simply set the m_bAutoDestruct member to TRUE to make the control bar "delete this" when the HWND is destroyed.

Control Bar Layout Algorithm

The control bar layout algorithm is very simple. The frame window sends a message WM_SIZEPARENT to all children in the control bar range. Along with this message, a pointer to the parent's client rectangle is passed. This message is sent to children in Z-order. The control-bar children use this information to position themselves and to decrease the size of the parent's client area. The final rectangle that is left for the normal client area (less control bars) is used to position the main client window (usually an MDI client, view or splitter window).

See CWnd::RepositionBars and CFrameWnd::RecalcLayout for more details.

(AFX) private Windows messages, including WM_SIZEPARENT, are documented in Technical Note 24.

CStatusBar

A status bar is a control bar which has a row of text output panes. There are two common ways to use text output panes:

 as a message line

(for example, the standard menu help message line). These are usually accessed by a 0-based indexed

 as status indicators

(for example, the CAP, NUM and SCRL indicators). These are usually accessed by string/command ID.

The font for the status bar is 10 point "MS Sans Serif" (dictated by the Windows Interface Application Design Guide or the font mappers best match of a 10-point Swiss proportional font).

The colors used in the status bar are also consistent with the recomendation of the Windows Interface Application Design Guide. These colors are not hard coded and are changed dynamically in response to user customization via the Control Panel.

Item	Windows COLOR value	Default RGB
Status bar background	COLOR_BTNFACE	RGB(192, 192, 192)
Status bar text	COLOR_BTNTEXT	RGB(000, 000, 000)
Status bar top/left edges	COLOR_BTNHIGHLIGHT	RGB(255, 255, 255)
Status bar bot/right edges	COLOR_BTHSHADOW	RGB(128, 128, 128)
CCmdUI Support for CStatusBar

The way indicators are usually updated is through the ON_UPDATE_COMMAND_UI mechanism. On idle time, the status bar will call the ON_UPDATE_COMMAND_UI handler with the string ID of the indicator pane.

The ON_UPDATE_COMMAND_UI handler can call:

Enable:  to enable or disable the pane. A disabled pane looks exactly like an enabled pane but the text is invisible (that is, turns off the text indicator).

SetText:  to change the text. Be careful if you use this since the pane will not automatically resize.

Please refer to the Class Library Reference for details about CStatusBar creation and customization APIs. Most customization of status bars should be done before the status bar is initially made visible.

The status bar supports only one stretchy pane, usually the first pane. The size of that pane is really a minimum size. If the status bar is bigger than the minimum size of all the panes, any extra width will be given to the stretchy pane. The default application with a status bar has right-aligned indicators for CAP, NUM and SCRL since the first pane is stretchy.

CToolBar

A toolbar is a control bar with a row of bitmap buttons that may include separators. Two styles of buttons are supported: pushbuttons and check  box buttons. Radio group functionality can be built with check box buttons and ON_UPDATE_COMMAND_UI.

All the bitmap buttons in the toolbar are taken from one bitmap. This bitmap must contain one image or glyph for each button. Typically the order of the images/glyphs in the bitmap is the same order they will be drawn on the screen. (This can be changed using the customization APIs.)

Each button must be the same size. The default is the standard 24x22 pixels. Each image/glyph must be the same size and must be side-by-side in the bitmap. The default image/glyph size is 16x15 pixels. Therefore, for a toolbar with 10 buttons (using standard sizes), you need a bitmap that is 160 pixels wide and 15 pixels high.

Each button has one and only one image/glyph. The different button states and styles (for example, pressed, up, down, disabled, disabled down, indeterminate) are algorithmically generated from that one image/glyph. Any color bitmap or DIB can be used in theory. The algorithm for generating the different button states works best if the original image is shades of gray. Look at the standard toolbar buttons and the toolbar button clipart provided with App Studio for examples.

The colors used in the toolbar are also consistent with the recommendation of the Windows Interface Application Design Guide. These colors are not hard coded and are changed dynamically in response to user customization via the Control Panel.

Item	Windows COLOR value	Default RGB
ToolBar background	COLOR_BTNFACE	RGB(192,192,192)
ToolBar buttons top/left edges	COLOR_BTNHIGHLIGHT	RGB(255,255,255)
ToolBar buttons bot/right edges	COLOR_BTNSHADOW	RGB(128,128,128)

In addition, the toolbar bitmap buttons are recolored as though they were standard Windows button controls. This recoloring occurs when the bitmap is loaded from the resource and in response to a change in system colors in response to user customization via the Control Panel. The following colors in a toolbar bitmap will be recolored automatically so they should be used with caution. If you do not wish to have a portion of your bitmap recolored, then use a color that closely approximates one of the mapped RGB values. The mapping is done based on exact RGB values.

RGB Value	Dynamically mapped COLOR value
RGB(000, 000, 000)	COLOR_BTNTEXT
RGB(128, 128, 128)	COLOR_BTNSHADOW
RGB(192, 192, 192)	COLOR_BTNFACE
RGB(255, 255, 255)	COLOR_BTNHIGHLIGHT

Please refer to the Class Library Reference for details about the CToolBar creation and customization APIs. Most customization of toolbars should be done before the toolbar is initially made visible.

The customization APIs can be used to adjust the button IDs, styles, spacer width and which image/glyph is used for what button. By default you do not need to use these APIs.

CCmdUI Support for CToolBar

The way toolbar buttons are always updated is through the ON_UPDATE_COMMAND_UI mechanism. On idle time, the toolbar will call the ON_UPDATE_COMMAND_UI handler with the command ID of that button. ON_UPDATE_COMMAND_UI is not called for separators, but it is called for pushbuttons and check box buttons.

The ON_UPDATE_COMMAND_UI handler can call:

Enable:  to enable or disable the button. This works equally for pushbuttons and check box buttons.

SetCheck:  to set the check state of a button. Calling this for a toolbar button will turn it into a check box button. SetCheck takes a parameter which can be 0 (not checked), 1 (checked) or 2 (indeterminate)

SetRadio:  shorthand for SetCheck.

Check box buttons are "AUTO" check box buttons; that is, when the user presses them they will immediately change state. Checked is the down or depressed state. There is no user interface way to change a button into the "indeterminate" state; that must be done through code.

The customization APIs will permit you to change the state of a given toolbar button, preferably you should change these states in the ON_UPDATE_COMMAND_UI handler for the command the toolbar button represents. Remember, the idle processing will change the state of toolbar buttons with the ON_UPDATE_COMMAND_UI handler, so any changes to these states made through SetButtonStyle may get lost after the next idle.

Toolbar buttons will send WM_COMMAND messages like normal buttons or menu items and are normally handled by an ON_COMMAND handler in the same class that provides the ON_UPDATE_COMMAND_UI handler.

There are four Toolbar button styles (TBBS_ values) used for display states:

TBBS_CHECKED: 	check box is currently checked (down)

TBBS_INDETERMINATE:	check box is currently indeterminate

TBBS_DISABLED: 	button is currently disabled

TBBS_PRESSED: 	button is currently pressed

The six official Windows Interface Application Design Guide button styles are represented by the following TBBS values:

Up = 0

Mouse Down = TBBS_PRESSED (| any other style)

Disabled = TBBS_DISABLED

Down = TBBS_CHECKED

Down Disabled = TBBS_CHECKED | TBBS_DISABLED

Indeterminate = TBBS_INDETERMINATE

CDialogBar:

A dialog bar is a control bar that contains standard Windows controls. It acts like a dialog in that it contains the controls and supports tabbing between them. It also acts like a dialog in that it uses a dialog template to represent the bar.

A CDialogBar is used to for the print-preview toolbar, which contains standard pushbutton controls.

Using a CDialogBar is like using a CFormView. You must define a dialog template for the dialog bar and remove all the styles except WS_CHILD. Note that the dialog must not be visible.

The control notifications for a CDialogBar will be sent to the parent of the control bar (just like toolbar buttons).

CCmdUI support for CDialogBar

Dialog bar buttons should be updated through the ON_UPDATE_COMMAND_UI handler mechanism. At idle time, the dialog bar will call the ON_UPDATE_COMMAND_UI handler with the command ID of all the buttons that have a ID >= 0x8000 (that is, in the range of command IDs).

The ON_UPDATE_COMMAND_UI handler can call:

Enable:  to enable or disable the button.

SetText:  to change the text of the button.

Customization can be done through standard window manager APIs.


MFC Exception Mechanism
Technical Note 32: MFC Exception Mechanism

This note briefly describes the implementation of the MFC exception-handling mechanism as well as some of the more advanced features.

Please refer to the Class Library User's Guide for more details on exceptions.

Please refer to the Class Library Reference for details on the class CException and derived classes. You should also refer to CWinApp::ProcessWndProcException for details on the processing of unhandled exceptions in Windows applications.

AFX_EXCEPTION_* Structures

Each running application/task has a AFX_EXCEPTION_CONTEXT object representing the current thread of execution. This structure stores the current "global" state needed by the exception mechanism. This includes a place to store a pointer to the current exception object being thrown and the head of a chain of AFX_EXCEPTION_LINK objects.

The AFX_EXCEPTION_LINK structure is used to represent one link on the stack where exception handling in some form must be done. One AFX_EXCEPTION_LINK object is allocated on the stack for each TRY/CATCH/END_CATCH block.

The contents of each AFX_EXCEPTION_LINK object consist of a pointer to the next AFX_EXCEPTION_LINK object and a memory buffer used for the target implementation of "setjmp". For Windows-16 this is done through the Catch/Throw Windows APIs, and on non Windows and Windows-NT platforms, this is done through the C run-time setjmp function.

There are also some user-extensible fields in the AFX_EXCEPTION_LINK structure you can exploit in your program to implement C++ classes that can be created on the frame and will clean themselves up automatically when exceptions are thrown. These extensible fields are described later in this technical note.

New Macros

There are two new undocumented macros that can be used in place of the standard CATCH/END_CATCH blocks to optimize you code if you always catch all exceptions in a block. Code using these macros will be slightly smaller than using the more general CATCH/AND_CATCH/END_CATCH macros.

If your last part of an exception handler looks like:

AND_CATCH(CException, e)

END_CATCH

You can replace that with

AND_CATCH_ALL(e)

END_CATCH_ALL

Similarly, a trivial TRY/CATCH block of

TRY

{

    // do something

}

CATCH (CException, e)

{

    // handle all exceptions

}

END_CATCH

Can be replaced with:

TRY

{

    // do something

}

CATCH_ALL (e)

{

    // handle all exceptions

}

END_CATCH_ALL

The behavior is the same, since all exceptions in MFC must derive from the CException class. The only difference is the CATCH_ALL/END_CATCH_ALL macros will result in slightly smaller code.

Extending the AFX_EXCEPTION_LINK Structure

The current MFC exception mechanism follows the proposed extension to the ANSI C++ language. The TRY/CATCH macros provide a similar syntax and the subset behavior of the proposed mechanism. These macros have been designed to work with future compilers that support the C++ language extension.

For most of your C++ and the MFC library class programming, the normal TRY/CATCH macros should meet your needs. However, the current MFC exception implementation and the current C++ compiler do not address cleaning up C++ objects on the stack. That is, if a C++ object has a destructor that has a side-effect (such as freeing memory or freeing some other resource), throwing an exception may circumvent those destructors, resulting in a memory leak or worse.

The extent this impacts your code depends upon your programming style and the kind of classes you build with MFC. If you use objects allocated on the heap, this problem does not arise. However, you must catch exceptions and delete the heap objects, which will in turn call the objects' destructors.

If you have a class that you always place on the frame and that you always want to be properly destructed whether exceptions are thrown or not, there are several things you can do to make this class smart enough to clean itself up when exceptions are thrown. We will call these classes "auto-cleanup" classes.

Please be warned, this is not a replacement for writing robust code that handles exceptions properly and does explicit cleanup with TRY and CATCH blocks. This should be used sparingly for those classes where automatic frame destruction is essential.

Example1

Here is an example of a simple "auto-cleanup" class:

class AFX_STACK_DATA CAutoCleanup

{

    AFX_EXCEPTION_LINK m_link;

    static void PASCAL CleanupProc(AFX_EXCEPTION_LINK* pLink);

public:

    CAutoCleanup();

    ~CAutoCleanup();

};

CAutoCleanup::CAutoCleanup()

{

    m_link.m_nType = 1;

    m_link.m_callback.pfnCleanup = CleanupProc;

    m_link.m_callback.pvData = this;

    //TODO: add your code here

    // do something else here that you want to guarantee is destroyed

}

CAutoCleanup::~CAutoCleanup()

{

    //TODO: add your code here

    // do the appropriate auto-destruction here

}

void PASCAL CAutoCleanup::CleanupProc(AFX_EXCEPTION_LINK* pLink)

{

    // called to cleanup when an exception is thrown

    CAutoCleanup* pToCleanup = (CAutoCleanup*)pLink->m_callback.pvData;

    ASSERT(pToCleanup != NULL);

    pToCleanup->CAutoCleanup::~CAutoCleanup();

}

Several things to note:

Objects of this auto-cleanup class must be stored on the frame and can not be allocated on the heap or embedded in another class. Although it is possible to work around this limitation, it is not advisable since it can lead to even more confusing object destruction.

The class has the "AFX_STACK_DATA" memory model, which makes the class stack-based. Stack-based classes have more efficient code even in large model applications.

The 'm_link' private member variable is of type AFX_EXCEPTION_LINK, which is the same structure used for TRY/CATCH blocks.

In the constructor for the class, three fields of the 'm_link' member are initialized. 'm_nType' is a user-extensible field. Setting it to any value other than 0 will enable the auto-cleanup feature. 'm_callback.pfnCleanup' is the address of a procedure to be called when the object is to be cleaned up as a result of a thrown exception. 'm_callback.pvData' is a general pointer that points to the CAutoCleanup object.

When a CAutoCleanup object is placed on the stack, it can be destroyed in two ways. If an exception is not thrown, the normal C++ destruction mechanism will call the destructor. If an exception is thrown, the CleanupProc will be called to explicitly destroy the object.

Example2

Here is a different example of a auto-cleanup class. This example can be used as is. When constructing a CAutoDelete, you pass a pointer to an object of a CObject-derived class. This object must be allocated on the heap. The CAutoDelete destructor will delete the heap object when the CAutoDelete object goes out of scope or an exception is thrown.

Example of code fragment not using CAutoDelete:

CFile* pFile = new CFile;        // may throw an exception here

TRY

{

// do something that may throw an exception

}

CATCH (CException, e)

{

delete pFile;

THROW_LAST();

}

END_CATCH

delete pFile;

Example of code fragment using CAutoDelete helper class:

{

    CFile* pFile = new CFile;        // may throw an exception here

    CAutoDelete ad(pFile);

    // do something that may throw an exception

}

The source code to class CAutoDelete:

class CAutoDelete

{

    AFX_EXCEPTION_LINK link;

    static void PASCAL CleanupProc(AFX_EXCEPTION_LINK* pLink);

public:

    CAutoDelete(CObject* pOb);

    ~CAutoDelete();

};

CAutoDelete::CAutoDelete(CObject* pOb)

{

    link.m_nType = 2;    // any non-zero value will do

    link.m_callback.pfnCleanup = CleanupProc;

    link.m_callback.pvData = pOb;

}

void PASCAL CAutoDelete::CleanupProc(AFX_EXCEPTION_LINK* pLink)

{

    CObject* pToCleanup = (CObject*)pLink->m_callback.pvData;

    delete pToCleanup;

}

CAutoDelete::~CAutoDelete()

{

    CObject* pToCleanup = (CObject*)link.m_callback.pvData;

    delete pToCleanup;

}


Using Multiple Resources Files with App Studio
Technical Note 35: Using Multiple Resource Files and Header Files with App Studio

This note describes how App Studio supports multiple resource files and header files shared in a single project or shared across multiple projects and how you can take advantage of that support. This note answers these questions:

When might you want to split a project into multiple resource files and/or header files, and how you do it?

How do you share a common header .H file between two .RC files?

How do you divide project resources into multiple .RC files?

How do you (and the tools) manage build dependencies between .RC, .CPP, and .H files?

This note assumes that you have read Chapter 2, "Working With Files and Symbols", of the App Studio User's Guide.

This note is structured to answer the above questions as follows:

Overview of How App Studio Manages Resource Files and Header Files provides an overview of how the File Set Includes command in App Studio lets you use multiple resource files and header files in the same project.

Analysis of AppWizard-created .RC and .H Files looks at the multiple resource and header files that are used by an AppWizard-created application. These files serve as a good model for additional resource files and header files you might want to add to your project.

Including Additional Header Files describes where you might want to include multiple header files, and provides details how to do so.

Sharing a Header File Between Two .RC Files shows how you can share one header file between multiple .RC files in different projects, or perhaps in the same project.

Using Multiple Resource Files in the Same Project describes where you might want to break up your project into multiple .RC files, and provides details how to do so.

Enforcement of Non-Editable App Studio Files describes how you can make sure App Studio does not edit and unintentionally reformat a custom resource.

Managing Symbols Shared by Multiple App Studio-Edited .RC Files describes how to share the same symbols across multiple .RC files and how to avoid assigning duplicate ID numeric values.

Managing Dependencies Between .RC, .CPP, and .H Files describes how App Studio and Visual C++ avoid unnecessary recompiling .CPP files that are dependent on resource symbol files.

How App Studio Manages Set Includes Information provides technical details about how App Studio keeps track of multiple (nested) .RC files and multiple header files that are #include'd by an .RC file.

Overview of How App Studio Manages Resource Files and Header Files

App Studio manages a single .RC resource file and a corresponding .H header file as a tightly coupled pair of files. When you edit and save resources in an .RC file, you indirectly edit and save symbols in the corresponding .H file. Although you can open and edit multiple .RC files at a time (using App Studio's MDI user interface) for any given .RC file you indirectly edit exactly one corresponding header file.

Symbol Header File

By default, App Studio always names the corresponding header file RESOURCE.H, regardless of the name of the resource file (eg, MYAPP.RC). Using the File Set Includes command in App Studio, you can change the name of this header file by updating the Symbol Header File file in the Set Includes dialog.

Read-Only Symbol Directives

Although App Studio only edits one header file for any given .RC file, App Studio supports references to symbols defined in additional read-only header files. Using the File Set Includes command in App Studio, you can specify any number of additional read-only header files as Read-Only Symbol Directives. The "read-only" restriction means that when you add a new resource in the .RC file, you can use a symbol defined in the read-only header file; but if you delete the resource, the symbol still remains defined in the read-only header file. You cannot change the numeric value assigned to a read-only symbol.

Compile-Time Directives

App Studio also supports nesting of resource files, where one .RC file is #include'd within another. When you edit a given .RC file using App Studio, any resources in the #include'd files are not visible. But when you compile the .RC file, the #include'd files are also compiled. Using the File Set Includes command in App Studio, you can specify any number of #include'd .RC files as Compile-Time Directives.

Note what happens if you read into App Studio an .RC file that #include's another .RC file that is not specified as a Compile-Time Directive. This situation might arise when you bring to App Studio an .RC file that you had been previously maintaining manually with a text editor. When App Studio reads the #include'd .RC file, it merges the #include'd resources into the parent .RC file. When you save the parent .RC file, the #include statement, in effect, will be replaced by the #include'd resources. If you do not want this merge to happen, you should remove the #include statement from the parent .RC file prior to reading it into App Studio; then using App Studio, add back the same #include statement as a Compile-Time Directive.

App Studio saves in an .RC file the three kinds of above Set Includes information (Symbol Header File, Read-Only Symbol Directives, and Compile-Time Directives) in #include directives and in TEXTINCLUDE resources. The TEXTINCLUDE resources, an implementation detail that you do not normally need to deal with, are explained in How App Studio Manages Set Includes Information.

Analysis of AppWizard-created .RC and .H Files

Examining the application code produced by AppWizard provides insight into how App Studio's manages multiple resource files and header files. The code excerpts examined below are from a MYAPP application produced by AppWizard using the default options.

An AppWizard-created application uses multiple resource files and multiple header files, as summarized in the diagram below:

   RESOURCE.H     AFXRES.H

          \       /

           \     /

          MYAPP.RC

              |

              |

        RES\MYAPP.RC2

        AFXRES.RC

        AFXPRINT.RC

You can view these multiple file relationships using the App Studio File/Set Includes command.

MYAPP.RC is the application resource file that you edit using App Studio.

RESOURCE.H is the application-specific header file. It is always named RESOURCE.H by AppWizard, consistent with App Studio's default naming of the header file. The #include for this header file is the first statement in the resource file (MYAPP.RC):

//Microsoft App Studio generated resource script

//

#include "resource.h"

RES\MYAPP.RC2 contains resources that will not be edited by App Studio but will be included in the final compiled .EXE file. AppWizard creates only one such resource in MYAPP.RC2,  the VERSIONINFO resource. This resource should not be edited by App Studio because it is formatted to be read and edited directly using the Visual Workbench text editor. It also relies on macros defined in the Windows header file VER.H, and conditionally compiled strings. If App Studio were to read the VERSIONINFO resource, it would expand the macros, display the data in non-readable hexadecimal form in its Binary Data Editor, and write the resource back out to the RES\MYAPP.RC2 file in a non-human maintainable form.

If you use additional custom formatted resources, you can add them to RES\MYAPP.RC2 and edit them using the Visual Workbench text editor.

AFXRES.RC and AFXPRINT.RC contain standard resources required by certain features of the framework. Like RES\MYAPP.RC2, these two framework-provided resource files are #include'd at the end of MYAPP.RC, and they are specified in the Compile-Time Directives of the Set Includes dialog box. Thus, you do not directly view or edit these framework resources while you edit MYAPP.RC in App Studio, but they are compiled into the application's binary .RES file and final .EXE file. For more information on the standard framework resources, including procedures for modifying them, see Technical Note 23.

AFXRES.H defines standard symbols, such as ID_FILE_NEW, used by the framework and specifically used in AFXRES.RC. AFXRES.H also #include's WINRES.H, which contains a subset of WINDOWS.H that are needed by App Studio generated .RC files as well as AFXRES.RC. The symbols defined in AFXRES.H are available as you edit the application resource file (MYAPP.RC). For example, ID_FILE_NEW is used for the File New menu item in MYAPP.RC's menu resource. You cannot change or delete these framework-defined symbols.

Including Additional Header Files

The AppWizard-created application includes only two header files: RESOURCE.H and AFXRES.H. Only RESOURCE.H is application-specific. You may need to include additional read-only header files in the following cases:

The header file is provided by an external source, or you want to share the header file among multiple projects or multiple parts of the same project.

The header file has formatting and comments that you do not want App Studio to change or filter out when it saves the file. For example, maybe you want to preserve #define's that use symbolic arithmetic such as:

#define RED 0

#define BLUE 1

#define GREEN 2

#define ID_COLOR_BUTTON 1001

#define ID_RED_BUTTON (ID_COLOR_BUTTON + RED)

#define ID_BLUE_BUTTON (ID_COLOR_BUTTON + BLUE)

#define ID_GREEN_BUTTON (ID_COLOR_BUTTON + GREEN)

You can include additional read-only header files by using the File Set Includes command to specify the #include statement as a second Read-Only Symbol Directive, as in:

#include "afxres.h"

#include "second.h"

The new file relationship diagram now looks like this:

                   AFXRES.H

    RESOURCE.H     SECOND.H

          \       /

           \     /

          MYAPP.RC

              |

              |

        RES\MYAPP.RC2

        AFXRES.RC

        AFXPRINT.RC

Sharing a Header File Between Two .RC Files

You may want to share a header file between two .RC files that are in different projects, or possibly the same project. To do so, simply apply the Read-Only Directives technique described above to both .RC files.  In the case where the two .RC files are for different applications (different projects), the result is illustrated in the following diagram:

     RESOURCE.H   AFXRES.H   RESOURCE.H

    (for MYAPP1)  SECOND.H   (for MYAPP2)

          \       /     \       /

           \     /       \     /

          MYAPP1.RC      MYAPP2.RC

           /    \        /     \

          /      \      /       \

RES\MYAPP1.RC2  AFXRES.RC     RES\MYAPP2.RC2

                AFXPRINT.RC

The case where the second header file is shared by two .RC files in the same application (project) is discussed below.

Using Multiple Resource Files in the Same Project

App Studio and the Resource Compiler support multiple .RC files in the same project through #include's of one .RC file within another. Multiple nesting is allowed. There are various reasons to split your project's resources into multiple .RC files:

  It is easier to manage a large number of resources among multiple project team members if you split the resources into multiple .RC files. If you use a source control management package for checking out files and checking in changes, splitting the resources into multiple .RC files will give you finer control over managing changes to resources.

  If you want to use preprocessor directives, such as #ifdef, #endif, and #define, for portions of your resources, you must isolate them in read-only resources that will be compiled by the Resource Compiler.

  Component .RC files will load and save faster in App Studio than one composite .RC file.

If you want to maintain a resource with a text editor in a human-readable form, you should keep it in a .RC file separate from the one App Studio edits. The VERSIONINFO resource in the AppWizard-created MYAPP\RES\MYAPP.RC2 file discussed earlier is a good example.

If you need to keep a user-defined resource in a binary or text form that is interpretable by another specialized data editor, then you should keep it in a separate .RC file so App Studio does not change the format to hexadecimal data. The .WAV (sound) file resources in the SPEAKN sample application are a good example.

You can #include a SECOND.RC in the Compile-Time Directives in the Set Includes dialog box:

#include "res\myapp.rc2"  // non-App Studio edited resources

#include "second.rc"  // THE SECOND .RC FILE

#include "afxres.rc"  // Standard components

#include "afxprint.rc"  // printing/print preview resources

The result is illustrated in the following diagram:

   RESOURCE.H     AFXRES.H

          \       /

           \     /

          MYAPP.RC

              |

              |

        RES\MYAPP.RC2

        SECOND.RC

        AFXRES.RC

        AFXPRINT.RC

Using Compile-Time Directives, you can organize your App Studio-editable and non-editable resources into multiple .RC files, where the "master" MYAPP.RC does nothing but #include the other .RC files. If you are using a Visual C++ project .MAK file, then you should include the "master" .RC file in the project so that all the #include'd resources are compiled with your application.

Enforcement of Non-Editable App Studio Files

The AppWizard-created RES\MYAPP.RC2 file is an example of a file that contains resources that you do not want to accidentally read into App Studio and then write it back out with loss of formatting information. To protect against this, we place the following lines in the beginning of the RES\MYAPP.RC2 file:

#ifdef APSTUDIO_INVOKED

#error this file is not editable by App Studio

#endif //APSTUDIO_INVOKED

When App Studio compiles the .RC file, it defines APSTUDIO_INVOKED as well as RC_INVOKED. If the AppWizard-created file structure is corrupted and App Studio reads the #error line above, it reports a fatal error and abort the reading of the .RC file.

Managing Symbols Shared by Multiple App Studio-Edited .RC Files

Two issues arise when you split up your resources into multiple .RC files that you want to edit separately in App Studio:

 You might want to share the same symbols across multiple .RC files.

 You need to help App Studio avoid assigning the same ID numeric values to distinct resources (symbols).

The following diagram illustrates an organization of .RC and .H files that deals with the first issue:

              MYAPP.RC

             /         \

            /           \

MYSTRS.H   / MYSHARED.H  \  MYMENUS.H

     \    /    /      \   \    \

      \  /    /        \   \    \

   MYSTRS.RC           MYMENUS.RC

In this example, string resources are kept in one resource file, MYSTRS.RC, and menus are kept in another, MYMENUS.RC.  Some symbols, such as for commands, may need to be shared between the two files. For example, a ID_TOOLS_SPELL may be the menu command ID for the Spell item in a Tools menu; and it may also be the string ID of the command prompt displayed by the framework in the application's main window status bar.

The ID_TOOLS_SPELL symbol is kept in the shared header file, MYSHARED.H. You maintain this shared header file manually with a text editor; App Studio does not directly edit it. In the two resource files MYSTRS.RC and MYMENUS.RC, you specify #include MYSHARED.H in the Read-Only Directives for MYAPP.RC, using the File Set Includes command, as described earlier.

It is most convenient to anticipate a symbol you will share before you attempt use it to identify any resource. Add the symbol to the shared header file and, if you have not already #include'd the shared header file in the Read-Only Directives for the .RC file, do so before using the symbol. If you did not anticipate sharing the symbol in this way, then you will have to manually (using a text editor) move the #define statement for the symbol from, say, MYMENUS.H to MYSHARED.H before using it in MYSTRS.RC.

When you manage symbols in multiple .RC files, you also must help App Studio avoid assigning the same ID numeric values to distinct resources (symbols). For any given .RC file, App Studio incrementally assigns IDs in each of four ID domains. Between editing sessions, App Studio keeps track of the last ID it assigned in each of the domains in the symbol header file for the .RC file. Here is what the "App Studio Next" (APS_NEXT) values are for an empty (new) .RC file:

#define _APS_NEXT_RESOURCE_VALUE  101

#define _APS_NEXT_COMMAND_VALUE   40001

#define _APS_NEXT_CONTROL_VALUE   1000

#define _APS_NEXT_SYMED_VALUE     101

_APS_NEXT_RESOURCE_VALUE is the next symbol value that will be used for a dialog resource, menu resource, and so on. The valid range for resource symbol values is 1 to 0x6FFF.

_APS_NEXT_COMMAND_VALUE is the next symbol value that will be used for a command identification. The valid range for command symbol values is 0x8000 to 0xDFFF.

_APS_NEXT_CONTROL_VALUE is the next symbol value that will be used for a dialog control. The valid range for dialog control symbol values is 8 to 0xDFFF.

_APS_NEXT_SYMED_VALUE is the next symbol value that will be issued when you manually assign a symbol value using the New command in the Symbol Browser.

App Studio starts with slightly higher values that the lowest legal value when creating a new .RC file. AppWizard will also initialize these values to something more appropriate for MFC applications. For more information about ID value ranges, see Technical Note 20t.

Now every time you create a new resource file, even in the same project, App Studio defines the same _APS_NEXT_ values. This means that if you add, say, multiple dialogs in two different .RC files, it is highly likely that the same #define value will be assigned to different dialogs. For example, IDD_MY_DLG1 in the first .RC file might be assigned the same number, 101, as IDD_MY_DLG2 in a second .RC file.

To avoid this, you should reserve a separate numeric range for each of the four domains of IDs in the respective .RC files. Do this by manually updating the _APS_NEXT values in each of the .RC files before you start adding resources. For example, if the first .RC file uses the default _APS_NEXT values, then you might want to assign the following _APS_NEXT values to the second .RC file:

#define _APS_NEXT_RESOURCE_VALUE  2000

#define _APS_NEXT_COMMAND_VALUE   42000

#define _APS_NEXT_CONTROL_VALUE   2000

#define _APS_NEXT_SYMED_VALUE     2000

Of course, it is still possible that App Studio will assign so many IDs in the first .RC file that the numeric values start to overlap those reserved for the second .RC file. You should reserve sufficiently large ranges so that this does not happen.

Managing Dependencies Between .RC, .CPP, and .H Files

When App Studio saves an .RC file, it also saves symbol changes to the corresponding RESOURCE.H file. Any of your .CPP files that refer to resources in the .RC file must #include the RESOURCE.H file, usually from within your project's master header file. This leads to an undesirable side-effect due to the Visual Workbench's internal project management which scans source files for header dependencies. Every time you add a new symbol in App Studio, all the .CPP file that #include RESOURCE.H would need to be recompiled.

App Studio, in conjunction with Visual C++, circumvents the dependency on RESOURCE.H by including the following comment as the first line of the RESOURCE.H file:

//{{NO_DEPENDENCIES}}

The Visual Workbench interprets this comment by ignoring the changes to RESOURCE.H so that dependent .CPP files will not need to be recompiled.

App Studio always adds the //{{NO_DEPENDENCIES}} comment line to a .RC file when it saves the file. In some cases, circumventing of the build dependency on RESOURCE.H may lead to run-time errors undetected at link time. For example, if you use the Symbol Browser to change the numeric value assigned to a symbol for a resource, the resource will not be correctly found and loaded at application run-time if the .CPP file referring to the resource is not recompiled. In such cases, you should explicitly recompile any .CPP files that you know are affected by the symbol changes in RESOURCE.H or select Rebuild All. If you have the need to frequently change symbol values for a certain group of resources, you will probably find it more convenient and safer to break out these symbols into a separate read-only header file, as described in the above section Including Additional Header Files.

How App Studio Manages Set Includes Information

As discussed above, the File menu Set Includes command lets you specify three types of information:

  	Symbol Header File

  	Read-Only Symbol Directives

  	Compile-Time Directives

The following describes how App Studio maintains this information in a .RC file. You do not need this information to use App Studio, but it may enhance your understanding so that you can more confidently use the Set Includes feature.

Each of the above three types of Set Includes information is stored in the .RC file in two forms:  (1) as #include or other directives interpretable by the Resource Compiler, and (2) as special TEXTINCLUDE resources interpretable only by App Studio.

The purpose of the TEXTINCLUDE resource is to safely store Set Include information in a form that is readily presentable in App Studio's Set Includes dialog box. TEXTINCLUDE is a resource type defined by App Studio. App Studio recognizes three specific TEXTINCLUDE resources that have the resource identification numbers 1, 2 and 3:

TEXTINCLUDE  resource i.d.	Type of Set Includes information
1	Symbol Header File
2	Read-Only Symbol Directives
3	Compile-Time Directives

Each of the three types of Set Includes information is illustrated by the default MYAPP.RC and RESOURCE.H files created by AppWizard, as described below. The extra \0 and "" tokens between BEGIN and END blocks are required by the RC syntax to specify zero terminated strings and the double quote character respectively.

Symbol Header File

The form of the Symbol Header File information interpreted by the Resource Compiler is simply a #include statement:

#include "resource.h"

The corresponding TEXTINCLUDE resource is:

1 TEXTINCLUDE DISCARDABLE

BEGIN

#resource.h\0"

END

Read-Only Symbol Directives

Read-Only Symbol Directives are included at the top of MYAPP.RC in the following form interpretable by the Resource Compiler:

#include "afxres.h"

The corresponding TEXTINCLUDE resource is:

2 TEXTINCLUDE DISCARDABLE

BEGIN

"#include ""afxres.h""\r\n"

"\0"

END

Compile-Time Directives

Compile-Time Directives are included at the end of MYAPP.RC in the following form interpretable by the Resource Compiler:

#ifndef APSTUDIO_INVOKED

///////////////////////

//

// From TEXTINCLUDE 3

//

#include "res\myapp.rc2"  // non-App Studio edited resources

#include "afxres.rc"  // Standard components

#include "afxprint.rc"  // printing/print preview resources

#endif  // not APSTUDIO_INVOKED

The #ifndef APSTUDIO_INVOKED directive instructs App Studio to skip over Compile-Time Directives.

The corresponding TEXTINCLUDE resource is:

3 TEXTINCLUDE DISCARDABLE

BEGIN

"#include ""res\myapp.rc2""  // non-App Studio edited resources\r\n"

"\r\n"

"#include ""afxres.rc""  // Standard components\r\n"

"#include ""afxprint.rc""  // printing/print preview resources\r\n"

"\0"

END


Using CFormView with AppWizard and ClassWizard
Technical Note 36: Using CFormView with AppWizard and ClassWizard

This technical note shows how to use AppWizard and ClassWizard to create a CFormView-derived class and associate it with a view in your application. The instructions contained in this technical note are an expanded and revised version of part of the CFormView preview in the Class Library Reference. Please refer to the Class Library Reference for additional information.

AppWizard does not provide an option for using a form view in a new application. To use a form view, you must run AppWizard as usual and then replace the original view class with a CFormView-derived class. The remainder of this note gives the steps necessary to create and use a form view using AppWizard.

1.	Design a form view dialog template.

Use the App Studio dialog editor to design the dialog box. Then, in the Styles property page, set the following properties:

In the Style box, select Child (WS_CHILD on).

In the Border box, select None (WS_BORDER off).

Clear the Visible check box (WS_VISIBLE off).

Clear the Titlebar check box (WS_CAPTION off).

These steps are necessary because a form view is not a true dialog box. For more information about creating a dialog-box resource using App Studio, see Chapter 3, Using the Dialog Editor, in the App Studio Users Guide.

If you want your form view to handle commands from menus and accelerators, move to the General property page and, in the ID box, give the form view the same ID as the menu or accelerator resource whose commands you want to handle. If you are writing a single document interface (SDI) application, specify the main application menu (IDR_MAINFRAME) as the dialog ID for your CFormView-derived class; if you are writing a multiple document interface (MDI) application, specify the MDI childs menu (IDR_yourappTYPE) as the dialog ID.

2.	Create a CFormView-derived view class with ClassWizard. This class replaces the CView-derived view class supplied by AppWizard.

With the form view dialog template still open, invoke ClassWizard, choose CFormView as the class type, and fill in the rest of the information in the Add Class dialog box. ClassWizard creates a CFormView-derived class and connects it to the dialog template you just designed using the templates resource ID.

3.	Define data members that correspond to the form views controls.

Use ClassWizard to define member variables in the CFormView-derived class for each of the appropriate form view controls. These data members usually correspond to members present in your document class, or in an object owned by your document class. This allows you to take advantage of the frameworks dialog data exchange (DDX) mechanism and automatically transfer data to and from the form view. For more information on DDX, see Chapter 7 of the Class Library Users Guide or Chapter 9 of the App Studio Users Guide.

4.	Associate your CFormView-derived class with your document and frame-window classes using a document template.

In the InitInstance member function of your CWinApp-derived class, modify the call to AddDocTemplate by replacing the name of the original view class with the name of your CFormView-derived class.

5.	Override CView::OnUpdate to update the form view as required.

The OnUpdate member function should first update the member variables in your view class with the appropriate values from the current document. Then the function should call UpdateData (defined by CWnd) with an argument of FALSE to move data from the classs data members to the corresponding controls in the form view. For example:

void CMyFormView::OnUpdate (CView*, LPARAM, CObject* )

{

    CMyappDoc* pDoc = (CMyappDoc*)GetDocument();

    // Then set view data members to same value as corresponding

    // document data members. For example:

    // m_string1 = pDoc->m_string1;

    UpdateData( FALSE );   // Transfers data from view class to screen

    Invalidate();        // Causes repainting of view

}

6.	Implement a member function to move data from your view class to your document class.

This member function is typically a message handler for a control-notification message or for a menu command such as Commit or Update. Using DDX, call the UpdateData member function to update the member variables in your view class. Then move their values to the document associated with the form view. For example:

void CMyFormView::OnCommit()

{
    CMyappDoc* pDoc = (CMyappDoc*)GetDocument();

    UpdateData(); //Transfers data from screen to view data members

    // Then move values from view class to document class.

    // For example,

    // pDoc->m_string1 = m_string1;

    pDoc->SetModifiedFlag();

    pDoc->UpdateAllViews( NULL );

}

7.	Remove the original AppWizard-created view class from the project by choosing the Edit command from the Project menu. Then delete the ClassWizard (.CLW) file in your project directory and invoke ClassWizard from App Studio to build an updated ClassWizard file.


Multithreaded MFC 2.1 Applications
Technical Note 37: Multithreaded MFC 2.1 Applications

Overview

The MFC 2.1 library does not support multithreading in this release.  For the most part, you cannot call MFC classes or global AFX helper functions from any thread but the primary thread.  The focus of this release is to bring MFC 2.0 APIs to Win32, so that Win16 MFC 2.0 applications can be easily ported to Win32 systems such as Windows/NT.  In the future, we'll consider adding wrappers for Win32specific features, such as multiple threads.

Threads are not supported because none of the MFC code has been written to be thread-protected.  There are portions of MFC that can be used from secondary threads, but these classes are the simplest MFC classes, and are by far the least helpful in implementing a sophisticated thread.  Examples of classes that will work are CRect, CPoint, CSize, CTime, and CTimeSpan.  Also, calling MFC code from secondary threads has not been tested.

What works and what doesn't

The MFC classes which will not work from a secondary thread are those that manipulate global data structures or allocate memory.  Unfortunately, this eliminates a large number of classes available under MFC.

Classes and functions that allocate memory are excluded because, under the _DEBUG version of MFC, the debug replacements for global operator new and operator delete are not reentrant.  This eliminates a fairly simple class like CString, for example.  Even the collection classes (CWordArray, CMapPtrToPtr, etc.) allocate memory dynamically and will not work correctly if called from multiple threads simultaneously.  Classes that allocate memory but do not manipulate global data structures (such as CString) should work when using the non _DEBUG version of the library.  In the retail library, the standard C++ runtime operator new and operator delete will be used, and provided that your application is linked with the multithread runtime, there should be no problem allocating memory from the heap from different threads simultaneously.  Not being able to use the _DEBUG memory allocator and its associated diagnostics is a big price to pay for thread support for these limited classes.

Classes that manipulate global data include any class that has a handle map.  For example, CWnd, CDC,  CGDIObject, and CMenu all maintain handle maps.  Because access to these maps is not thread-protected, these classes cannot be used from more than one thread.

Because MFC does not support callers from secondary threads, you should write those threads in C using the Windows C API.  It is possible to use C++, but for the reasons mentioned above, operator new and operator delete should not be used.

Using C++ member functions as a thread entry point

The entry point to the thread should have a C interface or be a static member.  It is possible to build a thread class that calls a C++ member function.  For example,

class CThread

{

// Operations

public:

HANDLE CreateThread(DWORD* pdwThreadID,

DWORD cbStack = 0, DWORD fdwCreate = 0);

// Overrideables

public:

virtual DWORD RunThread() = 0;

// derive your class from CThread and override RunThread().

// Implementation

public:

void* operator new(size_t nSize);

void operator delete(void* p);

// override operator new & delete to avoid MFC debug

// 	allocator.

virtual ~CThread();

// should have virtual destructor if have virtual functions

// 	or operator new/delete overloads

protected:

static DWORD __stdcall ThreadEntryPoint(LPVOID lpParam);

// implementation of thread entry point shared by all

// 	CThread derived classes.

};

DWORD __stdcall CThread::ThreadEntryPoint(LPVOID lpParam)

{

CThread* pThread = (CThread*)lpParam;

// lpParam is actually the 'this' pointer

ASSERT(pThread != NULL);

DWORD dwResult = pThread->RunThread();	// run it

delete pThread;	// auto delete the thread when it is finished

return dwResult;

}

HANDLE CThread::CreateThread(

DWORD* pdwThreadID, DWORD cbStack, DWORD fdwCreate)

{

// simple wrapper for Win32 CreateThread API

return ::CreateThread(NULL, cbStack,

ThreadEntryPoint,

      (LPVOID)this, // will be lpParam in ThreadEntryPoint

fdwCreate, pdwThreadID);

}

void* CThread::operator new(size_t nSize)

{

return ::malloc(nSize);

}

void CThread::operator delete(void* p)

{

::free(p);

}

CThread::~CThread()

{

}

Defining a CThread derived class

To use this class, we derive from CThread, add member data as needed, and override RunThread.

By using this technique you can customize the data that you pass to your CThread derivative as well as define the behavior of your CThread derivative.  For example, this class will call MessageBeep the number of times specified in the member m_nBeepCount.

class CCountThread : public CThread

{

public:

CCountThread(UINT nBeepCount);

// Implementation

protected:

virtual DWORD RunThread();

UINT m_nBeepCount;	// number of times left to beep

};

CCountThread::CCountThread(UINT nBeepCount)

{

m_nBeepCount = nBeepCount;

}

DWORD CCountThread::RunThread()

{

for (UINT n = 0; n < m_nBeepCount; n++)

::MessageBeep(0);

return 0;

}

Using a CThread derived class

To use the CCountThread class, we simply instantiate one on the heap.  Instead of using global operators new and delete, it will use those defined in the CThread class.  Next, we create the thread, which causes the CCountThread::RunThread to start running in the secondary thread.

...

CCountThread* pCount = new CCountThread(20);

// create CCountThread object on the heap

DWORD dwThreadID;

HANDLE hThread;

if ((hThread = pCount->CreateThread(&dwThreadID)) == NULL)

TRACE0("Error: could not CCountThread\n");

// the object will now run, beeping 20 times while this

// thread (the primary thread) continues to run.

// when it is done beeping, the pCount pointer will be

// automatically deleted and will become invalid.

// while that is happening the application may wish to

// use dwThreadID or hThread to manipulate/check status

// on the thread.

...

// sooner or later we need to call CloseHandle to cause

// the "system" thread object referenced by hThread

// to go away.  Doing so before the RunThread routine

// is done will not terminate the thread.

::CloseHandle(hThread);

Communication between threads

Once your secondary thread is running, there is no problem in communicating to the primary thread through Win32 IPC mechanisms or through Windows mechanisms such as PostMessage or SendMessage.  It is important that you do not pass the secondary thread C++ member variables such as CWnd*, for reasons already mentioned.  Instead, pass the thread Windows based handles (such as an HWND) where possible.

As discussed, you should not communicate through MFC objects or mechanisms.

Summary

Obviously, this note has only touched on the possible techniques for dealing with threads.  Threads are a very complex issue, and should be used only where needed.  Determine where your application would benefit most from threads, and focus on enabling those areas for threads.  Don't waste time making everything a thread simply because the system supports it.

If you have ideas or suggestions on what you'd like to see in future versions of MFC with regard to threads, contact us.  We'd like to hear your suggestions.


 Primary thread: the thread processing the main message loop in CWinApp::Run.


WIndows for Pen
Technical Note 15: Windows for Pen

Note:

Windows for Pen is not supported when compiling with Windows NT.

The MFC 2.0 product for Windows 3.1 supports the Windows for Pen environment that is available as an add-on to Windows 3.1.  Windows NT does not provide the Windows for Pen environment.  Therefore, the MFC provided with Visual C++ 1.0 for Windows NT does not provide this support.


Emulation Support for Visual Basic Custom Controls
Technical Note 27: Emulation Support for Visual Basic Custom Controls

Note:

VBX controls are not supported by Visual C++ for Windows NT.

The Visual C++ 1.0 product for Windows 3.1 supports the use of VBX custom controls.  VC++ 1.0 for NT does not support VBX controls.  This includes both the App Studio resource editor as well as runtime support in the MFC classes (CVBControl).  The VBX API is inherently a 16-bit API packaged as 16-bit DLLs.  32-bit NT applications cannot directly call 16-bit DLLs, so calling a VBX from a 32-bit application is difficult at best.  Complicating the matter is the fact that the VBX API takes advantage of Intel segmentation to a great degree, using compiler extensions such as based pointers.  This design (especially based pointers) does not mix well with the 32-bit flat address space of NT applications.  If an application is dependent on VBX custom controls, it should be built as a 16-bit Windows application and run under NT's WOW (Windows on Windows) layer.


DLL Version of MFC
Technical Note 33: DLL Version of MFC

Note:

These DLLs are not available when compiling with Windows NT.

The MFC 2.0 product for Windows 3.1 supports a single shared DLL version of the classes called MFC200[D].DLL (or the _AFXDLL variant).  DLLs on Windows NT are significantly different than DLLs on Windows 3.1.  As such, this shared DLL is not available with Visual C++ 1.0 for NT.  DLL writers can continue to use the _USRDLL variant, where a copy of the MFC implementation is included with the DLL.  Applications which used MFC200.DLL can simply be recompiled to use the static link library, NAFXCW[D].LIB, instead.


Writing a Windows 3.0 Compatible MFC Application
Technical Note 34: Writing a Windows 3.0 Compatible MFC Application

Note:

This Technical Note is not applicable to Windows NT.

The initial release of Windows NT contains the Windows 3.1 features.  Therefore, the support for the older Windows 3.0 programming has been removed from the MFC provided with Visual C++ 1.0 for Windows NT.


Click Event

See Also

Description

Occurs when the user presses and then releases a mouse button over an object.  It may also occur when the value of a control is changed.

For a control, this event occurs when the user clicks a control with the left mouse button.

Syntax

void CMyDialog::OnClickControl(UINT, int, CWnd*, LPVOID)

Remarks

Typically, you attach a Click procedure to a control to carry out commands and command-like actions.

When a Click event is generated, Visual C++ also calls the related events in this order:  MouseDown, MouseUp, Click.  When you're attaching procedures for these related events, be sure that their actions don't conflict.

Note	To distinguish between the left, right, and middle mouse buttons, use the MouseDown and MouseUp events.


See Also

Help:

DblClick Event

Mousedown, MouseUp Events


DblClick Event

See Also

Description

The DblClick Event occurs when the user double-clicks a control with the left mouse button.

Syntax

void CMyDialog::OnDblClickControl(UINT, int, CWnd*, LPVOID)

Remarks

You can use a DblClick event procedure for an "implied action" such as double-clicking an icon to open a window or document.  This type of procedure is also useful for carrying out multiple steps with a single action, such as double-clicking to select an item in a list box and to close the dialog box.

For those objects that receive mouse events, the events occur in this order: MouseDown, MouseUp, Click, and DblClick.

If DblClick does not occur within the system's double-click time limit, the object recognizes another sequence of MouseDown, MouseUp, and Click events.  The double-click time limit may vary because the user can set the double-click speed with the Control Panel.  When you are attaching procedures for these related events, be sure that their actions don't conflict.  Controls that do not receive DblClick events may receive two clicks instead of a DblClick.

Note	To distinguish between the left, right, and middle mouse buttons, use the MouseDown and MouseUp events.


See Also

Help

Click Event

MouseDown, MouseUp Events


DragDrop Event

Remarks

This event is only supported in Visual Basic.  It is never sent in Visual C++.


DragOver Event

Remarks

This event is only supported in Visual Basic.  It is never sent in Visual C++.


GotFocus Event

See Also

Description

Occurs when an object receives the focus, either by user action such as tabbing to or clicking the object, or by changing the focus in code using CWnd::SetFocus.

Syntax

void CMyDialog::OnGotFocusControl(UINT, int, CWnd*, LPVOID)

Remarks

Typically, you use a GotFocus procedure to specify the actions that occur when a control first receives the focus.  For example, by attaching a GotFocus procedure to each control on a form, you can guide the user by displaying brief instructions or status bar messages.  You can also provide visual cues by enabling, disabling, or showing other controls that depend on the control that has the focus.

Note	An object can receive the focus only if its Enabled and Visible properties are set to TRUE.  To customize Visual C++'s keyboard interface for moving the focus, set the Tab order or specify access keys for controls on a form.


See Also

Help:

LostFocus Event

TabIndex Property

TabStop Property


LostFocus Event

See Also

Description

Occurs when an object loses the focus, either by user action such as tabbing to or clicking another object, or by changing the focus in code using CWnd::SetFocus.

Syntax

void CMyDialog::OnLostFocusControl(UINT, int, CWnd*, LPVOID)

Remarks

A LostFocus procedure is primarily useful for verification and validation updates.  Using LostFocus can cause validation to take place as the user leaves the control.  Another use for this type of procedure is enabling, disabling, hiding, and displaying other objects, as in a GotFocus procedure.  You can also reverse or change conditions that you set up in the object's GotFocus procedure.


See Also

Help:

GotFocus Event

TabIndex Property

TabStop Property


MouseDown, MouseUp Events

See Also

Description

Occurs when the user presses (MouseDown) or releases (MouseUp) a mouse button over the control.

Syntax

void CMyDialog::OnMouseDownControl(UINT, int, CWnd*, LPVOID lpParams)

void CMyDialog::OnMouseUpControl(UINT, int, CWnd*, LPVOID lpParams)

Button = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 3)

Shift = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 2)

X = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 1)

Y = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 0)

Remarks

The MouseDown and MouseUp events use these arguments:

Argument	Description

Button	The button was pressed (MouseDown) or released (MouseUp) to cause the event.  The Button argument is a bit field with bits corresponding to the left button (bit 0), right button (bit 1), and middle button (bit 2) [image] values 1, 2, and 4, respectively.  Only one of the bits is set, indicating which button caused the event.

Shift	The state of the Shift, Ctrl and Alt keys when the button specified in the Button argument was pressed or released.  A bit is set if the key is down.  The Shift argument is a bit field, with the least-significant bits corresponding to the Shift key (bit 0), the Ctrl key (bit 1), and the Alt key (bit 2 ).  These bits correspond to the values 1, 2, and 4, respectively.  Shift indicates the state of these keys.  Some, all, or none of the bits can be set, indicating that some, all, or none of the keys is pressed.  For example, if both Ctrl and Alt were pressed, the value of Shift would be 6.

X, Y	The current location of the mouse pointer. X and Y are always expressed in Pixels.

Use a MouseDown or MouseUp procedure to specify actions to occur when a given mouse button is pressed or released.  Unlike the Click and DblClick events, MouseDown and MouseUp events allow you to distinguish between the left, right, and middle mouse buttons.  You can also write code for mouse-keyboard combinations that use the Shift, Ctrl, and Alt keyboard modifiers.

If you need to test for the Button or Shift arguments, you can declare constants that define the bits within the argument.  These constants have the following values:

#define LEFT_BUTTON	1

#define RIGHT_BUTTON	2

#define MIDDLE_BUTTON	4

#define SHIFT_MASK	1

#define CTRL_MASK	2

#define ALT_MASK	4

The constants then act as bit masks you can use to test for any combination of buttons without having to figure out the unique bit field value for each combination.

The following applies to both Click and DblClick events:

If a mouse button is pressed while the pointer is over a form or control, that object "captures" the mouse and receives all mouse events up to and including the last MouseUp event.  This implies that the X, Y mouse-pointer coordinates given by a mouse event may not always be in the client area of the object that receives them.

If mouse buttons are pressed in succession, the object that captures the mouse after the first press receives all mouse events until all buttons are released.

Note	You can use a MouseMove procedure to respond to an event caused by moving the mouse.  The Button argument for MouseDown and MouseUp differs from the Button argument used for MouseMove.  For MouseDown or MouseUp, the Button argument indicates exactly one button per event; for MouseMove, it indicates the current state of all buttons.


See Also

Help:

Click Event

DblClick Event

MouseMove Event

MousePointer Property


MouseMove Event

See Also

Description

Occurs when the user moves the mouse when over the control and/or when the mouse is captured by the control.

Syntax

void CMyDialog::OnMouseMoveControl(UINT, int, CWnd*, LPVOID lpParams)

Button = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 3)

Shift = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 2)

X = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 1)

Y = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 0)

Remarks

The MouseMove event uses these arguments:

Argument	Description

Button	The state of the mouse buttons, in which a bit is set if the button is down.  The Button argument is a bit field, with bits corresponding to the left button (bit 0), right button (bit 1), and middle button (bit 2) [image] values 1, 2, and 4, respectively.  It indicates the complete state of the mouse buttons; some, all, or none of these three bits can be set, indicating that some, all, or none of the buttons is pressed.

Shift	The state of the Shift, Ctrl and Alt keys: Bit is set if the key is down.  The Shift argument is a bit field, with the least-significant bits corresponding to the Shift key (bit 0), the Ctrl key (bit 1), and the Alt key (bit 2 ).  These bits correspond to the values 1, 2, and 4, respectively.  Shift indicates the state of these keys.  Some, all, or none of the bits can be set, indicating that some, all, or none of the keys is pressed.  For example, if both Ctrl and Alt were pressed, the value of Shift would be 6.

X ,Y	The current location of the mouse pointer.  X and Y are always expressed in Pixels.

The MouseMove event is generated continually as the mouse pointer moves across objects.  Unless another object has captured the mouse, an object recognizes a MouseMove event whenever the mouse position is within its borders.

If you need to test for the Button or Shift arguments, you can declare constants that define the bits within the argument.  These constants have the following values:

#define LEFT_BUTTON	1

#define RIGHT_BUTTON	2

#define MIDDLE_BUTTON	4

#define SHIFT_MASK	1

#define CTRL_MASK	2

#define ALT_MASK	4

The constants then act as bit masks you can use to test for any combination of buttons without having to figure out the unique bit field value for each combination.

You test for a condition by first assigning each result to a temporary integer variable and then comparing the Button or Shift arguments to a bit mask.  Use the logical AND operator (&) with each argument to test if the condition is not zero, indicating the key or button is pressed [image] for example:

LeftDown = (Button & LEFT_BUTTON) != 0
CtrlDown = (Shift & CTRL_MASK) != 0

Then, in a procedure, you can test for any combination of conditions [image] for example:

If (LeftDown && CtrlDown)

// Control + Left processing

Note	You can use MouseDown and MouseUp event procedures to respond to events caused by pressing and releasing mouse buttons.

The Button argument for MouseMove differs from the Button argument used for MouseDown and MouseUp.  For MouseMove, the Button argument indicates the current state of all buttons; a single MouseMove event may indicate that some, all, or no button is pressed.  For MouseDown or MouseUp, the Button argument indicates exactly one button per event.


See Also

Help:

Click Event

DblClick Event

MousePointer Property

MouseUp, MouseDown Events


KeyDown, KeyUp Events

Description

Occurs when the user presses (KeyDown) or releases (KeyUp) a key while an object has the focus.  (To interpret ANSI characters, use the KeyPress event.)

Syntax

void CMyDialog::OnKeyDownControl(UINT, int, CWnd*, LPVOID lpParams)

void CMyDialog::OnKeyUpControl(UINT, int, CWnd*, LPVOID lpParams)

KeyCode = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 1)

Shift = AFX_NUM_EVENTPARAMINDEX(short, lpParams, 0)

Remarks

The KeyDown and KeyUp events use these arguments:

Argument	Description

KeyCode	A key code, such as, VK_F1 (F1 key) or VK_HOME (Home key).  To specify key codes, use the constants in WINDOWS.H.

Shift	The state of the Shift, Ctrl, and Alt keys at the time of the event.  The Shift argument is a bit field, with the least-significant bits corresponding to the Shift key (bit 0), the Ctrl key (bit 1), and the Alt key (bit 2 ).  These bits correspond to the values 1, 2, and 4, respectively.  Some, all, or none of the bits can be set, indicating that some, all, or none of the keys is pressed.  For example, if both Ctrl and Alt are pressed, the value of Shift is 6.

For both events, the object with the focus receives all keystrokes.  Although KeyDown and KeyUp can apply to most keys, they are most often used for:

Extended character keys such as function keys.

 	Navigation keys.

Combinations of keys with standard keyboard modifiers.

Distinguishing between the numeric keypad and regular number keys.

Use KeyDown and KeyUp event procedures for keyboard handlers if you need to respond to both the pressing and releasing of a key.

In Dialogs and Form Views, the KeyDown and KeyUp are not invoked for:

The Enter key

The Esc key

The Tab key.

If you need to test for the Shift argument, you can declare constants that define the bits within the argument.  The Shift constants have the following values:

#define SHIFT_MASK	1

#define CTRL_MASK	2

#define ALT_MASK	4

The constants act as bit masks that you can use to test for any combination of buttons.


KeyPress Event

Description

Occurs when the user presses and releases an ANSI key.

Syntax

void CMyDialog::OnKeyPressControl(UINT, int, CWnd*, LPVOID lpParams)

KeyAscii = AFX_NUM_EVENTPARAM(short, lpParams)

AFX_NUM_EVENTPARAM(short, lpParams) = NewKey

Remarks

The KeyPress event uses these arguments:

Argument	Description

KeyAscii	Returns a standard numeric ANSI keycode.  Assigning NewKey to the parameter sends a different character to the object.  If NewKey is 0, it cancels the keystroke so that the object receives no character.

The object with the focus receives the event.  A KeyPress event can involve any printable keyboard character, the Ctrl key combined with a character from the standard alphabet or one of a few special characters, and the Enter or Backspace key.  A KeyPress event procedure is useful for intercepting keystrokes entered in a text box or combo box.  It allows you to immediately test keystrokes for validity or to format characters as they are typed.  Assigning a NewKey value changes the character displayed.

You can convert the argument KeyAscii into a character by using the expression:

Chr(KeyAscii)

You can then perform string operations and translate the character back to an ANSI number that the control can understand by using the expression:

KeyAscii = Asc(char)

Use KeyDown and KeyUp event procedures to handle any keystroke not recognized by KeyPress, such as function keys, editing keys, navigation keys, and any combinations of these with keyboard modifiers.  Unlike the KeyDown and KeyUp events, KeyPress does not indicate the physical state of the keyboard; instead, it passes a character.

KeyPress interprets the uppercase and lowercase of each character as separate keycodes and, therefore, as two separate characters.  KeyDown and KeyUp interpret the uppercase and lowercase of each character by means of two arguments: KeyCode, which indicates the physical key (thus returning "A" and "a" as the same key); and Shift, which indicates the state of Shift+key and therefore returns either "A" or "a."

Note	The ANSI number for the keyboard combination of Ctrl+@ is 0.  Because Visual C++ recognizes a KeyAscii value of 0 as a null string, you should avoid using Ctrl+@ in your applications.


BorderStyle Property

Description

Determines the border style for an object.

Usage

pControl->GetNumProperty("BorderStyle")

pControl->SetNumProperty("BorderStyle", nSetting)

Setting

The BorderStyle property settings for a grid are:

Setting	Description

0	None.

1	Fixed Single.

Data Type

Integer (Enumerated)


Caption Property

Description

Determines the text displayed in or next to a control.

Usage

pControl->GetStrProperty("Caption")

pControl->SetStrProperty("Caption", strCaption)

Remarks

When you create a new object, its default caption is the same as the default CtlName.  This default caption includes the object name and an integer, such as Command1 or Form1.  For a more descriptive label, set the Caption property.

You can use the Caption property to assign an access key to a control.  In the caption, include an ampersand (&) immediately preceding the character you want for an access key.  The character will be underlined.  Press Alt plus the underlined character to move the focus to that control.  To include an ampersand in a caption without creating an access key, type a double ampersand (&&).  A single ampersand will appear in the caption and no characters will be underlined.

For labels, the Caption is limited to 2048 characters.  For all other controls that have captions, the limit is 255 characters.

Data Type

String


DragIcon Property

Remarks

This property is only supported in Visual Basic.  It is not available in Visual C++.


DragMode Property

Remarks

This property is only supported in Visual Basic.  It is not available in Visual C++.


Enabled Property

Description

Determines whether the control can respond to user-generated events.

Usage

pControl->GetNumProperty("Enabled")

pControl->SetNumProperty("Enabled", bEnabled)

Setting

The Enabled property settings are:

Setting	Description

TRUE	(Default) Allows the object to respond to events.

FALSE	Prevents the object from responding to events.

Remarks

This property allows forms and controls to be enabled or disabled at run time.  For example, you can disable objects that don't apply to the current state of the application.  You can also disable a control used purely for display purposes, such as a text box that provides read-only information.

Note	In Visual C++ it is more efficient to use CWnd::IsWindowEnabled to determine if the control is enabled and to use CWnd::Enable to set this property.

Data Type

Integer (Boolean)


FontBold, FontItalic, FontStrikethru, FontTransparent, FontUnderline Properties
FontBold, FontItalic, FontStrikethru, FontUnderline Properties

Description

Determine font styles in the following formats:  FontBold, FontItalic, FontStrikethru, and FontUnderline.

Usage

pControl->GetNumProperty("FontBold")

pControl->SetNumProperty("FontBold", bBold)

pControl->GetNumProperty("FontItalic")

pControl->SetNumProperty("FontItalic", bItalic)

pControl->GetNumProperty("FontStrikethru")

pControl->SetNumProperty("FontStrikethru", bStrikethru)

pControl->GetNumProperty("FontUnderline")

pControl->SetNumProperty("FontUnderline", bUnderline)

Setting

The settings for the FontBold, FontItalic, FontStrikethru, and FontUnderline properties are:

Setting	Description

TRUE	(Default for FontBold) Turns on the formatting in that style.

FALSE	(Default for FontItalic, FontStrikethru, and FontUnderline) Turns off the formatting in that style.

Remarks

Use these font properties to format text, either at design time using the Properties window or at run time using code.  Font changes take effect on the screen immediately.

Note	Available fonts vary according to your system configuration, display devices, and printing devices.  Font-related properties can be set only to values for which actual fonts exist.

In general, you should change the FontName property before setting size and style attributes with the FontSize, FontBold, FontItalic, FontStrikethru, and FontUnderline properties.  One exception to this is when you need to set TrueType fonts to smaller than 8 points.  In this case, you should set the point size with the FontSize property, then set the FontName property, and finally, set the size again with the FontSize property.  The Windows environment actually uses a different font for TrueType fonts that are smaller than 8 points.

Data Type

Integer (Boolean)


Left, Top Properties

Description

Left [image] determines the distance between the left edge of an object and the left edge of its container.

Top [image] determines the distance between the top edge of an object and the top edge of its container.

Left and top are expressed in pixels.  The values for these properties change as the object is moved by the user or by code.

Usage

pControl->GetNumProperty("Left")

pControl->GetNumProperty("Left", nXPos)

pControl->GetNumProperty("Top")

pControl->GetNumProperty("Top", nYPos)

Remarks

Use Left and Top, and the Height and Width properties, for operations based on an object's external dimensions, such as moving or resizing.

Data Type

Integer


TabIndex Property

Description

Determines the tab order of a control within its parent.

Usage

pControl->GetNumProperty("TabIndex")

pControl->GetNumProperty("TabIndex", nIndex)

Setting

The valid range is any integer from 0 to (n-1), where n is the number of controls on the parent.  Assigning a TabIndex value of less than 0 generates an error.

Remarks

By default, Visual C++ assigns a tab order to controls as you draw them.  Each new control is placed last in the tab order.  If you change the value of a control's TabIndex property to adjust the default tab order, Visual C++ automatically renumbers the TabIndex of other controls to reflect insertions and deletions.  This property is not available in App Studio.  Instead, use the Layout.Set Tab Order command.

Note	A control's tab order does not affect its associated access key.  If you press the access key for a frame or label, the focus moves to the next control in the Tab order that can receive the focus.

Data Type

Integer


TabStop Property

Description

Determines whether a user can use the Tab key to set the focus to a control.

Usage

pControl->GetNumProperty("TabStop")

pControl->SetNumProperty("TabStop", bTabStop)

Setting

The TabStop property settings are:

Setting	Description

TRUE	(Default) Designates the control as a tab stop.

FALSE	Bypasses the control when the user is tabbing, although the control still holds its place in the actual tab order.

Remarks

This property allows you to add or remove a control from the tab order on a form.  For example, instead of moving through each button in an option button group, you can treat the whole group as one tab location.  To do this, set TabStop to TRUE for the first option button in the group and set TabStop to FALSE for the other option buttons in the group.

Data Type

Integer (Boolean)


Tag Property

See Also

Description

Stores any extra data needed for your program.  Unlike other properties, the value of the Tag property is not used by Visual C++; you can use this property to identify objects.

Usage

pControl->GetStrProperty("Tag")

pControl->SetStrProperty("Tag", strValue)

Remarks

By default, the Tag property is set to an empty string ("").

You can use this property to assign an identification string to an object without affecting any of its other property settings or causing side effects.  The Tag property is useful when you need to check the identity of a control that is passed as a variable to a procedure.

Data Type

String


See Also

Help:

CtlName Property


Text Property

Description

Determines the text contained in the edit area.

Usage

pControl->GetStrProperty("Text")

pControl->SetStrProperty("Text", strValue)

Remarks

At design time only, the defaults for the Text property are:

The control's CtlName property.

Data Type

String


Visible Property

Description

Determines whether an object is visible or hidden.

Usage

pControl->GetNumProperty("Visible")

pControl->SetNumProperty("Visible", bVisible)

Setting

The Visible property settings are:

Setting	Description

TRUE	(Default) Object is visible.

FALSE	Object is hidden.

Remarks

To hide a control at startup, set the Visible property to FALSE at design time.  Setting this property in code allows you to hide and later redisplay a control at run time in response to a particular event.

Note	In Visual C++ it is more efficient to use CWnd::IsWindowVisible to determine if the control is visible and to use CWnd::ShowWindow({SW_SHOWNA|SW_HIDE}) to set this property.

Data Type

Integer (Boolean)


Parent Property

Description

Specifies the form on which a control is located; not available at design time and read-only at run time.

Usage

pControl->GetNumProperty("Parent")

Remarks

This returns the HWND of the parent of the control.

Note	In Visual C++ it is more efficient to use CWnd::GetParent to get a CWnd pointer to the parent or ::GetParent(GetSafeHwnd) to get an HWND to the parent.

Data Type

HWND


BackColor, ForeColor Properties

Description

BackColor [image] determines the background color of an object.

ForeColor [image] determines the foreground color used to display text and graphics in an object.

Usage

pControl->GetNumProperty("BackColor")

pControl->SetNumProperty("BackColor", rgbColor)

pControl->GetNumProperty("ForeColor")

pControl->SetNumProperty("ForeColor", rgbColor)

Remarks

Visual Basic uses the Microsoft Windows environment RGB scheme for colors.  Each property has the following ranges of settings:

Range of settings	Description

Normal RGB colors	Colors specified by using the Color palette, or by using the RGB macro in code.

System default colors	Colors specified by using the MAKESYSCOLOR() macro function using the system colors specified in WINDOWS.H (e.g. MAKESYSCOLOR(COLOR_WINDOW)).  The Windows environment substitutes the user's choices as specified in the user's Control Panel settings.

For all controls, the default settings at design time are:

BackColor [image] set to the COLOR_WINDOW system default color.

ForeColor [image] set to the COLOR_WINDOWTEXT system color.

The valid range for a normal RGB color is 0 to 16,777,215 (&HFFFFFF).  The high byte of a number in this range equals 0; the lower three bytes, from least to most significant byte, determine the amount of red, green, and blue, respectively.  The red, green, and blue components are each represented by a number between 0 and 255 (&HFF).  If the high byte is not 0, Visual Basic uses the system colors, as defined in the user's Control Panel and enumerated in CONSTANT.TXT.

To display text in the Windows environment, both the text and background colors must be solid.  If the text or background colors you've selected are not displayed, one of the selected colors may be dithered [image] that is, comprised of up to three different-colored pixels.  If you choose a dithered color for either the text or background, the nearest solid color will be substituted.


hWnd Property

Description

Specifies a handle to a control.  Not available at design time; read-only at run time.

Usage

pControl->GetNumProperty("hWnd")

Remarks

The Windows environment identifies each control in an application by assigning it a handle, or hWnd.  The hWnd is used with Windows API calls.  Many Windows environment functions require the hWnd of the current window as an argument.

Note	In Visual C++ it is more efficient to use pControl->GetSafeHwnd().  This method can be used even with control that do not support the hWnd property.  Because the value of this property can change while a program is running, never store the hWnd value in a variable.

Data Type

Integer


FontName Property

See Also

Description

Determines the font used to display text in a control.

Usage

pControl->GetStrProperty("FontName")

pControl->SetStrProperty("FontName", strFont)

Remarks

The default for this property is determined by the system.  Available fonts vary according to your system configuration, display devices, and printing devices.  Font-related properties can be set only to values for which fonts exist.

In general, you should change FontName before setting size and style attributes with the FontSize, FontBold, FontItalic, FontStrikethru, and FontUnderline properties.

On systems running Windows 3.0, the fonts "Helv" or "Tms Rmn" are called "MS Sans Serif" and "MS Serif", respectively.  In code, if you set FontName to "Helv," then test whether the FontName is set to "Helv," the result would be FALSE, since it will have been changed internally to "MS Sans Serif."

Data Type

String


See Also

Help:

FontBold, FontItalic, FontStrikethru, FontUnderline Properties

FontSize Property


FontSize Property

See Also

Description

Determines the size of the font to be used for text displayed in a control.

Usage

pControl->GetFloatProperty("FontSize")

pControl->SetFloatProperty("FontSize", fSize)

Remarks

Use this property to format text in the font size you want.  The default is determined by the system.  To change the default, specify the size of the font in points.

The maximum value for FontSize is 2048 points.

Note	Available fonts vary according to your system configuration, display devices, and printing devices.  Font-related properties can be set only to values for which fonts exist..

In general, you should change the FontName property setting before you set size and style attributes with the FontSize, FontBold, FontItalic, FontStrikethru, and FontUnderline properties.  One exception to this is when you need to set font sizes less than 8 pt with TrueType fonts.  In this case, you should set the point size with the FontSize property, then set the FontName property, and finally, set the size again with the FontSize property.  The reason is that Windows actually uses a different font for TrueType fonts that are less than 8 points in size.

Data Type

Single


See Also

Help:

FontBold, FontItalic, FontStrikethru, FontUnderline Properties


Index Property

Remarks

This property is only supported in Visual Basic.  It is not available in Visual C++.


MousePointer Property

Description

Determines the type of mouse pointer displayed when the mouse is over this control at run time.

Usage

pControl->GetNumProperty("MousePointer")

pControl->SetNumProperty("MousePointer", nSetting)

Setting

The MousePointer property settings are:

Setting	Description

0	(Default) Shape determined by the control

1	Arrow

2	Cross (cross-hair pointer)

3	I-Beam

4	Icon (small square within a square)

5	Size (four-pointed arrow pointing north, south, east, west)

6	Size NE SW (double arrow pointing northeast and southwest)

7	Size N S (double arrow pointing north and south)

8	Size NW SE (double arrow pointing northwest and southeast)

9	Size W E (double arrow pointing west and east)

10	Up Arrow

11	Hourglass (wait)

12	No Drop

Remarks

The MousePointer property controls the shape of the mouse pointer.  This property is useful when you want to indicate changes in functionality as the mouse pointer passes over controls.  The Hourglass setting (11) is useful for indicating that the user should wait for a process or operation to finish.

Data Type

Integer


Height, Width Properties

See Also

Description

Determine the dimensions of an object.

Usage

pControl->GetNumProperty("Height")

pControl->GetNumProperty("Height", nHeight)

pControl->GetNumProperty("Width")

pControl->GetNumProperty("Width", nWidth)

Remarks

These values are in pixels and are the external dimensions of the control.

The values for these properties change as the object is sized by the user or by code.  Maximum limits of these properties for all objects are system-dependent.

Use the Height, Width, Left, and Top properties for operations or calculations based on an object's total area, such as sizing or moving the object.

Data Type

Integer


See Also

Help:

Left, Top Properties


Name Property
CtlName Property

See Also

Description

In Visual Basic, CtlName specifies the name used in code to identify an object.  In Visual C++, however, the CVBControl pointer (or hWnd) is used to identify a control.  The CtlName property has no special meaning at runtime.

Although this property is not directly editable in App Studio, when the ID of the control is defined, the CtlName property is set to the symbol of the ID.  For example, if a grid control were defined as IDC_GRID1, the CtlName would also be IDC_GRID1.

Usage

pControl->GetStrProperty("CtlName")

pControl->SetStrProperty("CtlName", strValue)

Data Type

String


See Also

Help:

Index Property

Text Property


Grid Control

Properties	Events	Errors

Description

A grid control displays a series of rows and columns.  The intersection of a row and column is a cell.  You can read and set the contents of each cell programmatically.

Remarks

You can put either text or a picture in any cell of a grid.

The Row and Col properties specify the current cell in a grid.  You can specify the current cell in code, or the user can change it at run time using the mouse or the arrow keys.  The Text property references the contents of the current cell.

If a cell's text is too long to be displayed in the cell, the text wraps to the next in within the same cell.  To display the wrapped text, you must increase the cell's column width (ColWidth property) or row height (RowHeight property).

Note	When distributing your application, you should install the GRID.VBX file in the user's Microsoft Windows \SYSTEM subdirectory.


Grid Control Properties

BackColor	FontItalic	Rows

BorderStyle	FontName	ScrollBars

CellSelected	FontSize	SelEndCol

Clip	FontStrikethru	SelEndRow

Col	FontUnderline	SelStartCol

ColAlignment	ForeColor	SelStartRow

Cols	GridLines	TabIndex

ColWidth	Height	TabStop

CtlName	HighLight	Text

DragIcon	hWnd	Tag

DragMode	Index	Top

Enabled	Left	TopRow

LeftCol	Visible

FixedAlignment	Parent	Width

FixedCols	Picture

FixedRows	Row

FontBold	RowHeight


Grid Control Events

Click	KeyDown	MouseMove

DblClick	KeyPress	MouseUp

DragDrop	KeyUp	RowColChange

DragOver	LostFocus	SelChange

GotFocus	MouseDown


Grid Control Errors

The following error codes may be returned by the Grid control to CVBControl::m_nError.  For additional information, see CVBControl::m_nError Values.

30000: Cannot use RemoveItem on a fixed row

30001: Cannot use AddItem on a fixed row

30002: Grid does not contain that row

30004:  Invalid column number for alignment

30005:  Invalid alignment value

30006: Unable to allocate memory for grid

30008: Not a valid picture type

30009: Invalid row value

30010: Invalid column value

30011: Unable to register the memory manager

30013: Invalid row height value

30014: Invalid column width value

30015: Cannot remove last non-fixed row

30016: FixedRows must be one less than Rows value

30017: FixedCols must be one less than Cols value

30018: Rows must be one more than FixedRows value

30019: Cols must be one more than FixedCols value


CellSelected Property
Grid Control CellSelected Property

See Also

Description

Determines whether the cell specified by the Col and Row properties (the active cell) is within the grid's selected region.  Not available at design time; read-only at run time.

Usage

pGrid->GetNumProperty("CellSelected")

Setting

The CellSelected property settings are:

Setting 	Description

TRUE 	The specified cell is within the selected area.

FALSE	The specified cell is not within the selected area.

Remarks

This property allows you to determine whether the user has selected a specific cell or a specific range of cells.

At run time, the user can select a range of cells by clicking a cell and then dragging the mouse or by pressing the Shift key and using the arrow keys.

Use the SelEndCol, SelStartCol, SelEndRow, SelStartRow properties to determine the selected region.  Use the Col and Row properties to determine the active cell.

Data Type

Integer (Boolean)


See Also

Help:

Col, Row Properties

RowColChange Event

SelChange Event

SelEndCol, SelStartCol, SelEndRow, SelStartRow Properties


Clip Property
Grid Control Clip Property

See Also

Description

Determines the contents of the cells in a grid's selected region.  Not available at design time.

Usage

pGrid->GetStrProperty("Clip")

pGrid->SetStrProperty("Clip", strData )

Remarks

The strData string can contain the contents of multiple rows and columns.  In strData, a tab character '\t' indicates a new cell in a row, and a carriage return '\r' indicates the beginning of a new row.  For example, this line of code puts text into a selected area two rows high and two columns wide:

pGrid->SetStrProperty("Clip", "1st\ta\r2nd\tb");

When placing data into a grid, only the selected cells are affected. If there are more cells in the selected region than are described in strData, the remaining cells are set to null. If there are more cells described in strData than in the selected region, the unused portion of strData is ignored.

Data Type

String


See Also

Help:

SelEndCol, SelStartCol, SelEndRow, SelStartRow Properties

Text Property


Col, Row Properties
Grid Control Col, Row Properties

See Also

Description

Determine the active cell in a grid.  Not available at design time.

Usage

pGrid->GetNumProperty("Col")

pGrid->SetNumProperty("Col", nColumn )

pGrid->GetNumProperty("Row")

pGrid->SetNumProperty("Row", nRow )

Remarks

Use these properties to specify a cell in a grid or to find out which row or column contains the active cell in a selected region.  Columns and rows are numbered from zero, beginning at the top for rows and at the left for columns.  Setting these properties at run time does not change which cells are selected.  Use SelEndCol, SelStartCol, SelEndRow, SelStartRow properties to specify a selected region.

Note	The Col, Row properties are not the same as the Cols, Rows properties.

Data Type

Integer


See Also

Help:

Clip Property

Cols, Rows Properties

SelEndCol, SelStartCol, SelEndRow, SelStartRow Properties

Text Property


ColAlignment Property
Grid Control ColAlignment Property

See Also

Description

This is an array that determines the alignment of data in a column.  Not available at design time.

Usage

pGrid->GetNumProperty("ColAlignment", nColumn)

pGrid->SetNumProperty("ColAlignment", nAlignment, nColumn)

Setting

The ColAlignment property settings are:

Setting 	Description

0 	(Default) Left align

1	Right align

2	Center

Remarks

Any column can have an alignment that is different from other columns.  This property does not affect cells in a fixed row.  To set the alignment for a fixed column, use the FixedAlignment property.

Data Type

Integer (Enumerated)


See Also

Help:

FixedAlignment Property


Cols, Rows Properties
Grid Control Cols, Rows Properties

See Also

Description

Determine the total number of columns or rows in a grid.  The minimum is 1.  The maximum number of rows is 2000; the maximum number of columns is 400.

Usage

pGrid->GetNumProperty("Cols")

pGrid->SetNumProperty("Cols", nColumns)

pgrid->GetNumProperty("Rows")

pgrid->SetNumProperty("Rows", nRows)

Remarks

You can use these properties to expand a grid dynamically at run time.  A grid must have at least one non-fixed column and one non-fixed row.

Note	The Cols, Rows properties are not the same as the Col, Row properties.

Data Type

Integer


See Also

Help:

Col, Row Properties


ColWidth Property
Grid Control ColWidth Property

See Also

Description

This array determines the width of the specified column in twips.  Not available at design time.

Usage

pGrid->GetNumProperty("ColWidth", nColumn)

pGrid->SetNumProperty("ColWidth", nWidth, nColumn)

Remarks

You can use this property to set the width of any column at run time.  Users can also change the width of a column by positioning the mouse pointer between columns and then clicking and dragging.

Data Type

Long


See Also

Help:

RowHeight Property


FixedAlignment Property
Grid Control FixedAlignment Property

See Also

Description

This array determines the alignment of data in the fixed cells of a column.  Not available at design time.

Usage

pGrid->GetNumProperty("FixedAlignment", nColumn)

pGrid->SetNumProperty("FixedAlignment", nAlignment, nColumn)

Setting

The FixedAlignment property settings are:

Setting 	Description

0 	(Default) Left align.

1	Right align.

2	Center.

3	Use the ColAlignment setting for the column specified by the column index.

Remarks

This property operates like the ColAlignment property except that it only affects the alignment of fixed cells.  This is useful when you want to align headings differently from the rest of the columns.

Data Type

Integer (Enumerated)


See Also

Help:

ColAlignment Property


FixedCols, FixedRows Properties
Grid Control FixedCols, FixedRows Properties

See Also

Description

Determine the total number of fixed columns or fixed rows for a grid.  By default, a grid has one fixed column and one fixed row.

Usage

pGrid->GetNumProperty("FixedCols")

pGrid->SetNumProperty("FixedCols", nFixedCols)

pgrid->GetNumProperty("FixedRows")

pgrid->SetNumProperty("FixedRows", nFixedRows)

Remarks

A fixed column is a stationary column on the left side of the grid. A fixed row is a stationary row along the top of the grid. You can have zero or more fixed columns and zero or more fixed rows.  Fixed columns and rows are displayed in gray and do not move when the other columns or rows in the grid are scrolled. You cannot change the color of a fixed column or row.

Fixed columns and rows are typically used in spreadsheet applications to display row numbers and column names or letters.

A grid must have at least one non-fixed column and one non-fixed row.  The maximum number of fixed columns or rows allowed in a grid is one less than the total number of columns or rows.

Data Type

Integer


See Also

Help:

Cols, Rows Properties

FixedAlignment Property


GridLines Property
Grid Control GridLines Property

See Also

Description

Determines whether the lines between cells are visible.

Usage

pGrid->GetNumProperty("Gridlines")

pGrid->SetNumProperty("Gridlines", {TRUE|FALSE})

Setting

The GridLines property settings are:

Setting 	Description

TRUE 	(Default) Grid lines are visible.

FALSE	Grid lines are not visible.

Data Type

Integer (Boolean)


See Also

Help:

Cols, Rows Properties


HighLight Property
Grid Control HighLight Property

See Also

Description

Determines whether selected cells appear highlighted.

Usage

pGrid->GetNumProperty("Highlight")

pGrid->SetNumProperty("Highlight", {TRUE|FALSE})

Setting

The HighLight property settings are:

Setting 	Description

TRUE 	(Default) Selected cells are highlighted.

FALSE	Selected cells are not highlighted.

Remarks

When this property is set to FALSE and the user selects a range of cells, there is no visual cue that shows which cells are currently selected.

Data Type

Integer (Boolean)


See Also

Help:

Col, Row Properties


LeftCol Property
Grid Control LeftCol Property

See Also

Description

Determines the leftmost visible column (other than a fixed column) in the grid.  Not available at design time.

Usage

pGrid->GetNumProperty("LeftCol")

pGrid->SetNumProperty("LeftCol", nLeftColumn)

Remarks

You can use this property in code to scroll a grid programmatically.  Use the TopRow property to determine the topmost visible row in the grid.

Data Type

Integer


See Also

Help:

Col, Row Properties

TopRow Property


Picture Property
Grid Control Picture Property

Description

Sets or gets the picture property for the current cell of the grid.

Usage

pControl->GetPictureProperty("Picture")

pControl->SetPictureProperty("Picture", hPic)

Setting

hPic is a handle to a picture that has either been obtained from another Picture property or from the AfxSetPict function.  For additional information, see How to Manage VBX Picture Properties in Technical Note 27.

Data Type

HPIC


RowHeight Property
Grid Control RowHeight Property

See Also

Description

This array determines the height of the specified row in twips.  Not available at design time.

Usage

pGrid->GetNumProperty("RowHeight", nRow)

pGrid->SetNumProperty("RowHeight", nRowHeight, nRow)

Remarks

The minimum RowHeight is 1 pixel.  Users can change the RowHeight of any row at run time by placing the mouse pointer on a gridline between rows and then clicking and dragging.

Data Type

Long


See Also

Help:

ColWidth Property

Row Property


SelEndCol, SelStartCol, SelEndRow, SelStartRow Properties
Grid Control SelEndCol, SelStartCol, SelEndRow, SelStartRow Properties

See Also

Description

Determine the starting or ending row or column for a range of cells.  Not available at design time.

SelEndCol [image] the last selected column on the right.

SelStartCol [image] the first selected column on the left.

SelEndRow [image] the lowest selected row.

SelStartRow [image] the first selected row.

Usage

pGrid->GetNumProperty("SelEndCol")

pGrid->SetNumProperty("SelEndCol", nEndColumn)

pGrid->GetNumProperty("SelStartCol")

pGrid->SetNumProperty("SelStartCol", nStartColumn)

pGrid->GetNumProperty("SelEndRow")

pGrid->SetNumProperty("SelEndRow", nEndRow)

pGrid->GetNumProperty("SelStartRow")

pGrid->SetNumProperty("SelStartRow", nStartRow)

Remarks

You can use these properties to select a specific region of the grid from code, or to read into code the dimensions of an area that the user selects.

SelStartCol and SelStartRow together specify the cell in the upper-left corner of a selected range.  SelEndCol and SelEndRow specify the cell in the lower-right corner of a selected range.

To specify a cell without moving the current selection, use the Col and Row properties.

Data Type

Integer


See Also

Help:

Col, Row Properties


ScrollBars Property
Grid Control ScrollBars Property

Description

Specifies whether an object has horizontal or vertical scroll bars; read-only at run time.

Usage

pGrid->GetNumProperty("ScrollBars")

Setting

The ScrollBars property settings are:

Setting	Description

0	(Default) None

1	Horizontal

2	Vertical

3	Both

Remarks

Scroll bars appear on a Grid only if its contents extend beyond its borders.  For example, a vertical scroll bar appears on a grid control when it can't display all of its rows.

Data Type

Integer (Enumerated)


TopRow Property
Grid Control TopRow Property

See Also

Description

Determines the uppermost row displayed in the grid.  Not available at design time.

Usage

pGrid->GetNumProperty("TopRow")

pGrid->SetNumProperty("TopRow", nTopRow)

Remarks

You can use this property in code to programmatically read or set the visible top row of the grid.  Use the LeftCol property to determine the leftmost visible column in the grid.

When setting this property, the largest possible row number is the total number of rows minus the number of rows that can be visible in the grid.  Attempting to set TopRow to a greater row number will generate an error.

Data Type

Integer


See Also

Help:

Col, Row Properties

LeftCol Property


SelChange Event
Grid Control SelChange Event

See Also

Description

Occurs when the selected range changes to a different cell or range of cells.

Syntax

void CMyDialog::OnGridSelChange(UINT, int, CWnd*, LPVOID)

Remarks

The SelChange event occurs whenever the user clicks a cell other than the selected cell and as a user drags to select a new range of cells.  A user can also select a range of cells by pressing the Shift key and using the arrow keys.

You can trigger this event in code by changing the selected region using the SelEndCol, SelStartCol, SelEndRow, SelStartRow properties.

The RowColChange event also occurs when a user clicks a new cell but does not occur while a user drags the selection across the grid, or when you programmatically change the selection without moving the active cell.


See Also

Help:

RowColChange Event

SelEndCol, SelStartCol, SelEndRow, SelStartRow Properties


RowColChange Event
Grid Control RowColChange Event

See Also

Description

Occurs when the currently active cell changes to a different cell.

Syntax

void CMyDialog::OnGridRowColChange(UINT, int, CWnd*, LPVOID)

Remarks

This event occurs whenever the user clicks a cell other than the selected cell or when you programmatically change the active cell within a selection.

You can trigger this event in code by changing the current cell using the Col and Row properties.

The SelChange event also occurs when a user clicks a new cell, but does not occur when you programmatically change the selected range without changing the active cell.


See Also

Help:

SelChange Event


Cannot remove last non-fixed row

A grid must contain at least one row.


Cannot use AddItem on a fixed row

You cannot insert a fixed row in a grid. Use the FixedRows property to set the number of fixed rows in a grid.


Cannot use RemoveItem on a fixed row

You cannot delete a fixed row from a grid.  The number of fixed rows in a grid is determined by the FixedRows property.


Cols must be one more than FixedCols value

There must be at least one non-fixed row and one non-fixed column.  FixedCols or FixedRows cannot be equal or greater than Cols or Rows, respectively.


FixedCols must be one less than Cols value

The number of columns assigned as Fixed in a grid must be one less than the number of columns.


FixedRows must be one less than Rows value

The number of Rows assigned as fixed in a grid must be one less than the number of rows.


Grid does not contain that row

You have tried to access a row that does not exist. The first row in a grid is zero. Therefore, the last row in a grid is the number of rows minus one. Use the Rows property to set or return the number of rows in a grid.


Invalid alignment value

The valid values for the ColAlignment and FixedAlignment properties are 0 [image] Left Justify, 1 [image] Right Justify, and 2 [image] center. FixedAlignment has an additional setting: 3 [image] Use ColAlignment Value.


Invalid column number for alignment

You cannot align a fixed column with ColAlignment; use the FixedAlignment property. Set the index argument of the ColAlignment property to a column that is not a fixed column. The FixedCols property returns the number of fixed columns in a grid.


Invalid column value

You have attempted to access a column that does not exist. The first column in a grid is zero. Therefore, the last column in a grid is the number of columns minus one. Use the Cols property to set or return the number of columns in a grid.


Invalid column width value

ColWidth must be greater than or equal to one.


Invalid row height value

RowHeight must be greater than or equal to one.


Invalid row value

You have attempted to access a row that does not exist. The first row in a grid is zero. Therefore, the last row in a grid is the number of rows minus one. Use the Rows property to set or return the number of rows in a grid.


Not a valid picture type

You cannot display a Windows metafile (.WMF) in a grid cell. The Picture property supports only bitmaps (.BMP) and icons (.ICO).


Rows must be one more than FixedRows value

The number of Rows assigned in a grid must be one more than the number of fixed rows.


Unable to allocate memory for grid

You are getting low on memory.  Close some unused applications or documents.


Unable to register the memory manager

There was insufficient memory to store the requested information in the grid.