PostMessage与SendMessage各自的问题

  深刻解析SendMessage、PostMessage

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

 

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

 

       正因为这两个函数的区别致使了以下想法:函数

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

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

 

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

 

       分析想法1:线程

       这里可分为两点:code

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

代码:code_1blog

#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的消息参数全被冲掉了。用pParam一、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次以后直接退出了,这致使栈上的变量nParam一、nParam2被释放,而后OnMyTest处理的时候,nParam一、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、

 

终上所述,咱们获得以下结论:

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

二、  若是用SendMessage致使应用程序用户体验降低,应该检查消息处理函数,而不只仅简单改成PostMessage。

三、  若是消息是程序必须处理的,则不能使用PostMessage。

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

五、  若是消息是可有可无的,则能够建议使用PostMessage。

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

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

相关文章
相关标签/搜索