5

如何创建类似的结构来处理 Win32 消息,就像它在 MFC 中一样?

在 MFC 中;

BEGIN_MESSAGE_MAP(CSkinCtrlTestDlg, CDialog)
    //{{AFX_MSG_MAP(CSkinCtrlTestDlg)
    ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
    ON_BN_CLICKED(IDC_DEFAULTSKIN, OnChangeSkin)
    ON_WM_DRAWITEM()
    ON_WM_MEASUREITEM()
    ON_WM_COMPAREITEM()
    ON_BN_CLICKED(IDC_CHECK3, OnCheck3)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP 宏处理此行为。纯Win32怎么办?

4

4 回答 4

14

以下是我在Zeus程序员编辑器中用于执行此操作的代码的简要摘要:

第 1 步:定义几个消息结构来保存 Windows 消息详细信息:

typedef struct
{
  MSG     msg;
  LRESULT lResult;
} xMessage;

struct xWM_COMMAND
{
  HWND hwnd;
  UINT Msg;
  WORD ItemID;
  WORD NotifyCode;
  HWND Ctl;
  LRESULT lResult;
};

//-- unpack a message buffer
#define MSG_UNPACK(var, id, msg) x##id *var = (x##id *)(msg);

第 2 步:使用一些特殊方法定义一个基窗口类:

class xWindow
{
protected:
  //-- windows callback function
  static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, 
                                  WPARAM wParam, 
                                  LPARAM lParam);

  //-- a message dispatch method
  void dispatch(HWND hwnd, UINT uMessageID, WPARAM wParam, 
                LPARAM lParam, LRESULT &Result);

  //-- method for command message dispatching
  virtual void dispatchToCmdMap(xMessage *pMessage);

  //-- method for windows message dispatching
  virtual void dispatchToMsgMap(xMessage *pMessage);
};

第 3 步:定义一些宏来发送 Windows 消息:

#define BEGIN_MSG_MAP                          \
   protected:                                  \
   virtual void dispatchToMsgMap(xMessage *msg)\
   {                                           \
     if (msg->msg.message == WM_NULL)          \
     {                                         \
       return;                                 \
     }

#define MSG_HANDLER(meth, wm_msg)              \
     else if (msg->msg.message == wm_msg)      \
     {                                         \
       this->meth(msg);                        \
       return;                                 \
     }

#define END_MSG_MAP(base)                      \
     else if (msg->msg.message == WM_COMMAND)  \
     {                                         \                       
       this->dispatchToCmdMap(msg);            \                       
       return;                                 \                       
     }                                         \                       
     else if (msg->msg.message == WM_NOTIFY)   \                       
     {                                         \                       
       this->dispatchToNotifyMap(msg);         \                       
       return;                                 \                       
     }                                         \                       
                                               \                       
     base::dispatchToMsgMap(msg);              \                       
   };

#define BEGIN_CMD_MAP                          \
   virtual void dispatchToCmdMap(xMessage *msg)\
   {                                           \                              
     MSG_UNPACK(Cmd, WM_COMMAND, msg);         \                              
                                               \                              
     if (Cmd->ItemID == 0)                     \                              
     {                                         \                              
        /* not allowed */                      \                              
     }                                                                        

#define CMD_HANDLER(meth, cmd_id)              \
     else if (Cmd->ItemID == cmd_id)           \
     {                                         \                                
       this->meth(Cmd->ItemID);                \                                
     }                                                                          

#define END_CMD_MAP(base)                      \
     else                                      \                              
     {                                         \                              
       base::dispatchToCmdMap(msg);        \                              
     }                                         \                              
   };

第 4 步:定义调度程序方法:

void xWindow::dispatch(HWND, UINT uMessageID, WPARAM wParam, 
                       LPARAM lParam, LRESULT &Result)
{
  xMessage message;

  //-- build up a message packet
  message.msg.message = uMessageID;
  message.msg.wParam  = wParam;
  message.msg.lParam  = lParam;
  message.lResult     = 0;

  //-- dispatch the message
  this->dispatchToMsgMap(&message);
}

步骤 5:定义静态窗口过程方法(注意:该方法需要在第一次注册类时用作窗口类的 Window 过程):

LRESULT CALLBACK xWindow::wndProc(HWND hwnd, UINT msg, 
                                  WPARAM wParam, 
                                  LPARAM lParam)
{
  LRESULT lResult = 0;

  //-- look for the creation message
  if (msg == WM_NCCREATE)
  {
    CREATESTRUCT *pCreateData = (CREATESTRUCT*)lParam;

    //-- get the window object passed in
    xWindow *pWindow = (xWindow)pCreateData->lpCreateParams;

    if (pWindow)
    {
      //-- attach the window object to the hwnd
      SetWindowLong(hwnd, pWindow);

      //-- let the window object dispatch the message
      pWindow->dispatch(hwnd, msg, wParam, lParam, lResult);
    }
    else
    {
      //-- leave the message to windows
      lResult = DefWindowProc(hwnd, msg, wParam, lParam);
    }
  }
  else if (hwnd)
  {
    //-- get the object attached to the hwnd
    xWindow *pWindow = (xWindow *)GetWindowLong(hwnd);

    //-- check to see if we have an object window attached to the handle
    if (pWindow)
    {
      //-- let the window object dispatch the message
      pWindow->dispatch(hwnd, msg, wParam, lParam, lResult);
    }
    else
    {
      //-- leave the message to windows
      lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
    }
  }

  return lResult;
}

