Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Windows 95 Subclassing and Superclassing


SP 96: Windows 95 Subclassing and Superclassing

Jeff and Jon are coauthors of Windows 95: A Developer's Guide (M&T Books, 1995). They can be contacted through the DDJ offices.


Windows supplies a number of ready-to-use window classes, including listboxes, comboboxes, and scrollbars. While these controls are intended to be feature laden and general enough for use in any application, there may be times when you require slightly different behavior from controls. Although you could design your own control from scratch, both window subclassing and superclassing let you inherit features and styles from one or more windows. However, the form you use is dictated by your particular requirements. For example, if you're creating a single control that inherits from a Windows control, then you'll likely use subclassing to create your new control. If, on the other hand, you plan to create several controls that inherit similar features from a common class, you may want to use superclassing. In this case, superclassing reduces your programming effort while producing smaller code in your application.

How Window Subclassing Works

When registering a window class, you fill in the members of the WNDCLASSEX structure and pass the address of the structure to the RegisterClassEx function. Of course, before calling RegisterClassEx, you must initialize the lpfnWndProc member of the WNDCLASSEX structure to point to the address of the class's window procedure. This procedure processes messages pertaining to all windows of this class. Before RegisterClassEx returns, the system allocates an internal block of memory that contains information about the window class. The class's window procedure address is part of this block.

Whenever a new window is created, the system allocates another internal block of memory containing information specific to the new window. When the system allocates and initializes the block of memory for this window, the system copies the class's window procedure address from the class's internal memory block into the window's internal memory block. When a message is dispatched to a window procedure, the system examines the value of the window procedure in the window's memory block and calls the function whose address is stored there. The system does not use the window procedure address stored in the window class's memory block when dispatching a message to a window. The window procedure address stored in the class's memory block is there only so that it can be copied when a window is created--the system does not use this address for any other purpose.

To subclass a window, you change the window procedure address in the window's memory block to point to a new window procedure. Because the address is changed in one window's memory block, it does not affect any other windows created from the same class. If the system did not give each window instance its own copy of the window procedure address, changing the class's address would alter the behavior of all windows in the class. If this were the case, subclassing a single edit control so that it would no longer accept digits also would cause all edit controls used within a single process to stop accepting digits. This certainly is not desirable.

Once you have changed the window procedure address in the window's memory block, all messages destined for the window are sent to the function at this new address. This function must look exactly like a standard window procedure. In other words, its prototype must have an identical prototype.

Once the message destined for the original window procedure has been sent to your procedure, you can:

  • Pass it to the original procedure. This option is used for most messages. The reason for subclassing is usually to alter the behavior of a window only slightly. For this reason, most messages are passed to the original procedure so that the default behavior for this window class can be performed.
  • Stop the message from being passed to the original procedure. For example, if you want an edit control to stop accepting digits, you examine WM_CHAR messages, check to see if the character (wParam parameter) is between 0 and 9, and, if it is, immediately return from the SubClassWndProc function. If the character is not a digit, you pass the message to the original window procedure for edit controls.
  • Alter the message before sending it. If you want a combobox to accept only uppercase characters, examine each WM_CHAR message and convert the key (contained in wParam) to uppercase (using the CharUpper function) before passing the message to the original procedure.
In
Example 1, which shows how to subclass a window, the only information required to subclass a window is its window handle. Notice that we have used the SubclassWindow macro to perform the window subclassing. This macro is defined in WindowsX.h; see Example 2. You can see that, aside from some casting, this macro simply calls SetWindowLong, changing the address of the window's window procedure. Of course, this means that the function address passed to SubclassWindow must identify a function that is contained in the process's address space. In Win32, it is not possible to subclass a window using a window procedure that is contained in another process's address space. The return value from SetWindowLong is the address of the original window procedure. This window procedure is saved in a window property that is associated with the subclassed window.

The function you write to intercept messages is identical in form to a window procedure. The only difference is that you pass the messages to the original window procedure instead of calling DefWindowProc. To have the original window perform its normal operations for a particular message, you must use the CallWindowProc function. This function is passed to the address of the original window procedure, the window handle (hwnd), the message (uMsg), and the two standard parameters (wParam and lParam).

