Thread: Message Handling

  1. #1
    Registered User
    Join Date
    Aug 2005
    Posts
    96

    Message Handling

    I have a problem with my Win32 message handling.

    I'll just dump the code.

    app.h

    Code:
    class App
    {
        public:
    
            App();
            ~App();
    
            int Run();
    };
    app.cpp

    Code:
    App::App()
    {
        INITCOMMONCONTROLSEX iccex;
    
        iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
        iccex.dwICC = ICC_BAR_CLASSES;
    
        ::InitCommonControlsEx(&iccex);
    }
    
    App::~App()
    {
    
    }
    
    int App::Run()
    {
        MSG msg;
    
        while (::GetMessage(&msg, 0, 0, 0) > 0)
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
    
        return msg.wParam;
    }
    widget.h

    Code:
    class Widget 
    {
        public:
    
            Widget(Widget* parent, const std::string& title, LPSTR className,
                   const Point& pos, const Size& size, long style);
    
            virtual ~Widget();
    
            HWND GetHandle();
    
        protected:
    
            virtual LRESULT CALLBACK ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
        private:
    
            static LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
            HWND m_hwnd;
    };
    widget.cpp

    Code:
    Widget::Widget(Widget* parent, const std::string& title, LPSTR className,
                   const Point& pos, const Size& size, long style)
    {
        static bool registered = false;
    
        if (!registered)
        {
            WNDCLASSEX wc;
    
            wc.cbSize = sizeof(WNDCLASSEX);
            wc.style = CS_DBLCLKS;
            wc.lpfnWndProc = WindowProcedure;
            wc.cbClsExtra = 0;
            wc.cbWndExtra = 0;
            wc.hInstance = ::GetModuleHandle(0);
            wc.hIcon = static_cast<HICON>(::LoadImage(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
            wc.hCursor = static_cast<HICON>(::LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE));
            wc.hbrBackground = static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH));
            wc.lpszMenuName = 0;
            wc.lpszClassName = "wx::msw::Widget";
            wc.hIconSm = static_cast<HICON>(::LoadImage(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
    
            if(!::RegisterClassEx(&wc))
                throw wx::Exception("Unable to register the window class.", __FILE__, __LINE__);
    
            registered = true;
        }   
    
        HWND hparent;
    
        if (parent)
            hparent = parent->GetHandle();
    
        else
            hparent = 0;
    
        m_hwnd = ::CreateWindowEx(0,
                                  className,
                                  title.c_str(),
                                  style,
                                  pos.x,
                                  pos.y,
                                  size.width,
                                  size.height,
                                  hparent,
                                  0,
                                  ::GetModuleHandle(0),
                                  this);
    
        if (!m_hwnd)
            throw Exception("Unable to create window.", __FILE__, __LINE__);
    }
    
    Widget::~Widget()
    {
        ::DestroyWindow(m_hwnd);
    }
    
    HWND Widget::GetHandle()
    {
        return m_hwnd;
    }
    
    LRESULT CALLBACK Widget::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_CLOSE:
            {
                ::DestroyWindow(m_hwnd);
            }
            break;
    
            case WM_DESTROY:
            {
                ::PostQuitMessage(0);
            }
            break;
    
            default:
                return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
    
        return 0;
    }
    
    LRESULT CALLBACK Widget::WindowProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_NCCREATE)
    		::SetWindowLongPtr(hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>((LPCREATESTRUCT(lParam))->lpCreateParams));
    
        Widget* widget = reinterpret_cast<Widget* >(::GetWindowLongPtr(hwnd, GWL_USERDATA));
    
        if (widget)
            return widget->ProcessMessage(hwnd, uMsg, wParam, lParam);
    
        return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    statusbar.h

    Code:
    class StatusBar : public Widget
    {
        public:
    
            StatusBar(Widget* parent);
    
            ~StatusBar();
    
            int GetFieldCount();
    
            std::string GetText(int field);
    
            void SetFieldCount(int fields);
    
            void SetText(const std::string& text, int field);
    
        protected:
    
            LRESULT CALLBACK ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    };
    statusbar.cpp

    Code:
    StatusBar::StatusBar(Widget* parent)
        : Widget(parent, "", STATUSCLASSNAME, wx::Point(0, 0), wx::Size(0, 0), WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP)
    {
    
    }
    
    StatusBar::~StatusBar()
    {
    
    }
    
    int StatusBar::GetFieldCount()
    {
        return ::SendMessage(GetHandle(), SB_GETPARTS, 0, 0);
    }
    
    std::string StatusBar::GetText(int field)
    {
        const int textLength = ::SendMessage(GetHandle(), SB_GETTEXTLENGTH, field, 0);
    
        char buffer[textLength];
    
        ::SendMessage(GetHandle(), SB_GETTEXT, field, reinterpret_cast<LPARAM>(buffer));
    
        return std::string(buffer);
    }
    
    LRESULT CALLBACK StatusBar::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_SIZE:
            {
                ::MessageBox(0, "StatusBar", "WM_SIZE", MB_OK);
                ::SendMessage(GetHandle(), WM_SIZE, 0, 0);
            }
            break;
    
            default:
                return Widget::ProcessMessage(hwnd, uMsg, wParam, lParam);
        }
    
        return 0;
    }
    
    void StatusBar::SetFieldCount(int fields)
    {
        ::SendMessage(GetHandle(), SB_SETPARTS, fields, 0);
    }
    
    void StatusBar::SetText(const std::string& text, int field)
    {
        ::SendMessage(GetHandle(), SB_SETTEXT, field, reinterpret_cast<LPARAM>(text.c_str()));
    }
    Then here is how I used it.

    main.cpp

    Code:
    #include <windows.h>
    
    .....
    
    int main(int argc, char* argv[])
    {
        App app;
    
        Widget w(0, "Test", "wx::msw::Widget", Point(50, 50), Size(760, 530), WS_OVERLAPPEDWINDOW);
    
        w.Show();
    
        StatusBar sb(&w);
    
        return app.Run();
    }
    The problem is that my statusbar window procedure never gets called. What is going wrong?

    I'm using MinGW 3.4.5 with Code::Blocks.

  2. #2
    Registered User Dante Shamest's Avatar
    Join Date
    Apr 2003
    Posts
    970
    I believe it's because the status bar is using its own private WNDPROC. You call CreateWindowEx, but your WindowProcedure will never receive any messages, because it will just use Microsoft's.

  3. #3
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    The params to CreateWindow() are different depending on if the window is a child (WS_CHILD) or not. RTM and look at the HMENU param.

    You are not taking this into account.

    Also all your windows will have the same ID number, 0.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  4. #4
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    Quote Originally Posted by novacain
    The params to CreateWindow() are different depending on if the window is a child (WS_CHILD) or not. RTM and look at the HMENU param.

    You are not taking this into account.

    Also all your windows will have the same ID number, 0.
    Yes you are right..... However I just added a quick hack, and for some reason my StatusBar::ProcessMessage() function is not being callled. :P

    Since I only added one control beside the top window here is the hack.

    Code:
    int widgetId = style & WS_CHILD ? 1 : 0;
    Then I added this as the HMENU param to CreateWindowEx.

    Code:
    reinterpret_cast<HMENU>(widgetId),
    So why is my StatusBar::ProcessMessage() function not being called? It should be because I'm overriding the base class (Widget) version of it....

  5. #5
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    Ok I almost got it I think.

    In Widget::ProcessMessage() I forgot to set m_hwnd.

    Code:
    LRESULT CALLBACK Widget::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_CREATE:
            {
                m_hwnd = hwnd;
               // Without the line above 
               // the code below won't work
               // ::SetWindowText(m_hwnd, "Test");
            }
            break;
    
            case WM_DESTROY:
            {
                ::PostQuitMessage(0);
            }
            break;
        }
    
        return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    However if I create a window with the WS_CHILD style well it doesn't work (My StatusBar::ProcessMessage() function doesn't get called).

    Anyone know why?

  6. #6
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    (My StatusBar::ProcessMessage() function doesn't get called).
    Quote Originally Posted by Dante Shamest
    I believe it's because the status bar is using its own private WNDPROC. You call CreateWindowEx, but your WindowProcedure will never receive any messages, because it will just use Microsoft's.
    I think Dante hit it on the nose, several posts ago. You pass STATUSCLASSNAME as your class - a class you did not register, meaning the window's procedure is the one specified by who did register the class: Microsoft. It's this procedure that makes the statusbar a statusbar.
    Most controls like that are designed to be ok that way - they can be controlled by the parent window through sending/receiving messages.
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  7. #7
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    Quote Originally Posted by Cactus_Hugger
    I think Dante hit it on the nose, several posts ago. You pass STATUSCLASSNAME as your class - a class you did not register, meaning the window's procedure is the one specified by who did register the class: Microsoft. It's this procedure that makes the statusbar a statusbar.
    Most controls like that are designed to be ok that way - they can be controlled by the parent window through sending/receiving messages.
    So umm how do I get it to use my window procedure for the status bar?

  8. #8
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    A common approach is to subclass the window with SetWindowLongPtr using the control's handle and your own window procedure with the GWLP_WNDPROC flag. Use CallWindowproc to forward unprocessed messages or those for which you want default system handling.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  9. #9
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    Quote Originally Posted by Ken Fitlike
    A common approach is to subclass the window with SetWindowLongPtr using the control's handle and your own window procedure with the GWLP_WNDPROC flag. Use CallWindowproc to forward unprocessed messages or those for which you want default system handling.
    So I need to do this in addition to what I was doing above? I was under the impression that if I have a static window procedure which forwards the messages to my virtual Widget::ProcessMessage() function then in my StatusBar class I could override the base (Widget) ProcessMessage(), and it would just work TM (because it was virtual).
    Please forgive my ignorance......

  10. #10
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    The statusbar control's message handler is provided by the system(like any other system registered window class); unless you superclass the control and register your own window class based on it and provide your window procedure during window class registration, that system handler will do what it does. If you override the system handler explicitly after the window is created as I have described ie. subclass it then you will be able to override that system handler; just be aware of the possible side-effects when you do so.

    What you have in your own c++ class construct is the capability to intercept and override your own registered window class's(ones you used RegisterClassEx with) window procedures while forwarding messages to your internal, c++ class member function handler. As alluded to in the preceding paragraph and mentioned previously by at least two others in this thread, system registered window classes have system handlers, tucked away in system dlls (like user32.dll or comctl32.dll); your window procedure(which is, I imagine, exclusively used by the main, parent window of your application) never sees messages intended for those controls, other than those specifically despatched to a parent window from a child control, such as WM_NOTIFY.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  11. #11
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    Quote Originally Posted by Ken Fitlike
    The statusbar control's message handler is provided by the system(like any other system registered window class); unless you superclass the control and register your own window class based on it and provide your window procedure during window class registration, that sytem handler will do what it does. If you override the system handler explicitly after the window is created as I have described ie. subclass it then you will be able to override that system handler; just be aware of the possible side-effects when you do so.

    What you have in your own c++ class construct is the capability to intercept and override your own registered window class's(ones you used RegisterClassEx with) window procedures while forwarding messages to your internal, c++ class member function handler. As alluded to in the preceding paragraph and mentioned previously by at least two others in this thread, system registerd window classes have system handlers, tucked away in system dlls (like user32.dll or comctl32.dll); your window procedure(which is, I imagine, exclusively used by the main, parent window of your applicatio) never sees messages intended for those controls, other than those specifically dispatched to a parent window from a child control, such as WM_NOTIFY.
    Ok. I think I get it now......

  12. #12
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    I sense a little doubt.

    It might be to your advantage to knock up a simple little example that includes just a main window and a statusbar(no c++ class wrappers!) and verify for yourself that your main window procedure never sees a statusbar message, other than in the form of, for example, a WM_NOTIFY. Then modify the example to subclass the statusbar as described in a previous post and include a separate window procedure for it but, instead of using DefWindowProc for default, system message handling, use CallWindowProc to ensure those statusbar messages get default, system processing.

    I think that will make it much clearer what's going on.

    edit: typos
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  13. #13
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    Quote Originally Posted by Ken Fitlike
    I sense a little a doubt.

    It might be to your advantage to knock up a simple little example that includes just a main window and a statusbar(no c++ class wrappers!) and verify for yourself that your main window procedure never sees a statusbar message, other than in the form of, for example, a WM_NOTIFY. Then modify the example to subclass the statusbar as described in a previous post and include a separate window procedure for it but, instead of using DefWindowProc for default, system message handling, use CallWindowProc to endure those statusbar messages get default, system processing.

    I think that will make it much clearer what's going on.
    Ok Will do.

  14. #14
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    Ok I just knocked out an example which I got to work. Now to figure out how to implment it with C++, and classes.....

  15. #15
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    I got it to process the messages now. My StatusBar::ProcessMessage() function gets called now. Thanks for the help everyone.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Strange string behavior
    By jcafaro10 in forum C Programming
    Replies: 2
    Last Post: 04-07-2009, 07:38 PM
  2. Problem in message handling VC++
    By 02mca31 in forum Windows Programming
    Replies: 5
    Last Post: 01-16-2009, 09:22 PM
  3. Message class ** Need help befor 12am tonight**
    By TransformedBG in forum C++ Programming
    Replies: 1
    Last Post: 11-29-2006, 11:03 PM
  4. Dialog Box Problems
    By Morgul in forum Windows Programming
    Replies: 21
    Last Post: 05-31-2005, 05:48 PM
  5. Tab Controls - API
    By -KEN- in forum Windows Programming
    Replies: 7
    Last Post: 06-02-2002, 09:44 AM