一、自定义命令
为了使用自定义命令,一般要经过如下几个步骤:
(1)自定义命令ID
命令ID是WORD型的。虽然可以自由设定其值,但是不能和应用程序中其他命令ID冲突。
在资源文件中新建一个命令,例如#define ID_LOADTOIE 8888
(2)添加自定义消息映射项
BEGIN_MESSAGE_MAP(CMsgDialog,CDialog)
ON_COMMAND(ID_LOADTOIE,LoadToIE)
END_MESSAGE_MAP()
(3)发送自定义命令
处理命令消息WM_COMMAND的窗口过程形式如下:
LRESULT CALLBACK WindowProc( HWND hWnd, //窗口句柄 WM_COMMAND, //命令的消息标志固定为WM_COMMAND WPARAM wParam, //通知代码(菜单、组合键命令)或标志(控件通知) LPRARAM lParam, //控件句柄 );
wParam分成高低位两部分。如果为控件命令,则其高位部分为控件的通知代码;如果为加速键命令,则其高位部分为1;如果为菜单命令,其高位部分为0。wParam的低位部分表示控件、菜单或加速键的ID。
对于控件命令,lParam表示控件句柄;否则lParam置为NULL。
MAKEWPARAM宏可以将两个WORD拼成一个WPARAM参数:
WPARAM MAKEWPARAM( WORD wLow, //低位 WORD wHigh, //高位 );
发送自定义命令如下:
(4)处理自定义命令
命令处理成员的名字是任意的,但其签名是固定的。
Example:
.h void LoadToIE(); .cpp BEGIN_MESSAGE_MAP(CMsgStudyDlg, CDialog) //{{AFX_MSG_MAP(CMsgStudyDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_COMMAND_RANGE(IDC_BUTTON1,IDC_BUTTON4,OnButton) ON_COMMAND_EX(ID_HELP,OnHelp) ON_COMMAND(ID_LOADTOIE,LoadToIE) END_MESSAGE_MAP() UINT DownLoadURLThreadFunc(LPVOID obj) { ASSERT(obj); CMsgStudyDlg * dlg = (CMsgStudyDlg *)obj; dlg->PostMessage(WM_COMMAND,MAKEWPARAM(ID_LOADTOIE,0),NULL); return 1; } void CMsgStudyDlg::OnButton(UINT nID) { CWinThread * pThread; switch(nID) { case IDC_BUTTON1: AfxMessageBox("This is BUTTON1"); pThread = AfxBeginThread(DownLoadURLThreadFunc,this); break; case IDC_BUTTON2: AfxMessageBox("This is BUTTON2"); break; case IDC_BUTTON3: AfxMessageBox("This is BUTTON3"); break; case IDC_BUTTON4: AfxMessageBox("This is BUTTON4"); break; default: break; } } BOOL CMsgStudyDlg::OnHelp(UINT nID) { AfxMessageBox("This is CMsgStudyDlg"); //返回FALSE,允许沿着处理链继续处理 return FALSE; } void CMsgStudyDlg::LoadToIE() { AfxMessageBox("OK"); }
二、自定义窗口消息
操作系统保留一定范围的窗口消息标记作为自定义消息,自定义窗口消息的消息标志都大于WM_USER。
一旦有了消息标志,就可以用目标窗口对象的SendMessage或PostMessage发送(或投递)消息,以请求目标窗口的窗口过程对该消息进行处理。
目标窗口为了将自定义窗口消息和消息处理成员关联起来,要用到ON_MESSAGE消息映射宏:
#define ON_MESSAGE(message,memberFxn) {message,0,0,0,AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW)static_cast<LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM,LPARAM)>(memberFxn))},
该宏规定了处理自定义窗口消息的成员必须满足AfxSig_lwl规定的签名:LRESULT(WPARAM,LPARAM),参数WPARAM和LPARAM包含了跟自定义消息相关的特定数据。
(1)定义消息标记
#define WM_MYMSG (WM_USER + 100)
如果仅在对话框内部使用自定义窗口消息,只需要在对话框的实现文件(.cpp)中添加自定义窗口消息的定义;如果在应用程序范围内使用自定义窗口消息,需要把自定义消息的定义放到Resource.h中。
(2)修改消息映射表
在对话框的消息映射表中加入一个ON_MESSAGE映射项:
BEGIN_MESSAGE_MAP(CMsgStudyDlg,CDialog) //... //使用自定义消息 ON_MESSAGE(WM_MYMSG,OnMyMessage) END_MESSAGE_MAP()
(3)添加自定义消息的成员函数
.h //头文件中的声明 afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); .cpp //实现文件中的实现 LRESULT CMsgStudyDlg::OnMyMessage(WPARAM wParam, LPARAM lParam) { return 0L; }
自定义消息的处理函数首先从消息参数中获取了消息参数(存放在LPARAM中)。消息处理函数知道消息参数为何种类型,并将消息参数造型设定为约定的类型。
(4)引发自定义消息
this->SendMessage(WM_MYMSG,0,(LPARAM)(&dt)); 或 this->PostMessage(WM_MYMSG,0,(LPARAM)(&dt));
注意:SendMessage的消息参数指针&dt既可以引用在堆栈上分配的对象,又可以引用在C++运行堆上分配的对象;PostMessage的消息参数指针&dt只能引用在C++运行堆上分配的对象。因为SendMessage函数等待消息响应完成后才返回,而PostMessage函数不需要等待消息响应,所以在堆栈上分配的对象,在函数返回后,会立即被自动销毁,C++运行堆上的对象必须手动销毁。
Example:
.h // Implementation protected: HICON m_hIcon; // Generated message map functions //{{AFX_MSG(CMsgStudyDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnButton(UINT nID); afx_msg BOOL OnHelp(UINT nID); afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); //}}AFX_MSG DECLARE_MESSAGE_MAP() void LoadToIE(); .cpp #define WM_MYMSG (WM_USER + 100) BEGIN_MESSAGE_MAP(CMsgStudyDlg, CDialog) //{{AFX_MSG_MAP(CMsgStudyDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_COMMAND_RANGE(IDC_BUTTON1,IDC_BUTTON4,OnButton) ON_COMMAND_EX(ID_HELP,OnHelp) ON_COMMAND(ID_LOADTOIE,LoadToIE) ON_MESSAGE(WM_MYMSG,OnMyMessage) END_MESSAGE_MAP() void CMsgStudyDlg::OnButton(UINT nID) { CWinThread * pThread; CTime dt; CTime * pTime = NULL; switch(nID) { case IDC_BUTTON1: AfxMessageBox("This is BUTTON1"); pThread = AfxBeginThread(DownLoadURLThreadFunc,this); break; case IDC_BUTTON2: AfxMessageBox("This is BUTTON2"); dt = dt.GetCurrentTime(); //设置消息参数 this->SendMessage(WM_MYMSG,0,(LPARAM)&dt); break; case IDC_BUTTON3: AfxMessageBox("This is BUTTON3"); dt = dt.GetCurrentTime(); pTime = new CTime(dt); this->PostMessage(WM_MYMSG,1,(LPARAM)pTime); break; case IDC_BUTTON4: AfxMessageBox("This is BUTTON4"); break; default: break; } } LRESULT CMsgStudyDlg::OnMyMessage(WPARAM wParam, LPARAM lParam) { CTime * pTime = (CTime *)lParam; CString m_strTime; m_strTime.Format("发出消息的时间是:%d时: %d分",pTime->GetHour(),pTime->GetMinute()); AfxMessageBox(m_strTime); if(wParam == 1) { delete pTime; } return 0L; }
自定义窗口消息的总结:
(1)使用自定义消息请求窗口进行某种特殊的操作。
(2)自定义消息的消息标志必须大于WM_USER。
(3)可以利用消息参数携带数据,注意消息参数的内存管理,保证既不发生内存泄露,又能使消息参数有效。
三、登记消息
自定义消息是局部范围的,同一应用程序中的消息窗口很容易利用自定义消息进行通信,但是如果要在两个进程之间通信,自定义消息就无能为力了,需要使用登记(或注册)消息。
(1)为了使用登记消息,首先要获得一个登记消息标记,该消息标记在整个系统内是唯一的。通过调用系统API RegisterWindowMessage,可以获得一个登记消息标记:
UINT RegisterWindowMessage( LPCTSTR lpString //消息字符串 );
(2)在消息映射表中手工添加ON_REGISTERED_MESSAGE消息映射宏,添加登记消息的处理函数
#define ON_REGISTERED_MESSAGE(nMessageVariable,memberFxn) {0xC000,0,0,0,(UINT_PTR)(UINT *)(&nMessageVariable), (AFX_PMSG)(AFX_PWMSG)(static_cast<LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM,LPARAM)>(memberFxn))},
(3)处理登记消息的成员函数
其定义表明处理登记消息的成员函数具有如下签名:LRESULT(WPARAM wParam,LPARAM lParam)
Example:
.h // Generated message map functions //{{AFX_MSG(CMsgStudyDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnButton(UINT nID); afx_msg BOOL OnHelp(UINT nID); afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnMyRegisteredMsg(WPARAM wParam,LPARAM lParam); //}}AFX_MSG DECLARE_MESSAGE_MAP() .cpp //1. static UINT NEAR WM_MYREGISTEREDMSG = RegisterWindowMessage("WM_MYREGISTEREDMSG"); //2. BEGIN_MESSAGE_MAP(CMsgStudyDlg, CDialog) //{{AFX_MSG_MAP(CMsgStudyDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_COMMAND_RANGE(IDC_BUTTON1,IDC_BUTTON4,OnButton) ON_COMMAND_EX(ID_HELP,OnHelp) ON_COMMAND(ID_LOADTOIE,LoadToIE) ON_MESSAGE(WM_MYMSG,OnMyMessage) ON_REGISTERED_MESSAGE(WM_MYREGISTEREDMSG,OnMyRegisteredMsg) END_MESSAGE_MAP() //3. void CMsgStudyDlg::OnButton(UINT nID) { CWinThread * pThread; CTime dt; CTime * pTime = NULL; CWnd * pWnd = NULL; TCHAR className[256]; CString title; switch(nID) { case IDC_BUTTON1: AfxMessageBox("This is BUTTON1"); pThread = AfxBeginThread(DownLoadURLThreadFunc,this); break; case IDC_BUTTON2: AfxMessageBox("This is BUTTON2"); dt = dt.GetCurrentTime(); //设置消息参数 this->SendMessage(WM_MYMSG,0,(LPARAM)&dt); break; case IDC_BUTTON3: AfxMessageBox("This is BUTTON3"); dt = dt.GetCurrentTime(); pTime = new CTime(dt); this->PostMessage(WM_MYMSG,1,(LPARAM)pTime); break; case IDC_BUTTON4: AfxMessageBox("This is BUTTON4"); this->GetWindowText(title); ::GetClassName(this->m_hWnd,className,255); pWnd = this->FindWindow(className,title); ASSERT(pWnd); pWnd->SendMessage(WM_MYREGISTEREDMSG); break; default: break; } } //4. LRESULT CMsgStudyDlg::OnMyRegisteredMsg(WPARAM wParam,LPARAM lParam) { AfxMessageBox("收到自定义的登记消息"); return 0L; }