Creating New Window Messages

When you subclass a window, you often want to define some new window messages that can be sent to the window. Let's say that you create an edit window that accepts only numbers (using the new ES_NUMBER window style). Then, you subclass this edit window to alter its behavior slightly. For example, you might want to define a new window message, EM_ADDNUM, that you can send to your subclassed window to make adding a value to the number shown in the subclassed edit window very easy. How should you define this EM_ADDNUM message? Normally, if you were implementing a window class entirely by yourself, you would define your class-specific window messages starting with WM_USER. For example, the EM_ADDNUM message might be defined as #define EM_ADDNUM (WM_USER + 0).

But, when you are subclassing a window that was originally created from a class that you did not implement yourself, you cannot simply create new window messages starting at WM_USER. All the new common controls, like trackbars, have their class-specific messages starting at WM_USER. For example, the trackbar class's TBM_GETPOS message is defined as #define TBM_GETPOS (WM_USER).

If you subclass a trackbar window and define your own message with a value of WM_USER + 0, your subclass procedure intercepts the message and processes the message your way. Any code that is expecting to send the TBM_GETPOS message in order to retrieve size information will be in for a big surprise!

So, you can start defining your messages at WM_USER + 500. But, this isn't really a safe approach because you don't know if Microsoft has created any undocumented (WM_USER + x) messages specific for the control. If you add your own user message starting at WM_USER + 500, it could conflict with an undocumented message recognized by the trackbar class. Actually, choosing any value would be tempting fate.

Fortunately, there are two ways to solve this problem. The first is to use the RegisterWindowMessage function, which creates a new message that is unique as long as the string we pass to it is also unique. Whenever you call RegisterWindowMessage, you know that the integer value returned identifies a message that is ignored by any and all window procedures that have not also requested to register the same string message.

The second way to solve the problem is much easier. Starting with Windows 95, Microsoft has declared that all third-party window-class procedures must ignore messages that range from WM_APP to 0xBFFF, inclusive where WM_APP is defined in WinUser.H as #define WM_APP 0x8000.

This means Microsoft guarantees that all system-global window classes and all new common controls will ignore messages in this range. It also means other companies that produce controls also should ignore messages in this range. You should definitely check with the vendor of any controls you use to make sure that they do not process any messages above WM_APP.

Because these classes ignore messages in this range, you can safely define your EM_ADDNUM message for our subclassed edit window as #define EM_ADDNUM (WM_APP + 0). Now, when you send this message to a subclassed edit window, it will know, beyond a shadow of a doubt, that you want it to add a number to the value shown in the edit control.

Associating Additional Data with a Subclassed Window

Another problem with subclassing windows is that associating additional data with the window is difficult. For example, you might like to associate a valid numeric range with our subclassed edit window. Because you did not register the edit window class, you have no way to add additional class and/or window extra bytes. In addition, because subclassing requires that a window instance already exists before you can subclass it, increasing the number of class or window extra bytes is also impossible.

The best way to associate data with a subclassed window is to use window properties. You also could use the GWL_USERDATA bytes to store an address of a data structure that contains some additional data, but this is not a good idea. The GWL_USERDATA bytes are supposed to be available for the application that creates and manipulates the window. Since your subclass procedure doesn't create the window, but acts very much like a window procedure, you should not touch the GWL_USERDATA bytes. Window properties are the only robust solution.

Of course, if you know that the users of your subclassed window will not touch the GWL_USERDATA bytes, you can use them, but you sacrifice some flexibility that you may desire in the future.

The NoDigits Application

The NoDigits application (see Listings One through Four) demonstrates how to subclass two edit control windows. When you invoke the application, the main application window appears. This window contains three edit controls and two buttons. The first edit control uses the standard, built-in window procedure. The other two edit controls are subclassed. The subclass procedure intercepts all characters being sent to the edit control. However, if the character is a digit, the subclass window procedure does not call the original window procedure. Instead, it beeps and simply returns.

When NoDigits starts, it creates a dialog box from a template that contains three edit controls and two buttons. When the dialog-box procedure receives a WM_INITDIALOG message, the NoDigits_OnInitDialog function subclasses two of the edit windows by calling the NoDigitsClass_ConvertEdit function.

