Menu

User-defined_Windows_messages

User-defined Windows Messages

Much of the behavior of an OWL program is defined by user-defined command messages. A command message is the type of message you define for a menu item, accompanied by a unique ID identifying the command. The issue of a command, e.g. menu item selection, is communicated through the WM_COMMAND message.

Although command messages and the other built-in Windows messages usually can handle most of the needs in your programs, sometimes there is a need for a message particular to your application. In this case you can define your own custom message.


Defining a custom message

Windows defines a constant, WM_USER, to represent the value at which you can start defining your own messages. Windows reserves all messages from 0 to WM_USER − 1 for its own use. However, you can define any messages you like between the values of WM_USER and WM_USER + 0x7FFF.

Simply add a macro definition representing the chosen ID of your message in the appropriate source file, taking care that the assigned value is unique throughout your application (at a minimum it must be unique among the messages processed by any single window class). For example:

#define PM_MYMESSAGE (WM_USER + 0)



Handling a custom message

To handle a user-defined message in your window class, add an EV_MESSAGE macro to its response table:

DEFINE_RESPONSE_TABLE1(TMyWindow, TFrameWindow)
  EV_MESSAGE(PM_MYMESSAGE, EvMyMessage),
END_RESPONSE_TABLE;

The EV_MESSAGE macro takes as its two parameters the message ID and the name of the function that will handle the message.

The next step is to write the message handler that will respond to our custom message. This function must have two parameters, TParam1 and TParam2, and it must return a TResult value. The meaning of the two parameters can be anything you like. After all, it's your message!

For example, for our new message PM_MYMESSAGE, we can define that TParam1 is unused and that TParam2 carries a pointer to a message string.

TResult TMyWindow::EvMyMessage(TParam1, TParam2 p2)
{
  PRECONDITION(p2 != 0);
  const char* msg = reinterpret_cast<const char*>(p2);
  MessageBox(msg, "Message");
  return 0;
}

Of course, your handler will probably do much more than this. You can handle the message any way you like.


Sending a custom message

To send the custom message, you simply use the ordinary Windows functions for sending messages, such as SendMessage and PostMessage. For example:

void SendMyMessage(TMyWindow* w, const char* msg);
{
  PRECONDITION(msg);
  TParam2 p2 = reinterpret_cast<TParam2>(msg);
  w->SendMessage(PM_MYMESSAGE, 0, p2);
}

That's all there is to it!


A note about parameters and type-safety

As you may have noticed above, the parameters of a message, TParam1 and TParam2, are generic parameters that require casting to and from the actual argument types defined by your message. Since such casting is error-prone, you should minimize the need to do so by encapsulating the dispatch of your message in type-safe wrappers (e.g. SendMyMessage above).


Defining a type-safe handler for a custom message

You can go a step further and define a type-safe handler signature for your message. Using our custom message above, and the new dispatch framework introduced in OWLNext 6.40, you can now do as follows:

template <TMsgId MessageId>
struct TMyDispatch;

template <>
struct TMyDispatch<PM_MYMESSAGE>
{
  template <class T, void (T::*M)(const char* msg)>
  static TResult Decode(void* i, TParam1, TParam2 p2)
  {
    PRECONDITION(p2 != 0);
    const char* msg = reinterpret_cast<const char*>(p2);
    (static_cast<T*>(i)->*M)(msg);
    return 0;
  }
};

#define EV_PM_MYMESSAGE\
  {{PM_MYMESSAGE}, 0, OWL_DISPATCH(TMyDispatch<PM_MYMESSAGE>::Decode, EvMyMessage)}

Note that the template parameters of the Decode function define the signature of the handler for our custom message. The EV_PM_MYMESSAGE macro uses our dispatch template to define a response table entry for our message, telling the dispatch machinery to use our decoder, and mandating that the message handler is named "EvMyMessage". With this response table macro in place, you can now very simply define a type-safe handler for your message. For example:

DEFINE_RESPONSE_TABLE1(TMyWindow, TFrameWindow)
  EV_PM_MYMESSAGE,
END_RESPONSE_TABLE;

void TMyWindow::EvMyMessage(const char* msg)
{
  PRECONDITION(msg);
  MessageBox(msg, "Message");
}

Note that your handler now has a type-safe signature, so there is no longer the need to do argument casting. Your code can now enjoy type-safety, just as with the in-built handlers defined by OWL for the system messages (e.g. EvLButtonDown).

For more information about the dispatch machinery, see Windows Message Dispatch in OWLNext.


Related

Discussion: User-defined Windows Messages tutorial
Wiki: Knowledge_Base
Wiki: Windows_Message_Dispatch_in_OWLNext

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.