MFC視窗之間的訊息傳遞
阿新 • • 發佈:2019-02-01
視窗訊息的傳遞
http://bbs.bccn.net/thread-348167-1-1.html
宣告:本文非本人所寫,本文已經寫在本人CSDN部落格裡。本原來源於一篇英文文章的翻譯。
訊息(Message)是視窗間通訊的最重要的方式之一。傳統的程式從main()函式處開始一行一行的執行直到退出,但是視窗的概念則不同。視窗對事件(event)進行響應,這種事件稱為訊息。事件由程式本身、其他程式或系統程式產生,這些事件又產生訊息。滑鼠移動、按鍵等都會產生事件。訊息分為兩種即視窗訊息和執行緒訊息。這裡只分析視窗訊息。
所謂的視窗訊息,大致上,必須傳遞給一個視窗。所有的訊息都儲存在訊息佇列(Message Queue)中。訊息佇列用於在應用程式之間傳遞訊息。
從訊息佇列中捕獲訊息的方式是訊息迴圈(Message Loop)。一旦一個訊息被某個視窗接受,訊息迴圈即分配此訊息並呼叫一個訊息控制代碼,有一個由程式設計師設計的函式用於處理此訊息。
訊息迴圈在接收到WM_QUIT訊息後終止,並指示程式結束。當用戶選擇File選單下的Exit子選單、點選關閉按鈕、按下alt+F4時均會產生WM_QUIT訊息。視窗有預設的訊息控制代碼用來進行預設行為。例如,按鈕(Button)派生於視窗類,當按鈕接收到WM_PAINT時會重新繪製按鈕,當左擊按鈕時會接收到WM_LBUTTONDOWN並自繪按下的按鈕形態。
視窗定義了很多型別的訊息,他們通常以“WM”開頭。例如WM_SIZE,當視窗大小發生變化時傳送此訊息。在MFC中,用On代替“WM_”,例如WM_SIZE在MFC中表示為OnSize。
一個訊息有兩個引數,這兩個引數攜帶該事件的一些資訊。每個引數均是32位寬的,lParam和wParam。有的時候訊息也會返回一個值給傳送該訊息的視窗。
MFC自動的生成了訊息迴圈所需的程式碼,WinMain呼叫的CWinApp成員函式提供訊息迴圈並將這些訊息送給各個視窗。需要我們做的只是建立訊息控制代碼,這可以藉助於ClassWizard完成。下面即是一個響應WN_CLOSE訊息的例子。
程式程式碼: view plaincopy to clipboardprint?void CAboutWindow::OnClose()
{
int Ret = MessageBox(_T("Are you sure you want to close the window?"),
_T(" Close Window?"), MB_YESNO);
if(Ret == IDYES){
// The User is sure, close the window by calling the base class
// member CWnd::OnClose()
}
else{
// The user pressed no, screen out the message by not calling
// the base class member
//Do nothing }
}
void CAboutWindow::OnClose()
{
int Ret = MessageBox(_T("Are you sure you want to close the window?"),
_T("Close Window?"), MB_YESNO);
if(Ret == IDYES){
// The User is sure, close the window by calling the base class
// member CWnd::OnClose()
}
else{
// The user pressed no, screen out the message by not calling
// the base class member
//Do nothing }
}
為了視窗間的通訊,程式設計師需要自己傳送訊息。由於訊息均是視窗傳送的,所以需要一個C++視窗指標。可以通過CWnd::FindWindow、GetDlgItem()、GetParent()等獲得視窗指標。CWnd類有一個SendMessage()成員函式用於傳送訊息給他的視窗。例如,如果有一個日曆控制元件需要去關閉,可以通過產生一個WM_CLOSE訊息去告知該控制元件。可以通過CWnd::FindWindow()傳遞一個Caption來獲得指向該控制元件的C++視窗指標。
程式程式碼: view plaincopy to clipboardprint?CWnd *pCalc;
//Get a pointer to the "Calculator" Window pCalc = CWnd::FindWindow(NULL, _T("Calculator)); if(pCalc == NULL){
//Couldn't find Calculator }
else{
pCalc->SendMessage(WM_CLOSE);
//Presto! The Calculator should close. }
當一個視窗接收到某個訊息後,MFC將呼叫類的成員函式。但是MFC如何知道該呼叫哪個函式呢?
為了解決上述問題,MFC運用了一個叫做訊息對映(Message Map)機制。訊息對映就是將訊息和所要呼叫的函式繫結在一起。一旦接受到一個訊息,MFC將進入訊息對映去尋找與該訊息相對應的訊息控制代碼。
MFC採用的是一系列的巨集(Macros)去新增訊息對映到類中。當運用ClassWizard去新增一個訊息控制代碼時首先新增該 函式到類中然後新增響應的巨集到訊息對映中。例如若用ClassWizard新增WM_CLOSE的訊息控制代碼時,會有一下三個動作:
1.在類的實現(.cpp)中的訊息對映中:
程式程式碼: view plaincopy to clipboardprint?BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg) ON_WM_CLOSE()
//}}AFX_MSG_MAP END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg) ON_WM_CLOSE()
//}}AFX_MSG_MAPEND_MESSAGE_MAP()
2.在類(.h)宣告中宣告函式:該函式前有afx_msg關鍵字
程式程式碼: view plaincopy to clipboardprint?protected:
//{{AFX_MSG(CAboutDlg) afx_msg void OnClose();
//}}AFX_MSG DECLARE_MESSAGE_MAP()
protected:
//{{AFX_MSG(CAboutDlg) afx_msg void OnClose();
//}}AFX_MSG DECLARE_MESSAGE_MAP()
3.在類的實現(.cpp)中:
程式程式碼: view plaincopy to clipboardprint?void CAboutDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
CDialog::OnClose();
}
void CAboutDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
CDialog::OnClose();
}
巨集DECLARE_MESSAGE_MAP()告知MFC給訊息對映新增必要的程式碼。
巨集BEGIN_MESSAGE_MAP()表示訊息對映的開始,括弧類的兩個引數指示了傳送訊息的類和其基類。
巨集END_MESSAGE_MAP()表示訊息對映的結束。
有一些訊息ClassWizard並不支援但是可以手工新增。但有得時候沒有訊息對映巨集,這個時候可以採用通用巨集ON_MESSAGE,利用該巨集可以傳遞任何訊息。
view plaincopy to clipboardprint?afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);
OnMessage()是控制代碼函式,ON_MESSAGE有兩個引數,控制代碼的地址和訊息。例如下面的例子將WM_GETTEXTLENGTH對映到OnGetTextLength():
程式程式碼: view plaincopy to clipboardprint?ON_MESSAGE (WM_GETTEXTLENGTH, OnGetTextLength)
ON_MESSAGE (WM_GETTEXTLENGTH, OnGetTextLength)
OnGetTextLength的原形為:
view plaincopy to clipboardprint?afx_msg LRESULT OnGetTextLength(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnGetTextLength(WPARAM wParam, LPARAM lParam);
有得時候,需要在兩個視窗或兩個應用程式間進行通訊,一個簡單的解決方法是採用自定義訊息。為了避免與系統定義的訊息衝突,需要運用一個大於0xBFF的數WM_APP。
程式程式碼: view plaincopy to clipboardprint?#define WM_DELETEALL WM_APP + 0x100
//... pYourDialog->SendMessage(WM_DELETEALL, 0, 0);
#define WM_DELETEALL WM_APP + 0x100
//...pYourDialog->SendMessage(WM_DELETEALL, 0, 0);
運用巨集WM_MESSAGE完成訊息對映,將WM_DELETEALL與控制代碼函式OnDeleteAll()繫結。
程式程式碼: view plaincopy to clipboardprint?#define WM_DELETEALL WM_APP + 0x100
//...
//Message Map entry: ON_MESSAGE (WM_DELETEALL, OnDeleteAll)
//OnDeleteAll is prototyped as afx_msg LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam);
//And is implemented as LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam){
//Do What ever you want return somevalue;
}
#define WM_DELETEALL WM_APP + 0x100
//...
//Message Map entry:ON_MESSAGE (WM_DELETEALL, OnDeleteAll)
//OnDeleteAll is prototyped asafx_msg LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam);
//And is implemented asLRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam){
//Do What ever you want return somevalue;
}
RegisterWindowMessage()用於定義一個新的視窗訊息,必須保證其獨一無二。巨集ON_REGISTERED_MESSAGE表示該訊息。例如:
view plaincopy to clipboardprint?class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd) afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd) ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP END_MESSAGE_MAP()
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd) afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd) ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAPEND_MESSAGE_MAP()
用該方式定義的訊息的範圍為0xC000 - 0xFFFF,並採用sendmessage()傳送該訊息。
程式程式碼: view plaincopy to clipboardprint?static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
//... pFindWindow->SendMessage(WM_FIND, lParam, wParam);
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
//...pFindWindow->SendMessage(WM_FIND, lParam, wParam);
宣告:本文非本人所寫,本文已經寫在本人CSDN部落格裡。本原來源於一篇英文文章的翻譯。
訊息(Message)是視窗間通訊的最重要的方式之一。傳統的程式從main()函式處開始一行一行的執行直到退出,但是視窗的概念則不同。視窗對事件(event)進行響應,這種事件稱為訊息。事件由程式本身、其他程式或系統程式產生,這些事件又產生訊息。滑鼠移動、按鍵等都會產生事件。訊息分為兩種即視窗訊息和執行緒訊息。這裡只分析視窗訊息。
所謂的視窗訊息,大致上,必須傳遞給一個視窗。所有的訊息都儲存在訊息佇列(Message Queue)中。訊息佇列用於在應用程式之間傳遞訊息。
從訊息佇列中捕獲訊息的方式是訊息迴圈(Message Loop)。一旦一個訊息被某個視窗接受,訊息迴圈即分配此訊息並呼叫一個訊息控制代碼,有一個由程式設計師設計的函式用於處理此訊息。
訊息迴圈在接收到WM_QUIT訊息後終止,並指示程式結束。當用戶選擇File選單下的Exit子選單、點選關閉按鈕、按下alt+F4時均會產生WM_QUIT訊息。視窗有預設的訊息控制代碼用來進行預設行為。例如,按鈕(Button)派生於視窗類,當按鈕接收到WM_PAINT時會重新繪製按鈕,當左擊按鈕時會接收到WM_LBUTTONDOWN並自繪按下的按鈕形態。
視窗定義了很多型別的訊息,他們通常以“WM”開頭。例如WM_SIZE,當視窗大小發生變化時傳送此訊息。在MFC中,用On代替“WM_”,例如WM_SIZE在MFC中表示為OnSize。
一個訊息有兩個引數,這兩個引數攜帶該事件的一些資訊。每個引數均是32位寬的,lParam和wParam。有的時候訊息也會返回一個值給傳送該訊息的視窗。
MFC自動的生成了訊息迴圈所需的程式碼,WinMain呼叫的CWinApp成員函式提供訊息迴圈並將這些訊息送給各個視窗。需要我們做的只是建立訊息控制代碼,這可以藉助於ClassWizard完成。下面即是一個響應WN_CLOSE訊息的例子。
程式程式碼: view plaincopy to clipboardprint?void CAboutWindow::OnClose()
{
int Ret = MessageBox(_T("Are you sure you want to close the window?"),
_T("
if(Ret == IDYES){
// The User is sure, close the window by calling the base class
// member CWnd::OnClose()
}
else{
// The user pressed no, screen out the message by not calling
// the base class member
//Do nothing }
}
void CAboutWindow::OnClose()
{
int Ret = MessageBox(_T("Are you sure you want to close the window?"),
_T("Close Window?"), MB_YESNO);
if(Ret == IDYES){
// The User is sure, close the window by calling the base class
// member CWnd::OnClose()
}
else{
// The user pressed no, screen out the message by not calling
// the base class member
//Do nothing }
}
為了視窗間的通訊,程式設計師需要自己傳送訊息。由於訊息均是視窗傳送的,所以需要一個C++視窗指標。可以通過CWnd::FindWindow、GetDlgItem()、GetParent()等獲得視窗指標。CWnd類有一個SendMessage()成員函式用於傳送訊息給他的視窗。例如,如果有一個日曆控制元件需要去關閉,可以通過產生一個WM_CLOSE訊息去告知該控制元件。可以通過CWnd::FindWindow()傳遞一個Caption來獲得指向該控制元件的C++視窗指標。
程式程式碼: view plaincopy to clipboardprint?CWnd *pCalc;
//Get a pointer to the "Calculator" Window pCalc = CWnd::FindWindow(NULL, _T("Calculator)); if(pCalc == NULL){
//Couldn't find Calculator }
else{
pCalc->SendMessage(WM_CLOSE);
//Presto! The Calculator should close. }
當一個視窗接收到某個訊息後,MFC將呼叫類的成員函式。但是MFC如何知道該呼叫哪個函式呢?
為了解決上述問題,MFC運用了一個叫做訊息對映(Message Map)機制。訊息對映就是將訊息和所要呼叫的函式繫結在一起。一旦接受到一個訊息,MFC將進入訊息對映去尋找與該訊息相對應的訊息控制代碼。
MFC採用的是一系列的巨集(Macros)去新增訊息對映到類中。當運用ClassWizard去新增一個訊息控制代碼時首先新增該 函式到類中然後新增響應的巨集到訊息對映中。例如若用ClassWizard新增WM_CLOSE的訊息控制代碼時,會有一下三個動作:
1.在類的實現(.cpp)中的訊息對映中:
程式程式碼: view plaincopy to clipboardprint?BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg) ON_WM_CLOSE()
//}}AFX_MSG_MAP END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg) ON_WM_CLOSE()
//}}AFX_MSG_MAPEND_MESSAGE_MAP()
2.在類(.h)宣告中宣告函式:該函式前有afx_msg關鍵字
程式程式碼: view plaincopy to clipboardprint?protected:
//{{AFX_MSG(CAboutDlg) afx_msg void OnClose();
//}}AFX_MSG DECLARE_MESSAGE_MAP()
protected:
//{{AFX_MSG(CAboutDlg) afx_msg void OnClose();
//}}AFX_MSG DECLARE_MESSAGE_MAP()
3.在類的實現(.cpp)中:
程式程式碼: view plaincopy to clipboardprint?void CAboutDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
CDialog::OnClose();
}
void CAboutDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
CDialog::OnClose();
}
巨集DECLARE_MESSAGE_MAP()告知MFC給訊息對映新增必要的程式碼。
巨集BEGIN_MESSAGE_MAP()表示訊息對映的開始,括弧類的兩個引數指示了傳送訊息的類和其基類。
巨集END_MESSAGE_MAP()表示訊息對映的結束。
有一些訊息ClassWizard並不支援但是可以手工新增。但有得時候沒有訊息對映巨集,這個時候可以採用通用巨集ON_MESSAGE,利用該巨集可以傳遞任何訊息。
view plaincopy to clipboardprint?afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);
OnMessage()是控制代碼函式,ON_MESSAGE有兩個引數,控制代碼的地址和訊息。例如下面的例子將WM_GETTEXTLENGTH對映到OnGetTextLength():
程式程式碼: view plaincopy to clipboardprint?ON_MESSAGE (WM_GETTEXTLENGTH, OnGetTextLength)
ON_MESSAGE (WM_GETTEXTLENGTH, OnGetTextLength)
OnGetTextLength的原形為:
view plaincopy to clipboardprint?afx_msg LRESULT OnGetTextLength(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnGetTextLength(WPARAM wParam, LPARAM lParam);
有得時候,需要在兩個視窗或兩個應用程式間進行通訊,一個簡單的解決方法是採用自定義訊息。為了避免與系統定義的訊息衝突,需要運用一個大於0xBFF的數WM_APP。
程式程式碼: view plaincopy to clipboardprint?#define WM_DELETEALL WM_APP + 0x100
//... pYourDialog->SendMessage(WM_DELETEALL, 0, 0);
#define WM_DELETEALL WM_APP + 0x100
//...pYourDialog->SendMessage(WM_DELETEALL, 0, 0);
運用巨集WM_MESSAGE完成訊息對映,將WM_DELETEALL與控制代碼函式OnDeleteAll()繫結。
程式程式碼: view plaincopy to clipboardprint?#define WM_DELETEALL WM_APP + 0x100
//...
//Message Map entry: ON_MESSAGE (WM_DELETEALL, OnDeleteAll)
//OnDeleteAll is prototyped as afx_msg LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam);
//And is implemented as LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam){
//Do What ever you want return somevalue;
}
#define WM_DELETEALL WM_APP + 0x100
//...
//Message Map entry:ON_MESSAGE (WM_DELETEALL, OnDeleteAll)
//OnDeleteAll is prototyped asafx_msg LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam);
//And is implemented asLRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam){
//Do What ever you want return somevalue;
}
RegisterWindowMessage()用於定義一個新的視窗訊息,必須保證其獨一無二。巨集ON_REGISTERED_MESSAGE表示該訊息。例如:
view plaincopy to clipboardprint?class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd) afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd) ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP END_MESSAGE_MAP()
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd) afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd) ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAPEND_MESSAGE_MAP()
用該方式定義的訊息的範圍為0xC000 - 0xFFFF,並採用sendmessage()傳送該訊息。
程式程式碼: view plaincopy to clipboardprint?static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
//... pFindWindow->SendMessage(WM_FIND, lParam, wParam);
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
//...pFindWindow->SendMessage(WM_FIND, lParam, wParam);