The NoDigitsClass_ConvertEdit function is implemented in NoDigCls.c (Listing Two) but is prototyped in NoDigits.c (Listing One). This function simply calls the SubclassWindow macro and saves the returned original window procedure as a property associated with the subclassed window.

NoDigitsClass_WndProc is a subclass window procedure that intercepts only a single window message, WM_CHAR. All other messages are passed to the original edit control's window procedure by calling NoDigitsClass_CallOrigWndProc at the end of the function. NoDigitsClass_CallOrigWndProc has the same prototype as any other window procedure, but it calls the CallWindowProc function to forward messages to the original window procedure. CallWindowProc's first parameter is the address of the window procedure you wish to forward the message to. It's easy for NoDigitsClass_WndProc to get the address of the edit control's original window procedure. It does this by calling the GetProp function, passing the same string that was passed to SetProp inside NoDigitsClass_ConvertEdit.

We'll now turn to how digits are prevented from being entered into the edit control after it's subclassed. As mentioned earlier, the subclass function performs some additional processing for the WM_CHAR message only. Whenever NoDigitsClass_WndProc receives a WM_CHAR message, it calls the NoDigitsClass_OnChar function. The first thing this function does is determine if the character passed to it is a digit. If the character is a digit, MessageBeep is called so that the user is given some indication that the window does not accept digits. However, if the character is not a digit, the WM_CHAR message is forwarded to the edit control's original window procedure by using the FORWARD_WM_CHAR macro. The last parameter is the address of the function that you wish to forward the message to, NoDigitsClass_CallOrigWndProc.

How Window Superclassing Works

Window superclassing is similar to window subclassing. Again, you are associating a different window procedure address with a window to alter a single window's behavior slightly. With window superclassing, however, you create a new window class that has altered behavior. Superclassing is most useful if you intend to create several windows, all with slightly altered behavior. (There are some other differences between subclassing and superclassing that will be discussed later.) Creating a superclass is accomplished by registering a new window class. For example, let's say that your application needs to create several edit windows where each of the windows can accept only letters. You could create all the windows and then subclass each individually in order to get the desired effect, or you could use window superclassing.

For window superclassing, you must create a superclass window procedure that is almost identical to a window subclass procedure. The prototype is the same, and the way that you intercept messages is the same. In fact, the only difference is how you call the original window procedure.

Example 3 shows how to create a superclass. Many more steps are necessary to create a superclass than to subclass a window. The process of superclassing a window begins with a call to the GetClassInfoEx function, passing it the name of the desired base class. This fills a WNDCLASSEX structure with statistics regarding the base class. This WNDCLASSEX structure serves as a starting point for the new window class.

Once you have the base class's information, it is very important to save the value of the lpfnWndProc member. This value is the address of the base class window procedure. This variable later will be used in the superclass window procedure as the first parameter to the CallWindowProc function.

The next step is to give the new class a name by setting the lpszClassName member to the new name for the class. The value of the hInstance member should be set to the value of hinstExe that was passed to WinMain, or the value of hinstDll passed to a DLL's DllMain function. This value lets the system know which module (EXE or DLL file image) in the process's address space is registering the new window class. Finally, the lpfnWndProc member of the WNDCLASSEX structure is changed to the address of the superclass window procedure.

Because a new window class is being registered, you can increase the values of the cbClsExtra and cbWndExtra members of the WNDCLASSEX structure. These additional bytes may be used by your superclass function and are a big advantage of superclassing over subclassing. But be careful when using the class or window extra bytes for a superclassed window class. The base class window procedure is written with the assumption that the class extra bytes (from 0 to cbClsExtra-1) and the window extra bytes (from 0 to cbWndExtra-1) are for its own use. The superclass window procedure must not access the class and window extra bytes within these ranges unless it knows exactly how they are used by the base class.

