Winapi Win32 No MFC中的消息映射
如何创建类似的结构来处理Win32消息,就像在MFC中一样 在MFC中Winapi Win32 No MFC中的消息映射,winapi,map,message,Winapi,Map,Message,如何创建类似的结构来处理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
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怎么办 您可以使用类似std::map的东西
if ( messageMap.find( uMsg ) != messageMap.end() )
{
messageMap[uMsg]( wParam, lParam );
}
它不会很整洁,但是很容易实现,尽管您将在运行时而不是在编译时定义消息映射
另一个解决方案是通读MFC宏代码,看看微软是如何做到的
另一个解决方案是使用ATL,如果您想要类似MFC的行为而不需要额外的开销。您还可以查看ATL的宏定义,了解它们是如何实现的
编辑:您可以通过存储CommandMap和NotifyMap来解决WM_命令或WM_通知处理。然后将WM_命令处理程序设置为一个函数,该函数执行类似的操作,并通过CommandMap传递命令
您最大的问题是在消息中没有得到任何标识特定类实例的信息。如果您只需要hWnd,但可能需要存储hWnd到类实例的进一步全局映射,那么这不是问题
这只是一个解决方案。你可以用许多不同的方法来解决这个问题。我只是想为您提出一个想法。MFC消息映射本身并不使用常规的WndProc。IIRC,它基于某种钩子机制 然而,我想将宏调整为普通的WndProc应该不是很难 想到的第一种方法是让宏创建消息id/处理程序函数对数组。更好的方法是:使用地图来提高性能 WndProc将在该数组中循环,以识别给定的
WM
,并执行相应的处理程序
您可能还希望BEGIN_MESSAGE_MAP
宏模拟一个switch
语句,其中,都是开关()内的一个case
行
这应该太难了。像std::map这样的东西很难做到这一点。特别是,这要求映射中的每个项都具有相同的类型,但不同的消息具有采用不同数量参数的处理程序,因此指向它们的指针不是相同的类型
不过,您可能想看看windowsx.h中的消息破解宏(尤其是HANDLE\u MSG
)。虽然这实际上只是为switch语句生成case's,但它仍然允许您编写类似于MFC消息映射的代码。以下是我在程序员编辑器中用于执行此操作的代码的breif摘要:
步骤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步:定义静态窗口过程方法(注意:首次注册窗口类时,需要将此方法用作窗口类的窗口过程):
现在,使用这个基类可以定义一个新的窗口类,如下所示:
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方法
dispatch方法更简单,因为它只是将Windows消息打包成一个易于移动的结构,然后将其发送给DispatchTomsMap方法
现在查看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)
这段代码只是使用前面定义的宏。如果仔细查看这些宏,您会发现上面的代码实际上是在创建DispatchTomsMap方法。这与dispatch方法调用的DispatchTomsMap方法完全相同
我知道这种处理Windows消息的方法确实有效,因为我在编辑器中使用了完全相同的方法。为什么不查看页眉,看看那些宏扩展到什么?事实上,我很难查看和跟踪到根。我是Win32和MFCBut的初学者,这里缺少一些东西,例如,我想将一个函数绑定到特定的按钮单击事件。在你的建议中,每一个事件都指向一个单一的功能。应该有另一个参数,但我认为map还不够?好吧,你的答案似乎是所有功能中最好的,再次感谢你嗨,你编译了这个源代码吗?当我把这个加入到我的项目中时,我有很多错误吗?是否应该考虑编译问题?我上面帖子中的代码取自我在编写ZeusforWindows编辑器时创建的Win32库。我只复制了非常大的库代码的一小部分,所以我不希望编译此代码。例如,上面显示的xWindow类实际上是大约2000多行代码,因此它自然地被大大减少了。但我试图发布的是如何将Windows消息映射到C++方法的基本概念。我编辑了帖子并添加了几个单词,试图更好地描述该代码如何实际工作。这不是一个真正的问题;您可以将获得的原始WPARAM/LPARAM交给每个消息处理程序。这样,就可以将不同的强制转换移动到相应的消息处理程序。
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)