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触发条件即可。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`SendMessage` 是 Windows API 中的一个函数,用于向一个窗口发送一条消息,并且等待该窗口处理完这个消息后才返回。`SendMessage` 的用法如下: ```c++ LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ); ``` - `hWnd`:窗口的句柄,接收消息的窗口。 - `Msg`:消息标识符,指定发送的消息类型。 - `wParam`:消息的附加信息,可以是整数、指针或句柄等。 - `lParam`:消息的附加信息,可以是整数、指针或句柄等。 `SendMessage` 函数将消息插入接收窗口的消息队列中,然后等待窗口处理完这个消息后才返回。这意味着,该函数阻塞当前线程,直到窗口处理完这个消息为止。这个函数通常用于向窗口发送同步消息,或者需要等待窗口处理完消息后才能继续执行的情况。 例如,下面的代码片段向窗口发送一条自定义消息,并且等待窗口处理完这个消息: ```c++ // 定义自定义消息标识符 #define WM_MYMESSAGE (WM_USER + 1) // 向窗口发送自定义消息,并等待窗口处理完毕 LRESULT result = SendMessage(hWnd, WM_MYMESSAGE, 0, 0); // 处理窗口返回的结果 ``` 在接收窗口的消息处理函数中,可以根据消息标识符 `WM_MYMESSAGE` 来处理这个消息,并且返回一个处理结果: ```c++ // 窗口消息处理函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_MYMESSAGE: // 处理自定义消息,并返回处理结果 return HandleMyMessage(); // 其他消息处理... default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } ``` 需要注意的是,由于 `SendMessage` 函数阻塞当前线程,所以在向其他线程的窗口发送消息时,可能导致死锁或响应性问题。在这种情况下,应该使用 `PostMessage` 函数来发送异步消息,而不是使用 `SendMessage` 函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值