If the superclass window procedure is going to add class and window extra bytes, it must save the original values of the cbClsExtra and cbWndExtra members of the WNDCLASSEX structure, usually in global variables, before changing the values of those members. When the superclass window procedure accesses any of the window extra bytes, it must add the original value of cbWndExtra to the index so that it does not reference the window extra bytes used by the base class. Example 4 shows how to prepare and access additional window bytes added to the superclass. Of course, it is possible to associate data with a superclassed window via window properties. However, it is always better to store information in window extra bytes because properties require more data space and take more time to access.

The lpszMenuName member of WNDCLASSEX also may be changed to give the new class a new menu. If a new menu is used, the IDs for the menu options should correspond to the IDs in the "standard" menu for the base class. This new menu is not necessary if the superclass window procedure processes the WM_COMMAND message in its entirety and does not pass this message to the base class window procedure.

The remaining members of the WNDCLASSEX structure--style, hIcon, hCursor, hbrBackground, and hIconSm--may be changed in any way you desire. For example, if you want your new window class to use a different mouse cursor or a different icon, you can change the hCursor and hIcon members of the WNDCLASSEX structure accordingly. Finally, call the RegisterClassEx function to inform Windows of the new class.

The main difference between subclassing and superclassing is that subclassing alters the behavior of an existing window, while superclassing creates a new window class where all windows created by the class have altered behavior. It is better to use superclassing when you wish to create several windows whose behavior differs slightly from an existing window class. This is because it is easier to register a new class, give it a new name, and create windows of this new class than it is to create all the desired windows and use the SetWindowLong function or SubclassWindow macro to change the address of each of their window procedures.

Superclassing can be used to create a dialog box that contains several superclassed controls. When a dialog box is created, CreateDialogIndirectParam goes through the dialog-box template and creates a window using the parameters specified on each CONTROL line in the template. If the template contains several listbox windows that require altered behavior, it is much easier to specify "NewListBox" in each CONTROL line of the template. With window subclassing, the system would have to create all the listbox windows before you could subclass these windows, one at a time, during the processing of the WM_INITDIALOG message. This is a tedious process.

Another advantage of superclassing is that the superclass window procedure performs its own initialization for the window. This is because Windows knows about the superclass window procedure from the class memory block before a window is created. When the window is created, the superclass window procedure receives the WM_NCCREATE and WM_CREATE messages. During the processing of these messages, the superclass window procedure may initialize its class or window extra bytes or do any other processing it desires.

Both of these messages should be passed to the base class window procedure, whether the superclass window procedure processes them or not. Windows must perform initialization for each window in response to WM_NCCREATE. If this message wasn't passed to the original window procedure, DefWindowProc would never be called, and the window would not be initialized properly. By passing the WM_NCCREATE message to the base class procedure, you ensure that DefWindowProc is eventually called. Similarly, the WM_CREATE message should also be passed to the base class window procedure.

Unfortunately, defining new window messages for superclassed windows involves the same problems that you have for subclassed windows. To define new window messages, you must either use the RegisterWindowMessage function or define your messages starting with WM_APP.

Conclusion

In our book Windows 95: A Developer's Guide, we present the Arcade application which demonstrates how to create an animated button class (AniBtn) by superclassing the standard button class. AniBtn works almost identically to the standard button class except that it takes advantage of the new image list controls to create buttons that have animated icon images instead of boring, old text. Due to space constraints, we can't present a discussion here. However, we have provided the Arcade application electronically (see "Availability," on page 3) and a complete discussion can be found in our book. With the techniques presented here, you should be able to create your own controls that inherit from standard Windows controls.

Example 1: Subclassing a window.

static LPCSTR g_szSubclassWndProcOrig = "SubclassWndProcOrig";
int WINAPI WinMain (HINSTANCE hinstExe, HINSTANCE hinstPrev,
   LPSTR lpszCmdLine, int nCmdShow) {
   HWND hwndEdit = CreateWindowEx(0, "EDIT", "", WS_CHILD, 10, 20,
      100, 16, hWndParent, NULL, hInstance, 0L);
   //Change the window procedure address associated with this one edit
   //control
   pfnWndProcOrig = SubclassWindow(hwndEdit, Edit_SubclassWndProc);
   // Associate the original window procedure address with the window
   SetProp(hwndEdit, g_szSubclassWndProcOrig, (HANDLE) (DWORD)
          pfnWndProcOrig);
   // Messages destined for hwndEdit are sent to Edit_SubclassWndProc
   // instead
}
   .
   .
   .
