Home Articles Books Downloads FAQs Tips

Q: Respond to messages sent to the application.


Answer:

There is more than one way to catch messages that Windows sends to your program. You can override the virtual WndProc function of the main form, you can create a message map, or you can create an OnMessage handler for the application. This FAQ describes each one briefly.


Overriding WndProc: All descendants of TControl, including TForm, contain a WndProc function that serves as the window procedure for the component. This function is virtual, which means that you can override the function to intercept messages. The argument to WndProc is a TMessage structure passed by reference that contains the message command ID and the WPARAM and LPARAM values.

Step 1: Add the WndProc declaration to your form's header.

    private:
        void __fastcall WndProc(Messages::TMessage &Message);

Step 2: Code the function. The derived WndProc should call the base class TForm::WndProc for all messages that you don't process. This function prevents the windows screensaver from starting.

    void __fastcall TForm1::WndProc(Messages::TMessage &Message)
    {
        if(  (Message.Msg    == WM_SYSCOMMAND) &&
             (Message.WParam == SC_SCREENSAVE)    )
        {
            Application->MessageBox("Good bye screen saver", "SCR No", MB_OK);
            Message.Result = true;
        }
        else
            TForm::WndProc(Message);
    }

Creating a Message Map: Message maps allow you to write a function that receives only one specific message, rather than all of a form's messages like WndProc does. Message maps utilize the various message structures found in \INCLUDE\MESSAGES.HPP. For example, a message map for WM_GETMINMAXINFO would utilize the TWMGetMinMaxInfo structure. These structures are nice because the WPARAM and LPARAM values are decoded into more meaningful structure members.

Step 1: Add a function declaration and the message map body to your form's header.

    private:
        void __fastcall WMSysCommand(TWMSysCommand &Message);
    public:
        __fastcall TForm1(TComponent* Owner);

    BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(WM_SYSCOMMAND,TWMSysCommand,WMSysCommand)
    END_MESSAGE_MAP(TForm)

Step 2: Code the function. If you need to pass the message on to the base class, call the Dispatch function of the base class.

    void __fastcall TForm1::WMSysCommand(TWMSysCommand &Message)
    {
        if(Message.CmdType == SC_SCREENSAVE)
        {
            Application->MessageBox("Good bye screen saver", "SCR No", MB_OK);
            Message.Result = true;
        }
        else
            TForm::Dispatch(&Message);
    }

Note: Make sure that you call the Dispatch method of the base class, and not the Dispatch method of your derived class. Message maps are triggered from the Dispatch method of the derived class. Calling the derived version of Dispatch triggers your message map again, and this results in infinite recursion.


Creating an OnMessage handler: The OnMessage handler allows you to handle messages before they are processed by the application.

Step 1: Add a function declaration for the OnMessage handler.

    private:
        void __fastcall AppOnMessage(TMsg &Message, bool &Handled);

Step 2: Code the function. Set the Handled flag for messages that you handle.

    void __fastcall TForm1::AppOnMessage(TMsg &Msg, bool &Handled)
    {
        if(  (Msg.message == WM_SYSCOMMAND) &&
             (Msg.wParam  == SC_SCREENSAVE)   )
        {
            Application->MessageBox("Good bye screen saver", "SCR No", MB_OK);
            Handled = true;
        }
        else
            Handled = false;
    }

Step 3: In the constructor of the main form, assign the function AppOnMessage to the OnMessage event of the global Application object.

    void __fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
    {
        Application->OnMessage = AppOnMessage;
    }

Notes: So how do you decide which of the previous methods to use? Overriding WndProc works well when you need to respond to a lot of different messages, or when you have existing switch/case message handling code from an API project that you would like to reuse with minimum effort. The message maps are nice because they allow you to focus on a single message, and the message structures can make your code easy to read. The OnMessage approach allows you to intercept messages before the program gets a chance to process them. The downfall of OnMessage is that messages sent via SendMessage or Perform don't get passed to the OnMessage handler. Additionally, the OnMessage handler can bog down the system.

In general, use WndProc or message maps for most cases. Use OnMessage only when its the only way to intercept a message. Never use OnMessage from within a component to intercept application messages. If necessary, use good ol' subclassing. The VCL makes subclassing painless through the WindowProc property of classes derived from TControl.

Note added 1/15/2002: Jonathan Arnold sent me this comment about the trapping the OnMessage event for the application.

In your discussion of the pros and cons of the various methods, you should mention that if you override the form's WndProc method, you don't get messages destined for controls on your form, while the OnMessage method does.

He is correct. With OnMessage you can intercept a message that was posted directly to a tree view or edit box or any window control that is on your form. This can prove useful at times. However, keep in mind that you can also intercept such messages by deriving a new class from the control. If you find yourself frequently using OnMessage to intercept messages for a particular control, you might want to consider deriving a new component class instead. Sometimes, making a new component class is a hastle, especially if you are not going to reuse the custom behavior in other projects. If this is the case, then OnMessage might be more appropriate.



Copyright © 1997-2000 by Harold Howe.
All rights reserved.