PostMessage与SendMessage各自的问题

深入解析SendMessage、PostMessage

本文将使用C++语言,在MFC框架的配合下给出PostMessage、SendMessage等的使用方式与使用不当造成的后果(讨论均针对自定义的消息进行)。如有什么错误,欢迎指正。

写过Windows程序的同学都知道PostMessage、SendMessage的区别,PostMessage函数调用发送之后,立即返回,不等待消息处理完成。而SendMessage则让调用的线程处于阻塞(BLOCk)状态,直到消息处理完成。

正由于这两个函数的区别导致了如下想法:

想法1:PostMessage立即返回,在程序中,处理界面显示(如处理进度条、滚动条等)时使用PostMessage,不会影响程序的用户体验。

想法2:在程序中全用PostMessage,放弃SendMessage,好处:PostMessage是立即返回的,可以不影响程序的正常流程,就算在消息处理函数中卡死了,也不影响主线程的运行。

起初“学习”到了这些想法,以为受益匪浅,但经过一段时间之后,发现此两种想法都是不可取的。

分析想法1:

这里可分为两点:

1)  在主线程中Post消息,以处理进度条显示(用WM_MY_TEST的参数WPARAM、LPARAM来处理进度条的显示)

代码:code_1

#define WM_MY_TEST (WM_USER + 100)

void CMyDlg::OnBnClickedOk()

{

int nParam1 = 0;

int nParam2 = 0;

for (int nIndex = 0; nIndex < 1000; nIndex++)

{

// Do other things

// …

nParam1++;

nParam2++;

PostMessage(WM_MY_TEST, (WPARAM)&nParam1, (LPARAM)&nParam2);

}

//OnOK();

}

LRESULT CMyDlg::OnMyTest(WPARAM wParam, LPARAM lParam)

{

static int nTimes = 0;

CString strOutPut;

int* pParam1 = (int*)wParam;

int* pParam2 = (int*)lParam;

nTimes++;

strOutPut.Format(_T("%s%d   %s%d %s%d"),

_T("Param1 = "), *pParam1,

_T("Param2 = "), *pParam2,

_T("RealTimes = "), nTimes);

OutputDebugString(strOutPut);

return 0;

}

code_1将运行的结果:

Param1 = 0 Param2 = 0 RealTimes = 1

Param1 = 0 Param2 = 0 RealTimes = 2

Param1 = 0 Param2 = 0 RealTimes = 3

Param1 = 0 Param2 = 0 RealTimes = 1000

结果远不如我们所料,表现为PostMessage多次发送时,2~1000的消息参数全被冲掉了。用pParam1、pParam2来处理进度条的话,后果可想而知(进度条根本没动)。如果将上面的PostMessage改为SendMessage,结果如下:

Param1 = 1 Param2 = 1 RealTimes = 1

Param1 = 2 Param2 = 2 RealTimes = 2

Param1 = 3 Param2 = 3 RealTimes = 3

Param1 = 1000 Param2 = 1000 RealTimes = 1000

可见,稳定的输出了需要的内容,可以很好的控制。

在此情况下(主线程中Post消息时),不仅没有改善用户体验,反而更差了。

不可以频繁使用PostMessage发送同一个消息,除非保证上一次发送的消息被处理完成(这如何保证???),这还不如直接用SendMessage。

当然OnMyTest函数可能是这样的:

LRESULT CMyDlg::OnMyTest(WPARAM wParam, LPARAM lParam)

{

static int nTimes = 0;

CString strOutPut;

int* pParam1 = (int*)wParam;

int* pParam2 = (int*)lParam;

nTimes++;

strOutPut.Format(_T("%s%d   %s%d %s%d"),

_T("Param1 = "), *pParam1,

_T("Param2 = "), *pParam2,

_T("RealTimes = "), nTimes);

OutputDebugString(strOutPut);

// 大量访问网络,磁盘等低速操作

return 0;

}

在这种情况下,如果用SendMessage的话,用户体验将会大大下降,甚至导致程序无法响应。于是有人提出了使用PostMessage,这样程序不会无法响应,最多显示不正确罢了。乍一看,提议似乎还不错,至少程序正常运行了。但是,这些网络访问、磁盘读写等操作为什么要放到界面的代码中呢?界面、代码分离才是合理的,因此可以认定,访问网络、磁盘读写等操作不应该放到这里来处理。

2)  在非主线程中Post消息,以处理进度条显示(用WM_MY_TEST的参数WPARAM、LPARAM来处理进度条的显示)

代码:code_2

DWORD WINAPI ThreadProc( LPVOID lpParam )

{

CMyDlg *pThis = (CMyDlg *)lpParam;

int nParam1 = 0;

int nParam2 = 0;

for (int nIndex = 0; nIndex < 1000; nIndex++)

{

nParam1++;

nParam2++;

pThis->PostMessage(WM_MY_TEST, (WPARAM)&(nParam1), (LPARAM)&(nParam2));

}

return 0;

}