LRESULT WINAPI Edit_SubclassWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
   LRESULT lResult = 0;
   BOOL fCallOrigWndProc = TRUE;
   switch (uMsg) {
      // Some cases set fCallOrigWndProc to FALSE.
      .
      .
      .
   }
   if (fCallOrigWndProc)
      lResult = CallWindow(
         (WNDPROC) (DWORD) GetProp(hwnd, g_szSubclassWndProcOrig),
         hwnd, uMsg, wParam, lParam);
   return(lResult);
}

Example 2: SubclassWindow macro as #defined in WindowsX.h.

#define SubclassWindow(hwnd, lpfn) \     
    ((WNDPROC)SetWindowLong((hwnd), GWL_WNDPROC, \
    (LPARAM)(WNDPROC)(lpfn)))

Example 3: Creating a superclass.

// Store the address of the original window procedure.
WNDPROC g_pfnWndProcOrig;
    .
    .
    .
int WINAPI WinMain (HINSTANCE hinstExe, HINSTANCE hinstPrev,  
    LPSTR lpszCmdLine, int nCmdShow) {  
    WNDCLASSEX wc;  
    adgINITSTRUCT(wc, TRUE);  
    // Get all the information about the original window class  
    GetClassInfoEx(NULL, "EDIT", &wc);  
    // Save the original window procedure address so that the  
    // Edit_SuperClassWndProc can use it.  
    g_pfnWndProcOrig = wc.lpfnWndProc;  
    // Our new class must have a new class name.  
    wc.lpszClassName = "NoDigits";  
    // Our new class is registered by our module.  
    wc.hInstance = hinstExe;  
    // Our new class has a different window procedure address.  
    wc.lpfnWndProc = NoDigitsClass_SuperClassWndProc;  
    // Register our new window class.  
    RegisterClassEx(&wc);  
    // At this point we can create windows of the NoDigits window class.
    // All messages that go to these windows are sent to the  
    // NoDigitsClass_SuperClassWndProc first where we can decide if we  
    // want to pass them on to g_pfnWndProcOrig.  
        .  
        .  
        .
}
LRESULT WINAPI NoDigitsClass_SuperClassWndProc (HWND hwnd, UINT uMsg,  
    WPARAM wParam, LPARAM lParam) {  
    LRESULT lResult = 0;  
    BOOL fCallOrigWndProc = TRUE;  
    switch (uMsg) {     
        .     
        .     
        .  
    }  
    if (fCallOrigWndProc)     
      lResult = CallWindowProc(g_pfnWndProcOrig,hwnd,uMsg,wParam,lParam);
       return(lResult);
}

Example 4: Preparing and accessing additional window bytes added to the superclass.

// Global variables to save the number of class extra bytes, the window
// extra bytes, and the window procedure address of listbox base class
int g_cbClsExtraOrig, g_cbWndExtraOrig;
WNDPROC g_pfnWndProcOrig;
// Index into window extra bytes where our edit data can be found.
// These data follow the data required by the base class.
#define GWL_NODIGITSDATA      (g_cbWndExtraOrig + 0)
    .
    .
    .
ATOM WINAPI NoDigitsClass_RegisterClass (void) {  
    WNDCLASSEX wc;  
    GetClassInfoEx(NULL, "Edit", &wc);     
    // Save the information we need later in global variables.  
    g_cbClsExtraOrig = wc.cbClsExtra;  
    g_cbWndExtraOrig = wc.cbWndExtra;  
    g_pfnWndProcOrig = wc.lpfnWndProc;  
    // Add four window extra bytes to account for additional edit data.
    wc.cbWndExtra += sizeof(LONG);  
    // Change lpfnWndProc, lpszClassName, and hInstance members, too.
        .  
        .  
        .  
    // Register the new window class.  
    return(RegisterClassEx(&wc));
}
LRESULT WINAPI NoDigitsClass_SuperClassWndProc (HWND hwnd, UINT uMsg,  
    WPARAM wParam, LPARAM lParam) {  
    int nNoDigitsData;  
        .  
        .  
        .  
    // Retrieve our data from the added window extra bytes.  
    nNoDigitsData = GetWindowLong(hwnd, GWL_NODIGITSDATA);  
        .  
        .  
        .  
    // Call base class window procedure for remainder of processing. return(CallWindowProc(g_pfnWndProcOrig,  hwnd, uMsg, wParam, lParam));
}

