Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Typesafe Windows Message Passing Using MFC

0.00/5 (No votes)
8 Nov 2010 1  
Easy to use macros provide typesafe Windows message passing with call semantics.

Introduction

The Microsoft Foundation Classes reduce, but do not eliminate, the need for passing Windows messages using the Windows ::SendMessageand ::PostMessagefunctions. Unfortunately, passing message parameters using these functions is not type safe. You must cast your parameters to a WPARAMor LPARAMdata type in the call, then cast back to the desired data type in the handler. This is bad enough when passing numeric values, but even worse when passing pointers. The macros defined below allow simple type safe message passing in an MFC application. As an added benefit, the macros provide HWNDbased message passing using method call semantics.

The Macro Definitions

Place the following macro definitions in your code:

// Macro to define the message and the global calling functions
// This macro is used at global scope
#define TYPESAFE_MESSAGE( NAME, MSG, RESULT, ARG1, ARG2 )   \
  const UINT MSG__##NAME( ( MSG ) ) ;  \
  inline RESULT NAME( const HWND hwnd, ARG1 a , ARG2 b ) { \
     return (RESULT)( ::SendMessage( hwnd, (MSG), WPARAM( a ), LPARAM( b ) ) ) ; }  \
  inline BOOL post__##NAME( const HWND hwnd, ARG1 a , ARG2 b ) { \
       return ::PostMessage( hwnd, (MSG), WPARAM( a ), LPARAM( b ) ) ; }   
//  
// Macro to define the message handler   
// This macro is used in the class definition of an MFC CWnd-derived class
#define TYPESAFE_MESSAGE_HANDLER( NAME, RESULT, ARG1, ARG2 )   \
  virtual RESULT NAME( ARG1, ARG2 );  \
  afx_msg LRESULT ON__##NAME( WPARAM a, LPARAM b )
      { return LRESULT( NAME( (ARG1) a, (ARG2) b ) ); }  \
  void NAME##___compile_test() const { RESULT x = ::NAME( (HWND) 0, (ARG1) 0, (ARG2) 0 );  
//
// macro to define the message map entry
#define TYPESAFE_MAPENTRY( NAME ) ON_MESSAGE( MSG__##NAME, ON__##NAME )

Using the Macros

The macros are used as follows:

  1. Create a header file to contain the global message descriptions (e.g., app_messages.h). Messages are defined at global scope using the TYPESAFE_MESSAGEmacro.
  2. Add message handlers to your CWndderived class definitions using the TYPESAFE_MESSAGE_HANDLERmacro.
  3. Add entries to the MFC MESSAGE_MAPusing the TYPESAFE_MAPENTRYmacro.
  4. Write the body of the message handler methods defined by the TYPESAFE_MESSAGE_HANDLERmacro.

The TYPESAFE_MESSAGEmacro is defined as:

TYPESAFE_MESSAGE( name, code, result_type, wparam_type, lparam_type )

where:

  • nameis the name of the message
  • codeis the UINTmessage code used in ::SendMessageand ::PostMessagecalls
  • result_typeis the data type of the result returned by ::SendMessage
  • wparam_typeis the data type of wparam/li>
  • lparam_typeis the data type of lparam/li>

result_typemust be a data type that can be cast to LRESULT wparam_typeand lparam_typemust be data types that can be cast to LPARAM

The TYPESAFE_MESSAGE_HANDLERmacro is defined as:

TYPESAFE_MESSAGE_HANDLER( name, result_type, wparam_type, lparam_type )

where name result_type wparam_type and lparam_typemust match the corresponding values used in the TYPESAFE_MESSAGEmacro.

The TYPESAFE_MAPENTRYmacro is defined as:

TYPESAFE_MAPENTRY( name )

where namemust match the name used in the above two macros.

Example

Assume you want to create a message named on_foowith the message code WM_APP+200 Further, you want on_footo take as arguments a const intand a const CWnd* and you want on_footo return a float Finally, you want the class MyClassto have a handler for on_foo

In the file app_messages.h, define:

TYPESAFE_MESSAGE( on_foo, WM_APP+200, float, const int, const CWnd* )

In the file MyClass.h, define:

class MyClass : public CWnd
{
  ...
  TYPESAFE_MESSAGE_HANDLER( on_foo, float, const int, const CWnd* )
  ...
};

In the file MyClass.cpp, define:

BEGIN_MESSAGE_MAP(MyClass, CWnd)
  ...
  TYPESAFE_MAPENTRY( on_foo )
END_MESSAGE_MAP()

float MyClass::on_foo( const int a, const CWnd* b )
{
  ...
}

That's it. Now you can send an on_foomessage to any MyClasswindow and invoke the MyClass::on_foomethod by using the code:

float x = on_foo( hwnd, a, b );
// where hwnd is a window handle to a MyClass window 

Alternatively, you can post an on_foomessage using the code:

BOOL b = post_on_foo( hwnd, a, b ); 

Notice that in both cases, you are using method call semantics for message passing.

How it Works

In the above example, the TYPESAFE_MESSAGEmacro creates the following code at global scope:

const UINT MSG__on_foo( WM_APP + 200 );
inline float on_foo( const HWND hwnd, const int a, const CWnd* b )
   { return float( ::SendMessage( hwnd, WM_APP+200, WPARAM( a ), LPARAM( b ) ) ); }
inline BOOL post_on_foo( const HWND hwnd, const int a, const CWnd* b )
   { return ::PostMessage( hwnd, WM_APP+200, WPARAM( a ), LPARAM( b ) ) ); }

The TYPESAFE_MESSAGE_HANDLERmacro produces the following code in the MyClassheader:

virtual float on_foo( const int, const CWnd* );
afx_msg LRESULT ON__on_foo( WPARAM a, LPARAM b )
  { return LRESULT( on_foo( const int a, const CWnd* b ) ); }
void on_foo__compile_test() const
  { float x = ::on_foo( (HWND) 0, (const int) 0, (const CWnd*) 0 ); }

And, the TYPESAFE_MAPENTRYmacro produces the following code in the MyClassMFC message map:

ON_MESSAGE( MSG__on_foo, ON__on_foo ) 

In MyClass the ON__on_fooMFC message handler receives the on_foomessage, casts the arguments to the proper data types, and invokes the on_foomethod.

The method on_foo___compile_test()is never called. Its sole purpose is to generate a call to ::on_foo( hwnd, a, b )and force the compiler to do a compile-time check to make sure that the data types specified in TYPESAFE_MESSAGE_HANDLERmatch those in TYPESAFE_MESSAGE

History

  • 11/8/2010 - Corrected a couple of typos.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here