飞鸽传书源码分析二消息机制

来源:互联网 发布:淘宝有多少分可以扣 编辑:程序博客网 时间:2024/05/21 20:12

转载请注明出处:http://blog.csdn.net/mxway/article/details/40225725

本篇文章是在飞鸽传书2.06源码的基础的分析的。

飞鸽传书的消息大致可分为三类:普通窗口类(后面以TMainWin为例进行分析)消息,对话框类(后面以TSendDlg为例进行分析)消息,对话框控件(后面以TEditSub为例进行分析)消息。这三类消息先合后分,这三类窗口设置的消息处理函数都是TApp::WinProc,在TApp::WinProc函数中再分发给各自的消息处理函数


                                                   图1:消息处理

首先看下三种窗口的继承关系


                       图2:TMainWin类的继承关系


                                          图3:TsendDlg继承关系


                     图4:TEditSub继承关系

从上面的三个图中可以看出,TWin是所有窗口类的根类。即在飞鸽传书的源码中,所有的窗口都需要继承TWin类。飞鸽传书所有消息都是经过TApp::WinProc传到TWin::WinProc或其子类重写的WinProc函数中。

TWin::WinProc部分源码如下:

LRESULT TWin::WinProc(UINT uMsg, WPARAM wParam, LPARAM lParam){BOOLdone = FALSE;LRESULTresult = 0;switch(uMsg){case WM_CREATE:done = EvCreate(lParam);break;        case WM_MOUSEMOVE:done = EvMouseMove((UINT)wParam, MAKEPOINTS(lParam));break;case WM_LBUTTONUP:case WM_RBUTTONUP:case WM_NCLBUTTONUP:case WM_NCRBUTTONUP:case WM_LBUTTONDOWN:case WM_RBUTTONDOWN:case WM_NCLBUTTONDOWN:case WM_NCRBUTTONDOWN:case WM_LBUTTONDBLCLK:case WM_RBUTTONDBLCLK:case WM_NCLBUTTONDBLCLK:case WM_NCRBUTTONDBLCLK:done = EventButton(uMsg, wParam, MAKEPOINTS(lParam));break;default:if (uMsg >= WM_USER && uMsg < 0x7FFF || uMsg >= 0xC000 && uMsg <= 0xFFFF)result = done = EventUser(uMsg, wParam, lParam);break;}returndone ? result : DefWindowProc(uMsg, wParam, lParam);}
从上面可以看出TWin的WM_CREATE消息交由EvCreate函数进行处理,WM_MOUSEMOVE消息交由EvMouseMove处理,鼠标左右键或双击等事件交由EventButton处理,对于用户自定义的事件交由EventUser处理。

一、TMainWin的消息处理

在上篇文章飞鸽传书程序启动过程中提到过TMsgApp::InitWindow,在这个方法中有下面的一些代码

void TMsgApp::InitWindow(void){wc.lpfnWndProc= TApp::WinProc;        ...wc.lpszClassName= class_name;        ...mainWnd = new TMainWin(nicAddr, port_no);mainWnd->Create(class_name, IP_MSG, WS_OVERLAPPEDWINDOW | (IsNewShell() ? WS_MINIMIZE : 0));}
这个就是创建飞鸽传书主窗口的函数,并设置主窗口的消息响应函数为TApp:WinProc。消息处理函数出来,那么消息循环及消息分发处理在哪呢?回到上一篇文章中飞鸽传书的程序启动过程。在TApp::Run函数中调用TApp::InitApp,然后由于虚函数的原因调用TMsgApp::InitWindow函数。调用完TMsgApp::InitWindow后又回到TApp::Run方法中继续执行后面的代码,TApp::Run后面的代码就是进行消息循环及分发。下面是TApp:Run函数的源码

int TApp::Run(void){MSGmsg;InitApp();InitWindow();while (::GetMessage(&msg, NULL, 0, 0)){if (PreProcMsg(&msg))continue;::TranslateMessage(&msg);::DispatchMessage(&msg);}returnmsg.wParam;}
TMainWin的消息处理函数及消息分发都设置好了,我们设置的消息处理函数是TApp::WinProc。那么TApp::WinProc是怎么识别出是TMainWin窗口,并把消息处理函数转到TMainWin中去做呢?先看下TApp::WinProc的源码

LRESULT CALLBACK TApp::WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){TWin *win = SearchWnd(hWnd);if (win)returnwin->WinProc(uMsg, wParam, lParam);if ((win = preWnd) != NULL){preWnd = NULL;AddWinByWnd(win, hWnd);returnwin->WinProc(uMsg, wParam, lParam);}returnDefWindowProc(hWnd, uMsg, wParam, lParam);}
飞鸽传书将所有需要进行消息处理的窗口都加到TApp的wndArray成员变量中,Search方法是从wndArray中找到中找到窗口句柄为hWnd的TWin对象返回。现在找到TMainWin的对象win,调用win->WinProc(uMsg,wParam,lParam)。由于TMainWin继承TWin类,TWin类的WinProc是虚函数,所以win->WinProc相当于调用TMainWin的WinProc方法,至此TMainWin的消息通过TApp::WinProc再回到TMainWin类中进行处理。注:为了简化问题,if(win=preWnd)!=NULL)这段代码没有进行分析,这段代码实际是对TMainWin窗口第一次消息处理进行的特殊判断。