Listing One

/******************************************************************************
Module name: NoDigits.c
Written by: Jeffrey Richter
Notices: Copyright (c) 1995 Jeffrey Richter
Purpose: Demonstrates how to subclass a window
******************************************************************************/
#include "..\Win95ADG.h"          /* See Appendix A for details */
#include <Windows.h>
#include <WindowsX.h>
#pragma warning(disable: 4001)    /* Single-line comment */
#include "resource.h"
///////////////////////////////////////////////////////////////////////////////
// Function that converts an edit window to a NoDigitsClass window
// by subclassing the edit window
BOOL WINAPI NoDigitsClass_ConvertEdit (HWND hwnd, BOOL fSubclass);
///////////////////////////////////////////////////////////////////////////////
BOOL NoDigits_OnInitDialog (HWND hwnd, HWND hwndFocus, LPARAM lParam) {
   adgSETDLGICONS(hwnd, IDI_SUBCLASS, IDI_SUBCLASS);
   // Turn the regular edit windows into NoDigits windows by subclassing them.
   NoDigitsClass_ConvertEdit(GetDlgItem(hwnd, IDC_NODIGITS1), TRUE);
   NoDigitsClass_ConvertEdit(GetDlgItem(hwnd, IDC_NODIGITS2), TRUE);
   return(TRUE);  // Accepts default focus window.
}
///////////////////////////////////////////////////////////////////////////////
void NoDigits_OnCommand (HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) {
   switch (id) {
      case IDCANCEL:              // Allows dialog box to close.
         // Turn the NoDigits windows back into regular Edit windows.
         NoDigitsClass_ConvertEdit(GetDlgItem(hwnd, IDC_NODIGITS1), FALSE);
         NoDigitsClass_ConvertEdit(GetDlgItem(hwnd, IDC_NODIGITS2), FALSE);
         EndDialog(hwnd, id);
         break;
   }   
}
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI NoDigits_DlgProc (HWND hwnd, UINT uMsg, 
   WPARAM wParam, LPARAM lParam) {
   switch (uMsg) {
      // Standard Window's messages
      adgHANDLE_DLGMSG(hwnd, WM_INITDIALOG, NoDigits_OnInitDialog);
      adgHANDLE_DLGMSG(hwnd, WM_COMMAND,    NoDigits_OnCommand);
   }
   return(FALSE);                 // We didn't process the message.
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hinstExe, HINSTANCE hinstPrev, 
   LPSTR lpszCmdLine, int nCmdShow) {
   adgWARNIFUNICODEUNDERWIN95();
   adgVERIFY(-1 != DialogBox(hinstExe, MAKEINTRESOURCE(IDD_NODIGITS),
      NULL, NoDigits_DlgProc));
   return(0);
}
//////////////////////////////// End of File //////////////////////////////////

Listing Two

/******************************************************************************
Module name: NoDigCls.c
Written by: Jeffrey Richter
Notices: Copyright (c) 1995 Jeffrey Richter
Purpose: NoDigits subclass child control implementation file
******************************************************************************/
#include "..\Win95ADG.h"          /* See Appendix A for details */
#include <Windows.h>
#include <WindowsX.h>
#pragma warning(disable: 4001)    /* Single-line comment */
#include "resource.h"
///////////////////////////////////////////////////////////////////////////////
static LPCTSTR g_szNoDigitsClassWndProcOrig = 
   __TEXT("NoDigitsClassWndProcOrig");
