SDK Subclassing source files
Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular window before the window has a chance to process them. This is typically done by replacing the Window Procedure for a window with application-defined window procedure. I will devide this article into 3:
- Subclassing in SDK programs,
- Subclassing in MFC programs,
- Reflected messages
Sometimes you want to take the functionality of a control and modify it slightly. One example is replacing the menu in an edit control, Another is adding a context menu when you press on a button. One of the most common questions I encounter is "How do I screen out characters from an edit control?". I will show the solution to this from an MFC approach and from an SDK approach, while I try to explain Subclassing.
The need for subclassing comes from the fact that the code for the Windows controls is within Windows, meaning you cannot edit the code. Although you cannot edit the code of the control itself, you intercept the messages sent to it, and handle them your self. You do so by subclassing the control. Subclassing involves replacing the Message Handlers of the control, and passing any unprocessed message to the controls Message Handler.
Although the Message Procedure for the control is located within windows, you can retrieve a pointer to it by using the
GetWindowLong function with the
GWL_WNDPROC identifier. Likewise, you can call
SetWindowLong and specify a new Window Procedure for the control. This process is called Subclassing, and allows you to hook into a window/control and intercept any message it gets. Subclassing is the Windows term for replacing the Window Procedure of a window with a different Window Procedure and calling the old Window Procedure for default (superclass) functionality. Remember
DefWindowProc()? instead of calling
DefWindowProc for default Message Handling you use the old Window Procedure as the default Message Handler.
Implementing SDK Subclassing
So, lets try to solve the classic question "How do I screen out characters from an edit control?", or "How do I create a letter-only edit control?"
First lets analyze how an edit control works:
An edit control is a window. It's window procedure lies within windows. Among other things, whenever it gets a
WM_CHAR message it adds the character to the text it contains. Now that we know that, we can simply subclass the edit control, and intercept the
WM_CHAR messages. Whenever the
WM_CHAR message is a letter or a key like space bar or backspace we'll pass the message to the edit control. If it isn't one of the above, we'll just "Swallow" the message, blocking it from reaching the Edit Control.
The first step to subclassing is to add a global/static
WNDPROC variable that will store the address of the edit control's Window Procedure.
The second step is to create a new Window Procedure for the edit control:
LRESULT CALLBACK NewEditProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
chCharCode = (TCHAR) wParam;
if(chCharCode > 0x20 && !IsCharAlpha(chCharCode))
return CallWindowProc (g_OldEdit, hwnd, message, wParam, lParam);
IsCharAlpha function determines whether a character is an alphabetic character. This determination is based on the semantics of the language selected by the user during setup or by using Control Panel.
More interesting is the
CallWindowProc function. The
CallWindowProc function passes message information to the specified Window Procedure. A call to
CallWindowProc will allow you to call the old Window Procedure with any message you receive, thus providing default message handling
The third step is to replace the Window Procedure for the edit control, and to store the old one in g_OldEdit. For example, if you want to subclass an edit control that resides in a dialog (hDlg) and has the ID of
IDC_EDIT1 you would use the following code:
hwnd hWndEdit = GetDlgItem(hDlg, IDC_EDIT1);
g_OldEdit = (WNDPROC)SetWindowLong(hWndEdit, GWL_WNDPROC, (LONG)NewEditProc);
The control is now subclassed. Any message to the edit control will first go through the
NewEditProc Window Procedure, which will decide if the message should go to the edit control's Window Procedure .
Subclassing in both MFC and SDK programs is done by replacing the message handlers of a control. It is rather easy to subclass in a MFC program. First you inherit your class from a class that encapsulates the functionality of a the control. In ClassWizard, click on "Add Class", then "New". For the base class, choose the MFC Control class you are deriving from, in our case,
Using MFC relieves you from having to call the old Message Handlers, since MFC will take care of it for you.
The second step is to add Message Handlers to your new class. If you handle a message and you want the control's message handler to get a shot at it, you should call the base class member function the corresponds with the message. this is the subclassed
void CLetterEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
if(nChar <= 0x20 || IsCharAlpha((TCHAR)nChar))
CEdit::OnChar(nChar, nRepCnt, nFlags);
The third and final stage is to associate the window with an instance of our new class. In a dialog this is done simply by using ClassWizard to create a control member variable of your class in the window's parent, and associate it with the control.
In a non-dialog parent you should add a member variable instance of your control to it, and call one of the two
CWnd Subclassing functions:
CWnd::SubclassWindow or CWnd::SubclassDlgItem. Both routines attach a
CWnd object to an existing Windows
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.
For a more in depth treatment of MFC subclassing see Chris Maunder's article: "Create your own controls - the art of subclassing".
Reflected Messages - MFC 4.0+
When you subclass a control, besides handling the message it receives, in MFC you can also handle the notifications it sends to it's parent window. This technique is called Message Reflecting. Windows controls often send notifications to their parents, for example, a Button will send a
WM_COMMAND message telling it's parent it has been clicked. Usually it is the parent's job to handle these messages, but MFC will also allow you to handle them in the control itself. In ClassWizard these messages appear with an "=" sign before them, indicating they are Reflected Messages. You handle them just like any other message. The macros for these messages are similar to the regular messages, but have
_REFLECT added to the end of the macro. For example,
For a more in depth treatment of MFC reflected messages see the MFC technical notes:
- TN014: Custom Controls
- TN062: Message Reflection for Windows Controls