65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 MFC 进行类型安全的 Windows 消息传递

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (7投票s)

2010年11月4日

CPOL

3分钟阅读

viewsIcon

31945

易于使用的宏提供具有调用语义的类型安全的 Windows 消息传递。

引言

Microsoft 基础类减少了,但并没有消除使用 Windows ::SendMessage::PostMessage 函数传递 Windows 消息的需要。不幸的是,使用这些函数传递消息参数不是类型安全的。您必须将参数转换为 WPARAMLPARAM 数据类型,然后在处理程序中再转换回所需的数据类型。在传递数值时这已经很糟糕了,而在传递指针时更糟。下面定义的宏允许在 MFC 应用程序中进行简单的类型安全的消息传递。 作为一个额外的优势,这些宏使用方法调用语义提供基于 HWND 的消息传递。

宏定义

将以下宏定义放置在您的代码中

// 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 )

使用宏

宏的使用方法如下

  1. 创建一个头文件来包含全局消息描述(例如,app_messages.h)。使用 TYPESAFE_MESSAGE 宏在全局范围内定义消息。
  2. 使用 TYPESAFE_MESSAGE_HANDLER 宏将消息处理程序添加到您的 CWnd 派生类定义中。
  3. 使用 TYPESAFE_MAPENTRY 宏将条目添加到 MFC MESSAGE_MAP 中。
  4. 编写由 TYPESAFE_MESSAGE_HANDLER 宏定义的消息处理程序方法的正文。

TYPESAFE_MESSAGE 宏定义如下

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

其中

  • name 是消息的名称
  • code::SendMessage::PostMessage 调用中使用的 UINT 消息代码
  • result_type::SendMessage 返回的结果的数据类型。
  • wparam_typewparam 的数据类型
  • lparam_typelparam 的数据类型

result_type 必须是可以转换为 LRESULT 的数据类型。wparam_typelparam_type 必须是可以转换为 LPARAM 的数据类型。

TYPESAFE_MESSAGE_HANDLER 宏定义如下

TYPESAFE_MESSAGE_HANDLER( name, result_type, wparam_type, lparam_type )

其中 nameresult_typewparam_typelparam_type 必须与 TYPESAFE_MESSAGE 宏中使用的对应值匹配。

TYPESAFE_MAPENTRY 宏定义如下

TYPESAFE_MAPENTRY( name )

其中 name 必须与上述两个宏中使用的名称匹配。

示例

假设您想创建一个名为 on_foo 的消息,其消息代码为 WM_APP+200。此外,您希望 on_foo 接受 const intconst CWnd* 作为参数,并且您希望 on_foo 返回一个 float。最后,您希望类 MyClass 具有 on_foo 的处理程序。

在文件 app_messages.h 中,定义

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

在文件 MyClass.h 中,定义

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

在文件 MyClass.cpp 中,定义

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

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

就这样。现在,您可以通过使用代码将 on_foo 消息发送到任何 MyClass 窗口并调用 MyClass::on_foo 方法

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

或者,您可以使用以下代码发布 on_foo 消息

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

请注意,在这两种情况下,您都在使用方法调用语义进行消息传递。

工作原理

在上面的例子中,TYPESAFE_MESSAGE 宏在全局范围内创建以下代码

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 ) ) ); }

TYPESAFE_MESSAGE_HANDLER 宏在 MyClass 标头中生成以下代码

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 ); }

并且,TYPESAFE_MAPENTRY 宏在 MyClass MFC 消息映射中生成以下代码

ON_MESSAGE( MSG__on_foo, ON__on_foo ) 

MyClass 中,ON__on_foo MFC 消息处理程序接收 on_foo 消息,将参数转换为适当的数据类型,并调用 on_foo 方法。

方法 on_foo___compile_test() 从未被调用。其唯一目的是生成对 ::on_foo( hwnd, a, b ) 的调用,并强制编译器进行编译时检查,以确保在 TYPESAFE_MESSAGE_HANDLER 中指定的数据类型与 TYPESAFE_MESSAGE 中的数据类型匹配。

历史

  • 2010 年 11 月 8 日 - 修正了一些拼写错误。
© . All rights reserved.