二、TSendDlg消息处理

对话框分为模态对话框及非模态对话框。创建非模态对话框使用win API的CreateDialog(),创建模态对话框使用DialogBox()。从图2及图3中可以看出来,TMainWin及TDlg都继承TWin类,那么在调用Create函数如何区别是普通窗口或是创建对话框呢?

BOOL TWin::Create(LPCSTR className, LPCSTR title, DWORD style, DWORD exStyle, HMENU hMenu){if (className == NULL)className = TApp::defaultClass;TApp::AddWin(this);if ((hWnd = ::CreateWindowEx(exStyle, className, title, style, rect.left, rect.top, rect.right, rect.bottom, parent ? parent->hWnd : NULL, hMenu, TApp::hI, NULL)) == NULL)returnTApp::DelWin(this), FALSE;elsereturnTRUE;}

BOOL TDlg::Create(HINSTANCE hInstance){TApp::AddWin(this);if ((hWnd = ::CreateDialog(hInstance ? hInstance : TApp::hI, resId ? (LPCSTR)resId : resName, parent ? parent->hWnd : NULL, (DLGPROC)TApp::WinProc)) == NULL)returnTApp::DelWin(this), FALSE;elsereturnTRUE;}
TWin的Create是虚函数,TDlg继承TWin并重写了Create函数。创建TDlg的对象最后就会调用CreateDialog创建对话框,对于其它的继承TWin并且没有重写Create函数的类调用的就是TWin的Create创建出普通的窗口。在TDlg::Create中的CreateDialog中设置的对话框的消息处理函数为TApp::WinProc方法,在这之前调用TApp::AddWin(this)将创建的对话框对象加到TApp的wndArray成员变量中。与TMainWin窗口的消息处理相似,对话框的消息先经过TApp::WinProc。然后再传给TDlg中进行处理。还有一个很重要的问题:对话框有WM_INITDIALOG等消息是普通窗口不具有的,怎么处理这些消息呢。通过上面的分析我们知道是通过重写TWin类的WinProc虚函数。
LRESULT TDlg::WinProc(UINT uMsg, WPARAM wParam, LPARAM lParam){LRESULTresult = 0;switch (uMsg){case WM_INITDIALOG:returnEvCreate(lParam);                ...case WM_ENDSESSION:EvEndSession((BOOL)wParam, (BOOL)lParam);return0;                ...        }}
三、TEditSub窗口消息处理

在TSendDlg对话框中有一个文本框控件,该控件有一个功能就是在文本框中双击的时候选中文本框中的内容。TSendDlg对话框中只有关于对话框的消息处理,那么对话框中文本框的消息是如何进行处理的

在TSendDlg的EvCreate函数中有如下代码

BOOL TSendDlg::EvCreate(LPARAM lParam){        ...editSub.CreateByWnd(GetDlgItem(SEND_EDIT));        ...}
TEditSub继承TSubClass类,没有对CreateByWnd进行重写

BOOL TSubClass::CreateByWnd(HWND _hWnd){TApp::AddWinByWnd(this, _hWnd);return(oldProc = (WNDPROC)::SetWindowLong(_hWnd, GWL_WNDPROC, (LONG)TApp::WinProc)) ? TRUE : FALSE;}
这个功能是设置TEditSub的消息处理函数为TApp::WinProc。与前面两种窗口的处理过程相似,对于TEditSub消息先传到TApp::WinProc中,再传到TEditSub的窗口类中进行处理。注:使用SetWindowLong方法设置GWL_WNDPROC为TApp::WinProc,在TApp::WinProc(实际上是TEditSub窗口类的消息处理函数)中没有处理的消息,必须使用CallWindowProc函数调用SetWindowLong之前的消息处理函数,如果不使用CallWindowProc即使使用WIN API的DefWindowProc也会出现如文本框无法显示的问题。

TEditSub中重写了EventButton函数。

BOOL TEditSub::EventButton(UINT uMsg, int nHitTest, POINTS pos){switch (uMsg){case WM_LBUTTONDBLCLK:PostMessage(WM_EDIT_DBLCLK, 0, 0);break;}returnFALSE;}
所以在鼠标双击文本框后就会调用PostMessage(WM_EDIT_DBLCLK,0,0);触发一条自定义的消息。该自定义的消息的处理就是完成选定文本框内容的功能。

2 0
原创粉丝点击