void CMyDlg::OnBnClickedOk()

{

HANDLE hThread = CreateThread(NULL,

0,

ThreadProc,

(void*)this,

0,

NULL);

//OnOK();

}

LRESULT CMyDlg::OnMyTest(WPARAM wParam, LPARAM lParam)

{

static int nTimes = 0;

CString strOutPut;

int* pParam1 = (int*)wParam;

int* pParam2 = (int*)lParam;

nTimes++;

strOutPut.Format(_T("%s%d   %s%d %s%d"),

_T("Param1 = "), *pParam1,

_T("Param2 = "), *pParam2,

_T("RealTimes = "), nTimes);

OutputDebugString(strOutPut);

return 0;

}

code_2的运行结果:

(程序直接崩溃了)

线程函数不等待WM_MY_TEST的返回,循环1000次之后直接退出了,这导致栈上的变量nParam1、nParam2被释放,然后OnMyTest处理的时候,nParam1、nParam2的地址已经无效了,导致崩溃。SendMessage则不会出现此类情况。

修改程序

代码:code_2(2)

DWORD WINAPI ThreadProc( LPVOID lpParam )

{

CqwerDlg *pThis = (CqwerDlg *)lpParam;

int *nParam1 = NULL;

int *nParam2 = NULL;

nParam1 = new int;

nParam2 = new int;

for (int nIndex = 0; nIndex < 1000; nIndex++)

{

*nParam1 = nIndex;

*nParam2 = nIndex;

pThis->PostMessage(WM_MY_TEST, (WPARAM)nParam1, (LPARAM)nParam2);

}

return 0;

}

由于堆内存没有被释放,所以程序没有崩溃,在我的机器上运行结果为:

Param1 = 27 Param2 = 27 RealTimes = 1

Param1 = 117 Param2 = 117 RealTimes = 2

Param1 = 162 Param2 = 162 RealTimes = 3

Param1 = 218 Param2 = 218 RealTimes = 4

Param1 = 272 Param2 = 272 RealTimes = 5

Param1 = 312 Param2 = 312 RealTimes = 6

Param1 = 353 Param2 = 353 RealTimes = 7

Param1 = 391 Param2 = 391 RealTimes = 8

Param1 = 431 Param2 = 431 RealTimes = 9

程序执行非常不稳定,每次结构都不同,当然也不能用这些数据了。当把两个new int放到for循环中,执行结果是稳定的,但这样的代码晦涩难懂。在这里用PostMessage没有任何好处,所以建议使用SendMessage。

分析想法2:

1)  已知一个线程处理了A,由于其他需要,此线程还需要处理B(必须在A完成之后)。需要新加入代码来实现,以前的代码为:

代码:code_3

DWORD WINAPI ThreadProc(LPVOID lpParam)

{

HWND hWnd = (HWND)lpParam;

// Do some things

::PostMessage(hWnd, WM_MUST_DO_THING_A, 0, 0);

return 0;

}

LRESULT CMyDlg::OnMustDoThingA(WPARAM wParam, LPARAM lParam)

{

Do some things for A

Do some things for B   // 费解,这是A的处理函数!!!

}

我们可以多加个消息,WM_MUST_DO_THING_B,然后用PostMessage发送,哦,不能这样,B一定要在A完成之后,现在唯一的处理方式只有对B的处理加入到A的消息处理函数中,这将导致费解的代码。如果在原来的线程函数中PostMessage为SendMessage,则不会如此。

如果A、B是不相关联的两个操作,为了以后扩展,也不该用PostMessage,这种情况下应该多创建一个线程进行处理。

2)  对于需要处理的比较重要的操作(这些可能导致卡死):

LRESULT CMyDlg::OnDoThing(WPARAM wParam, LPARAM lParam)

{

Things To do. // 这里可能会卡死,但又必须处理

}

在这种情况下,建议使用SendMessageTimeout,当等待一段时间后,消息仍然没有处理完成,则程序放弃操作继续运行。

3)对于所有无关紧要的操作:

这些操作包括:清理磁盘临时文件等等,这些操作有没正常处理,程序并不关心,在这种情况下,则可使用PostMessage、

终上所述,我们得到如下结论:

1、  PostMessage不能频繁的发送同一个消息,除非保证上次Post过的消息处理完成。

2、  如果用SendMessage导致应用程序用户体验下降,应该检查消息处理函数,而不仅仅简单改为PostMessage。

3、  如果消息是程序必须处理的,则不能使用PostMessage。

4、  如果消息是程序必须处理,而又有可能导致程序卡死,则使用SendMessageTimeout。

5、  如果消息是无关紧要的,则可以建议使用PostMessage。

6、  对于WM_HOTKEY 等Windows特定的消息,则只能使用PostMessage(未在本文中说明)。

