首页 > 代码库 > 飞鸽传书源码分析二消息机制

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

转载请注明出处: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)
{
	BOOL	done = FALSE;
	LRESULT	result = 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;
	}
	return	done ? 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)
{
	MSG		msg;

	InitApp();
	InitWindow();

	while (::GetMessage(&msg, NULL, 0, 0))
	{
		if (PreProcMsg(&msg))
			continue;

		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}

	return	msg.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)
		return	win->WinProc(uMsg, wParam, lParam);

	if ((win = preWnd) != NULL)
	{
		preWnd = NULL;
		AddWinByWnd(win, hWnd);
		return	win->WinProc(uMsg, wParam, lParam);
	}

	return	DefWindowProc(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)
		return	TApp::DelWin(this), FALSE;
	else
		return	TRUE;
}

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)
		return	TApp::DelWin(this), FALSE;
	else
		return	TRUE;
}
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)
{
	LRESULT	result = 0;

	switch (uMsg)
	{
	case WM_INITDIALOG:
		return	EvCreate(lParam);
                ...
	case WM_ENDSESSION:
		EvEndSession((BOOL)wParam, (BOOL)lParam);
		return	0;
                ...
        }
}
三、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;
	}
	return	FALSE;
}
所以在鼠标双击文本框后就会调用PostMessage(WM_EDIT_DBLCLK,0,0);触发一条自定义的消息。该自定义的消息的处理就是完成选定文本框内容的功能。

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