///////////////////////////////////////////////////////////////////////////////
// The sole purpose of this function is to call the base class's window 
// procedure. When using the FORWARD_* message crackers, a function like
// this one is necessary because the last parameter to a FORWARD_* message cracker
// is a function with the standard WNDPROC function prototype, whereas 
// CallWindowProc requires a fifth parameter - the address of the window
// procedure to call.
LRESULT NoDigitsClass_CallOrigWndProc (HWND hwnd, UINT uMsg, 
   WPARAM wParam, LPARAM lParam) {
   // Call the base class's window procedure.  It was saved as a window
   // property by the NoDigitsClass_ConvertEdit function.
   return(CallWindowProc(
     (WNDPROC) (DWORD) GetProp(hwnd, g_szNoDigitsClassWndProcOrig),
     hwnd, uMsg, wParam, lParam));
}
///////////////////////////////////////////////////////////////////////////////
void NoDigitsClass_OnChar (HWND hwnd, TCHAR ch, int cRepeat)  {
   if (adgINRANGE(__TEXT('0'), ch, __TEXT('9'))) {
      // Beep when a digit is received.
      MessageBeep(0);
   } else {
   
      // Allow nondigits to pass through to the original window procedure.
      FORWARD_WM_CHAR(hwnd, ch, cRepeat, NoDigitsClass_CallOrigWndProc);
   }
}
///////////////////////////////////////////////////////////////////////////////
// This function processes all messages sent to the NoDigits windows.
LRESULT WINAPI NoDigitsClass_WndProc (HWND hwnd, UINT uMsg,
   WPARAM wParam, LPARAM lParam) {
   switch (uMsg) {
      HANDLE_MSG(hwnd, WM_CHAR, NoDigitsClass_OnChar);
   }
   return(NoDigitsClass_CallOrigWndProc(hwnd, uMsg, wParam, lParam));
}
///////////////////////////////////////////////////////////////////////////////
// Function that converts an edit window to a NoDigitsClass window
// by subclassing the edit window.
BOOL WINAPI NoDigitsClass_ConvertEdit (HWND hwnd, BOOL fSubclass) {
   BOOL fOk = FALSE;
   if (fSubclass) {
      fOk = SetProp(hwnd, g_szNoDigitsClassWndProcOrig,
        (HANDLE) (DWORD) SubclassWindow(hwnd, NoDigitsClass_WndProc));
   } else {
      WNDPROC wp = (WNDPROC) (DWORD) 
         RemoveProp(hwnd, g_szNoDigitsClassWndProcOrig);
     SubclassWindow(hwnd, wp);
     fOk = (wp != NULL);      
   }
   return(fOk);
}

//////////////////////////////// End of File //////////////////////////////////

Listing Three

//Microsoft Visual C++ generated resource script. 
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Icon
//
IDI_SUBCLASS            ICON    DISCARDABLE     "NoDigits.Ico"
/////////////////////////////////////////////////////////////////////////////
// Dialog
//
IDD_SUBCLASS DIALOG DISCARDABLE  -32768, 5, 192, 79
STYLE WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "No Digits"
FONT 8, "MS Sans Serif"
BEGIN
    LTEXT           "&Edit:",IDC_STATIC,7,8,15,8
    EDITTEXT        IDC_EDIT,43,8,140,13,ES_AUTOHSCROLL
    LTEXT           "NoDigit &1:",IDC_STATIC,7,24,34,8
    EDITTEXT        IDC_NODIGITS1,43,24,140,13,ES_AUTOHSCROLL
    LTEXT           "NoDigit &2:",IDC_STATIC,7,40,34,8
    EDITTEXT        IDC_NODIGITS2,43,40,140,13,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "OK",1,36,60,50,14
    PUSHBUTTON      "Cancel",2,104,60,50,14
END
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""windows.h""\r\n"
    "\0"
END
3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END
/////////////////////////////////////////////////////////////////////////////
#endif    // APSTUDIO_INVOKED
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

Listing Four

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by NoDigits.RC
//
#define IDD_NODIGITS                    104
#define IDD_SUBCLASS                    104
#define IDI_NODIGITS                    105
#define IDI_SUBCLASS                    105
#define IDC_EDIT                        1000
#define IDC_NODIGITS1                   1004
#define IDC_NODIGITS2                   1005
#define IDC_STATIC                      -1
// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1002
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.