Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C++
Article

True Windows control subclassing

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
29 Oct 2012CPOL3 min read 25.5K   874   16   5
How to subclass a Windows control with more actual control over message handling.

Introduction

When writing an interface in Windows, it's some times necessary to come-up with our own Control structures, that handle some things differently than we're offered by default. For example if you want to have some custom text coloring in your listbox, that is more complex than a single color per line of text; or put in filters that would reject or redirect the handling based on some runtime conditions; or if you want a custom background for your Control (listbox, button, etc.); or if you simply want to log the messages a window receives, so you can learn from them. You quickly find that there is a lot of work going into coding it from scratch to handle all the messages you need, making the task infeasible or at least not worth the effort.

Background

Initially when I came across the problem in my particular task, I checked to see if Microsoft's documentation on MSDN can provide me with a way to make a control, that inherits it's functions from an existing one, but can modify any part of it to suit my needs. What I found (article BB773183) looked promising. An entire article, devoted to subclassing our controls to fit that very purpose. It has an old method and a new, supposedly improved one.

The old method involved creating a window of the parent class, then replacing its WndProc function value using SetWindowLongPtr, which returns the old value. Your window message processing functions had to be declared as a WNDPROC, but it had to use the value saved from SetWindowLongPtr to call CallWindowProc, instead of DefWindowProc. There is only one problem with this. It happens after the window (control) was created and it misses-out on at least the WM_CREATE and WM_PAINT messages and probably many more, that you may have wanted to handle.

The new method, specifically redesigned to perform the task of subclassing, suffers from the same design flaw. It requires that you first create the window with the parent class, then cast it to a subclass message handler. Though the function names are different, the fact remains, that there are messages you miss with both approaches.

After searching through the usual sources and coming-up with nothing, it suddenly came to thought that just as the API provides registering classes with a WNDCLASSEX structure, it probably also provides looking them up and writing their information back in one. GetClassInfoEx does exactly that. From there I just grabbed the lpfnWndProc pointer and saved it to a variable to be used in my custom message handler, instead of DefWindowProc.

Using the code

The article code provides two wrappers for effectively subclassing a control.

C++
WNDPROC RegisterSubClass(HINSTANCE hThisInstance,LPCTSTR ParentClass,LPCTSTR ChildClassName,WNDPROC ChildProc);
WNDPROC RegisterSubClassEx(LPCTSTR ParentClass,WNDCLASSEX * ChildClass);

The first registers a class with all the parameters being the same as the original, except for the name, instance and message handler. The second registers a class of your custom WNDCLASSEX definition. Both functions return the pointer to the parent class message handler, which must be saved and called instead of DefWindowProc.

C++
// globals
LRESULT CALLBACK SubProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam); // subclass message handler
WNDPROC listBoxDefault;
const TCHAR subListBox[] = TEXT("SubListBox");

//in WinMain
...
if(!(listBoxDefault = RegisterSubClass(hInstance, WC_LISTBOX, subListBox, SubProc))) {
    MessageBox(NULL, TEXT("SubClass Registration Failed!"),TEXT("Error!"),MB_ICONEXCLAMATION|MB_OK);
    return 0;
}
...

Where within the SubProc message handler you will have something of this sort.

C++
LRESULT CALLBACK SubProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    if(Message == LB_ADDFILE)
        return 1;
        
    return listBoxDefault(hwnd, Message, wParam, lParam);
}

The following example simply denies the processing of the LB_ADDFILE message, but instead it could log it, change it and then call listBoxDefault, then have some post-processing before it returns. The possibilities go only as far as you're willing to take them. With this method you can create any number of reusable control classes, that are mostly handled by existing Windows code, with very little and precise modifications on your part.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Bulgaria Bulgaria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionChange to set cbSize before calling GetClassInfoEx Pin
chrisgbk10-Nov-12 19:28
chrisgbk10-Nov-12 19:28 
AnswerRe: Change to set cbSize before calling GetClassInfoEx Pin
Jordan Gigov6-Dec-12 10:35
Jordan Gigov6-Dec-12 10:35 
QuestionCallWindowProc Pin
Ramon F. Mendes29-Oct-12 12:28
Ramon F. Mendes29-Oct-12 12:28 
AnswerRe: CallWindowProc Pin
Jordan Gigov30-Oct-12 8:41
Jordan Gigov30-Oct-12 8:41 
GeneralRe: CallWindowProc Pin
Paul M Watt4-Apr-15 10:50
mentorPaul M Watt4-Apr-15 10:50 
The CallWindowProc function handles Unicode-to-ANSI conversion. You cannot take advantage of this conversion if you call the window procedure directly.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.