现在,使用这个基类可以定义一个新的窗口类,如下所示:

class MyWindow : public xWindow
{
protected:  
  //-- the WM_COMMAND message handlers
  virtual void onAdd(int);
  virtual void onDelete(int);

  //-- the WM_CLOSE message handler
  virtual void onClose(xMessage *pMessage);

  //-- the WM_SIZE message handler
  virtual void onSize(xMessage *pMessage);

public:
  //-- ctor and dtor
  MyWindow();
  virtual ~MyWindow();

  BEGIN_MSG_MAP
    //-- command message handlers
    CMD_HANDLER(onAdd   , IDPB_ADD   )
    CMD_HANDLER(onDelete, IDPB_DELETE)

    //-- other message handling
    MSG_HANDLER(onClose , WM_CLOSE)
    MSG_HANDLER(onSize  , WM_SIZE )
  END_MSG_MAP(xWindow)
};

编辑:此代码如何工作。

理解此代码如何工作的秘诀是记住xWindow类中的wndProc只不过是在注册 Win32 窗口时传递给RegisterClassEx的Win32窗口过程。

现在,如果您查看wndProc代码,您会看到它进行了一些设置和检查,但通常它只是将 Windows 消息发送到调度方法。

dispatch方法更简单,因为它只是将 Windows 消息打包到一个易于移动的结构中,然后将其发送到dispatchToMsgMap方法。

现在查看MyWindow类,您将看到以下代码:

BEGIN_MSG_MAP    
   //-- command message handlers    
   CMD_HANDLER(onAdd   , IDPB_ADD   )    
   CMD_HANDLER(onDelete, IDPB_DELETE)    

   //-- other message handling    
   MSG_HANDLER(onClose , WM_CLOSE)    
   MSG_HANDLER(onSize  , WM_SIZE )  
END_MSG_MAP(xWindow)

这段代码只是使用了前面定义的宏。如果您仔细查看这些宏,您会发现上面的代码实际上是在创建一个dispatchToMsgMap方法。这与dispatch方法调用的dispatchToMsgMap方法完全相同。

我知道这种处理 Windows 消息的方法确实有效,因为我在Zeus for Windows编辑器中使用了完全相同的方法。

于 2009-12-08T00:14:02.330 回答
0

您可以使用 std::map< short, MessageFn > 之类的东西。其中 short 是窗口消息,MessageFn 是处理消息的函数。然后,您可以按如下方式处理消息:

if ( messageMap.find( uMsg ) != messageMap.end() )
{
   messageMap[uMsg]( wParam, lParam );
}

它不会那么整洁,但很容易实现,尽管您将在运行时而不是在编译时定义消息映射。

另一种解决方案是通读 MFC 宏代码,看看微软是如何做到的......

另一种解决方案是使用 ATL,如果您想要类似 MFC 的行为而没有开销。您还可以查看 ATL 的宏定义,看看他们是如何做到的......

编辑:您也可以通过存储 CommandMap 和 NotifyMap 来解决 WM_COMMAND 或 WM_NOTIFY 处理。然后,您将 WM_COMMAND 处理程序设置为一个函数,然后该函数执行类似的操作并通过 CommandMap 传递命令。

您最大的问题是您在消息中没有得到任何标识特定类实例的信息。如果您只需要 hWnd,这不是问题,但您可能需要将 HWND 的进一步全局映射存储到类实例......

这只是一种解决方案。您可以通过许多不同的方式解决问题。我只是为你抛出一个想法。

于 2009-12-07T21:58:38.180 回答
0

MFC 消息映射本身不使用常规的 WndProc。IIRC,它基于某种钩子机制。

但是,我想将宏适应通常的 WndProc 应该不是很难。

想到的第一种方法是让宏创建消息 id/Handler 函数对的数组。更好的是:使用地图来提高性能。

您的 WndProc 将遍历该数组以识别给定的WM并执行相应的处理程序。

您可能还希望BEGIN_MESSAGE_MAP宏模拟一个switch语句,其中每一ON_BLAH()行都是caseswitch() 中的一行。

那应该太难了。

于 2009-12-07T22:02:03.920 回答
0

用类似的东西很难做到这一点std::map。特别是,这要求映射中的每个项目都具有相同的类型,但不同的消息具有采用不同数量参数的处理程序,因此指向它们的指针不是相同的类型。

不过,您可能想看看HANDLE_MSGwindowsx.h 中的消息破解器宏(尤其是 )。尽管这实际上只是为 switch 语句生成 case,但它仍然允许您编写看起来像 MFC 消息映射的代码。

于 2009-12-07T22:11:34.630 回答