SendMessage不为人知的秘密

        SendMessage的说明我这里就不再复述了,相信大家应该都很了解,写这篇文件的目的就是让大家更好的了解SendMessage机制和运行原理,好吧我们开始吧。

SendMessage的调用如果是在窗口消息线程本身调用时是直接调用程序的WinProc的消息处理函数的,那么在其他线程是怎么样的呢,那么这个问题就是我写这篇文章的主要目的。当系统发现调用SendMessage的代码处于非窗口线程那么该线程就会一直等待窗口线程来处理发送的消息,当我们完成分发的消息处理时,放回到消息循环的GetMessage或PeekMessage中时SendMessage的消息才会被处罚执行。那么这里就有疑问来了,我们在看MSDN或者自己理解的SendMessage是不进入消息循环是被立即调用的,那么应该是不会跟GetMessage和PeekMessage相关的,为什么会扯上关系呢?

        上面的疑问可能不是很妥当,那么我们换个说法吧?我们还是来讲讲这个问题的产生原因,首先我们创建一个线程执行函数,该函数用于执行SendMessage操作,代码定义如下:

#define WM_TEST  WM_USER + 101

DWORD WINAPI MyThreadTest(LPVOID pParam)
{
	LPARAM lParam = (LPARAM)pParam;

	printf("Start SendMessage, lparam:%d!\n", lParam);

	SendMessage(g_hWnd, WM_TEST, 0, lParam);

	printf("End SendMessage, lparam:%d!\n", lParam);

	return 0;
}

然后在某个地方去调用这个SendMessage的线程,我这边是在Timer消息这块调用的,调用代码如下:

case WM_TIMER:

		for (int i = 0; i < 10; i++)
		{
			CloseHandle(CreateThread(NULL, 0, MyThreadTest, (LPVOID)i, 0, 0));

			Sleep(100);
		}
		
		printf("Start Call GetMessage Or PeekMessage!\n");

		MSG msg;
		//GetMessage(&msg, g_hWnd, 0, 0);
		PeekMessage(&msg, g_hWnd, WM_LBUTTONUP, WM_LBUTTONUP, PM_REMOVE);
		
		printf("End Call GetMessage Or PeekMessage!\n");

		KillTimer(g_hWnd, wParam);
		break;
	case WM_TEST:
		printf("Handler WM_TEST Message, lparam:%x!\n", lParam);
		break;


我们在处理Timer消息时,创建10个SendMessage的线程,那么每个SendMessage肯定会在等待消息循环的结束来执行Send的消息,当我们执行完线程的创建工作后,就会调用到PeekMessage,这个时候我们刚才创建的SendMessage线程发送的消息全部被触发,而且也是跟发送的次序保持一致的,打印消息如下:

Start SendMessage, lparam:0!
Start SendMessage, lparam:1!
Start SendMessage, lparam:2!
Start SendMessage, lparam:3!
Start SendMessage, lparam:4!
Start SendMessage, lparam:5!
Start SendMessage, lparam:6!
Start SendMessage, lparam:7!
Start SendMessage, lparam:8!
Start SendMessage, lparam:9!


Start Call GetMessage Or PeekMessage!


Handler WM_TEST Message, lparam:0!
End SendMessage, lparam:0!
Handler WM_TEST Message, lparam:1!
End SendMessage, lparam:1!
Handler WM_TEST Message, lparam:2!
End SendMessage, lparam:2!
Handler WM_TEST Message, lparam:3!
End SendMessage, lparam:3!
Handler WM_TEST Message, lparam:4!
End SendMessage, lparam:4!
Handler WM_TEST Message, lparam:5!
Handler WM_TEST Message, lparam:6!
End SendMessage, lparam:6!
Handler WM_TEST Message, lparam:7!
End SendMessage, lparam:5!
Handler WM_TEST Message, lparam:8!
Handler WM_TEST Message, lparam:9!
End SendMessage, lparam:8!
End SendMessage, lparam:9!
End SendMessage, lparam:7!


End Call GetMessage Or PeekMessage!


从上面的输出我们可以很清楚的认识到,在其他线程SendMessage的消息的执行是从消息循环取消息时触发的,而且内部应该也是维护了一个消息队列的东西,或者可以认为其他线程发生消息到窗口消息线程也是放入到消息队列的,只是这个消息处理的优先级大于PostMessage存储到消息队里的消息,上面打印的结果顺序有些地方颠倒了,可能是多线程输出的问题,测试时多次输出的顺序均不相同,我们只需要关注SendMessage触发条件即可。

竟然了解了上面的原理,那么我们在写基于消息驱动的应用程序时应该要注意下多线程发送消息的问题,防止消息的执行顺序发生混乱,目前我们现实项目中就遇到了这个问题,所以总结到这里来,也希望能帮助到大家,同时也欢迎各位探讨。

你可能感兴趣的:(windows)