Thread: Difficulty superclassing EDIT window class

  1. #1
    cDir
    Guest

    Difficulty superclassing EDIT window class

    I am relatively new at this, but I think I have the problem pinpointed. Of course I could also be entirely off.

    I am attempting to superclass the windows EDIT window class.

    I can successfully superclass it when not using c++ classes, but I would very much like to implement my new window class as a c++ class.

    I am reasonable certain that the problem has to do with the last paragraph from the MSDN article:

    "The superclass must not use the original extra bytes for the base class or the window for the same reasons that an instance subclass or a global subclass should not use them. Also, if the application adds extra bytes for its use to either the class or the window instance, it must reference the extra bytes relative to the number of extra bytes used by the original base class. Because the number of bytes used by the base class may vary from one version of the base class to the next, the starting offset for the superclass's own extra bytes may also vary from one version of the base class to the next. MSDN article"

    I assume this is because I must use the Set/GetWindowLong(...) method of referencing my class from a static member function, but I do not understand what to do to fix it. How many bytes does the old EDIT window class use?

    Here is the relevant code:
    Code:
    //file: ShEdit.cpp
    
    #include "ShEdit.h"
    
    #define ID_EDIT_SH 101
    
    ShEdit::ShEdit(HWND hwndParent, HINSTANCE hInstance)
    {
        // retrieve the WNDCLASS of the windows EDIT window class
        WNDCLASS wc;
        ZeroMemory(&wc, sizeof(WNDCLASS));
        GetClassInfo(hInstance, "EDIT", &wc);
    
        wc.hInstance = hInstance;
        wc.lpszClassName = "ShEdit";
    
        // store the old WNDPROC of the EDIT window class
        m_pfnPrevWndProc = wc.lpfnWndProc;
    
        // replace it with local WNDPROC
        wc.lpfnWndProc = WindowProc;
    
        // register the new window class, "ShEdit"
        RegisterClass(&wc);
    
        // use the new window class to create a window
        m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "ShEdit", "",
                       WS_CHILD | WS_VISIBLE | WS_VSCROLL |
                       WS_HSCROLL | ES_MULTILINE | 
                       ES_AUTOVSCROLL | ES_AUTOHSCROLL,
                       CW_USEDEFAULT, CW_USEDEFAULT, 100, 100,
                       hwndParent, (HMENU)ID_EDIT_SH, hInstance, (LPVOID)this);
    }
    
    
    LRESULT CALLBACK ShEdit::WindowProc(HWND hwnd , UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        // retrieve the pointer to the creator instance of ShEdit
        ShEdit* shEdit = NULL;
        shEdit = (ShEdit*)GetWindowLong(hwnd, DWL_USER); // null until WM_CREATE
    
        switch(uMsg)
        {
            case WM_CREATE:
            {
                // store the pointer to the instance of ShEdit passed in lParam
                SetWindowLong(hwnd, DWL_USER, lParam);
                shEdit = (ShEdit*)lParam;
    
                // call the default WNDPROC for the EDIT window class
                CallWindowProc(shEdit->m_pfnPrevWndProc, hwnd, uMsg, wParam, lParam);
    
                break;
            }
    
            default:
            {
                // didn't handle message, so send it to the default WNDPROC
                return CallWindowProc(shEdit->m_pfnPrevWndProc, hwnd, uMsg, wParam, lParam);
            }
        }
    
        return 0;
    }
    I realize that this is a lot, but I have really come to a standstill on this. It seems to me that this must be a fairly common problem. However, most of the online documentation I can find either uses MFC, or it does not use c++ classes.

    Thanks for your time at least,
    cDir

  2. #2
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    what errors are you getting?

    actually forget the last paragraph of msdn - you aren't touching the extra bytes - it's irrelevant.

    >>// replace it with local WNDPROC
    wc.lpfnWndProc = WindowProc;<<

    That's your problem. The WNDCLASS/WNCLASSEX lpfnWndProc cannot be a normal class fn. There are two ways around this:

    1. Declare a global wndproc and use this to fwd messages to your control/wnd.
    2. Use a static wndproc.

    The first method is real easy and doesn't require too much fiddling about. The second method is a bit more tricky due to the nature of what's going on and how msgs are sent during the CreateWindow/CreateWindowEx call.
    Last edited by Ken Fitlike; 02-20-2002 at 02:13 PM.

  3. #3
    Registered User
    Join Date
    Feb 2002
    Posts
    5
    >what errors are you getting?

    I am getting a run-time error. I am using winXP. Hitting debug when the error message comes up shows:
    "Application error - the instruction ... referenced memory at ... . The memory could not be "read". Click on OK to terminate the program."

    >That's your problem. The WNDCLASS/WNCLASSEX lpfnWndProc >cannot be a normal class fn. There are two ways around this:
    >
    >1. Declare a global wndproc and use this to fwd messages to >your control/wnd.

    I had already tried method 1, and I can successfully superclass the EDIT window class and create a new window with it. However, this is not what I want to do.

    >2. Use a static wndproc.

    I am already using method 2 here, but I realize that might not have been clear from the code portion I posted. I have used method 2 in the past, and have done it successfully, but never with superclassing.

    I will attach my VC++ 6 project file, zipped. It is quite small. If it would help to actually have a look at it that way, well, I would really appreciate anything you can suggest.

    cDir

  4. #4
    Registered User
    Join Date
    Feb 2002
    Posts
    5
    In addition to my previous post, maybe it will help if I explain what I am trying to do.

    According to MSDN, it seems to me that I want:
    "Superclassing
    Superclassing involves creating a new class that uses the window procedure of an existing class for basic functionality."

    As opposed to subclassing, I think.

    I want to:
    - have an enhanced EDIT control (well, eventually an enchanced RichEdit control) with added functionality via adding to a default EDIT control WNDPROC
    - be able to create multiple instances of this control in one application, but only in the current application (not system wide)
    - still be able to create instances of the default EDIT control in the same application

    I hope that this makes sense to someone. I realize that I may not be describing this the best, but I would really like to understand how to do this.

  5. #5
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    >>subclassing...superclassing<<

    It's cool, I know the difference and understand why you want to

    I've downloaded your source and will take a look later and try to get back to you asap (no promises, though but it does sound really interesting ).

  6. #6
    Registered User
    Join Date
    Feb 2002
    Posts
    5
    I am going about this wrong, conceptually. I don't want to waste your time. I don't think that the code will make any sense considering this.

    I've been doing more reasearch, and am now quite certain that superclassing is not appropriate the way that it is used in the code above, and I do not believe that it is what I want to do.

    I need to use subclassing instead. The ShEdit c++ class should, every time an instance of it is instantiated, create an instance of an EDIT control and then subclass that instance of the EDIT control by providing the new WNDPROC . I will not be able to handle WM_CREATE messages, but that is ok.


    This is just a learning project, but I should really have planned this out more in advance. I just didn't have a clear grasp of what the terms meant. I am going to attempt this the right way now, and see if I can avoid the previous problems.


    Thank you for at least being willing to take a look at it,
    cDir

  7. #7
    Registered User
    Join Date
    Feb 2002
    Posts
    5
    Drat, but if I can not handle the WM_CREATE message, then I can not retrieve the pointer to the creator class passed in lParam.

    I am going to have to create a custom message to get that to my new WNDPROC.

    Yes, this will work, but I am just going to have to do a bit more learning before things are going to fall together.


    Thanks again, I'll get it eventually.

  8. #8
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    >>I don't want to waste your time<<

    Don't worry, you're not. This is fun!

    Anyway, I was about to give you some info when my pc crashed!
    When I came back here again you had posted. Still, if it's not too late I tested your stuff on win2k (same error as you got - runtime, not compile time) and win98 (lovely, took me right to the 'switch default' of your wndproc) which confirmed my suspicions:

    During windows processing of CreateWindow/CreateWindowEx a whole bunch of msgs are sent: WM_CREATE, WM_NCCREATE, WM_MEASURITEM, WM_NCCALCSIZE. The problem with these - and there may be others - is that the this ptr you supply as the last parameter of CreateWindow/CreateWindowEx is not available.

    Fortunately the lParam of the WM_NCCREATE and WM_CREATE contains the info you need but you have to cast the lpCreateParams of the (CREATESTRUCT*)lParam to your class object ptr as that's the part that contains the this ptr. What you've done is attempt to just cast the lParam itself.

    The other thing you should do is make your class object ptr static within the wndproc - once its value is set within either (and you must examine both) WM_NCCREATE or WM_CREATE.

    When you use SetWindowLong use the 'GWL_USERDATA' to store your this ptr simply because your wnd isn't a dialog.

    Anyway, the cut a long story short, here's an amended version of your windowproc that should allow you to play around a bit, but your superclassed window is failing to create (I think):
    Code:
    LRESULT CALLBACK ShEdit::WindowProc(HWND hwnd , UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        // retrieve the pointer to the creator instance of ShEdit stored
        static ShEdit* shEdit;
        int p=uMsg;
    	int q=WM_NCCREATE;
    	int r=WM_CREATE;
    	int s=WM_NCCALCSIZE;
    	int t=WM_MEASUREITEM;
    	int u=WM_NCDESTROY;
        // note that this is NULL and not used the first time
        // through this function (when the message is WM_CREATE)
        //shEdit = (ShEdit*)GetWindowLong(hwnd, DWL_USER);
    
        switch(uMsg)
        {
    		case WM_NCCREATE:
            case WM_CREATE:
            {
    		CREATESTRUCT* cs=(CREATESTRUCT*)lParam;
    		shEdit=(ShEdit*)cs->lpCreateParams;
                // store the pointer passed in lParam from CreateWindow(...) for later
                //SetWindowLong(hwnd, DWL_USER, lParam);
    			SetWindowLong(hwnd,GWL_USERDATA,(LONG)shEdit);
                // shEdit is now a pointer to the instance of ShEdit that created the 
                // window that this function is a WNDPROC for
                //shEdit = (ShEdit*)lParam;
    
                // call the default WNDPROC for the EDIT window class
                CallWindowProc(shEdit->m_pfnPrevWndProc, hwnd, uMsg, wParam, lParam);
    
                break;
            }
    		
            default:
            {
                // didn't handle message, so pass it to the default WNDPROC
                return CallWindowProc(shEdit->m_pfnPrevWndProc, hwnd, uMsg, wParam, lParam);
            }
        }
    
        return 0;
    }
    If you step through this in debug mode you will see the int values of various msgs and the order that they come into your wndproc. You'll also see at which points your this ptr becomes available for use.

    As an aside, what I do is have a base class with the static wndproc. This examines incoming msgs and decides whether it's from a wnd, dialog or sub/super-classed cntrl before forwarding the msg to a derived class's virtual wndproc. That way you only have to write the static wndproc once, bury it in the base class and forget about it....until some one asks how it works.

    Hope that's been of some use/interest to you. Thanks too!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. deriving classes
    By l2u in forum C++ Programming
    Replies: 12
    Last Post: 01-15-2007, 05:01 PM
  2. Button positioning
    By Lionmane in forum Windows Programming
    Replies: 76
    Last Post: 10-21-2005, 05:22 AM
  3. Pong is completed!!!
    By Shamino in forum Game Programming
    Replies: 11
    Last Post: 05-26-2005, 10:50 AM
  4. Experienced coders read. ( Common/efficient writing )
    By knave in forum C++ Programming
    Replies: 8
    Last Post: 04-23-2003, 09:07 PM
  5. Edit controls not sending messages to parent window
    By EMiller in forum Windows Programming
    Replies: 5
    Last Post: 11-13-2001, 11:03 PM