参考:http://blog.csdn.net/xt_xiaotian/article/details/2778689

时间: 2024-10-06 13:49:07

PostMessage与SendMessage各自的问题的相关文章

用PostMessage或SendMessage发送结构体指针

SendMessage可以随意发送结构体指针. PostMessage则必须注意结构体的生命周期. 例如如下发送代码,使用PostMessage: struct _tag_aa { CString s1; char szBuffer[512]; int a; char* szText; }; _tag_aa *a=new _tag_aa; a->s1="ssssss11111111111111"; strcpy(a->szBuffer,"Bufferrrrrrrr

PostMessage和SendMessage的区别

1, PostMessage只把消息放入队列,不管其他程序是否处理都返回,然后继续执行,这是个异步消息投放函数.而SendMessage必须等待其他程序处理消息完了之后才返回,继续执行,这是个同步消息投放函数. 2, 如果在同一个线程内,PostMessage发送消息时,消息要先放入线程的消息队列,然后通过消息循环Dispatch到目标窗口.SendMessage发送消息时,系统直接调用目标窗口的消息处理程序,并将结果返回,SendMessage在同一线程中发送消息并不入线程消息队列. 3,Po

MFC窗口消息PostMessage和SendMessage

以前这些消息用得比较少,但是今天碰到了个事儿,我看非用消息不可. 事情是这样的,我在线程中需要刷新对话框上面的内容,但是每每执行到UpdateData时就出现了断言错误. 查了相关资料,发现这个可能是多个模块调用同一个方法出现的问题.反正照我之前的逻辑走不通. 我现在将更新对话框信息的函数,通过线程中发窗口消息,让对话框的消息处理函数来负责处理我自定义的消息,为我刷新对话框数据. // http://blog.csdn.net/a8082649/article/details/7733527 1

PostMessage和SendMessage有什么区别?(有EnumChildWindowsProc的例子)

PostMessage只是把消息放入队列,不管其他程序是否处理都返回,然后继续执行;而SendMessage必须等待其他程序处理消息后才返回,继续执行.PostMessage的返回值表示PostMessage函数执行是否正确;而SendMessage的返回值表示其他程序处理消息后的返回值. 字号: 大大  中中  小小 教你:Delphi获取句柄sendmessage 查找另外一个窗口的句柄: handle := FindWindow(nil,PChar('窗口的标题'));//查到窗体句柄查找

PostMessage与SendMessage的区别

PostMessage只负责将消息放到消息队列中,不确定何时及是否处理 SendMessage要等到受到消息处理的返回码(DWord类型)后才继续 PostMessage执行后马上返回 SendMessage必须等到消息被处理后才会返回. 下面通过一个小例子来说明下这2个方法进行参数传递的不同点: //Win32 API 类 using System; using System.Runtime.InteropServices; namespace TestHwnd { public class

MFC发送自定义消息-PostMessage和SendMessage

PostMessage:把消息投放到线程的消息队列,不能消息被处理就立即返回SendMessage:消息被处理完后才返回 几种发送消息的写法:   ::PostMessage(GetSafeHwnd(), WM_USER_THREADEND, 0, 0); //GetSafeHwnd()表示得到当前窗口句柄,所以这条语句是发送给当前窗口   GetParent()->PostMessage(WM_USER_THREADEND, 0, 0);//发送给父窗口    this->PostMessa

VC++中PostMessage、SendMessage和PeekMessage之间的区别

1, PostMessage只把消息放入队列,不管其他程序是否处理都返回,然后继续执行,这是个异步消息投放函数.而SendMessage必须等待其他程序处理消息完了之后才返回,继续执行,这是个同步消息投放函数.而且,PostMessage的返回值表示PostMessage函数执行是否正确:而SendMessage的返回值表示其他程序处理消息后的返回值.这点大家应该都明白. 2, 如果在同一个线程内,PostMessage发送消息时,消息要先放入线程的消息队列,然后通过消息循环Dispatch到目

postMessage和sendMessage

参考:http://www.cnblogs.com/giggle/p/5350288.html(浅谈webWorker) http://blog.csdn.net/zha_zi/article/details/41677033 HTML5 中 postMessage sendMessage 用法 Web Workers----工作线程 Html5对多线程的支持.在 HTML5 中提出了工作线程(Web Workers)的概念.用于解决页面之间数据处理的多线程,加快数据处理.如同java中的Thr

SendMessage、PostMessage原理

SendMessage.PostMessage原理 SendMessage.PostMessage原理 本文讲解SendMessage.PostMessage两个函数的实现原理,分为三个步骤进行讲解,分别适合初级.中级.高级程序员进行理解,三个步骤分别为: 1.SendMessage.PostMessage的运行机制. 2.SendMessage.PostMessage的运行内幕. 3.SendMessage.PostMessage的内部实现. 注:理解这篇文章之前,必须先了解Windows的消