关于在窗口消息处理函数中使用MessageBox造成消息重入的问题的研究及解决
原文地址:http://sns.ruanko.com/space.php?uid=15384&do=blog&id=97415
这两天慢慢把这次实训的成果总结出来,这是第一篇日志。
最早碰到的一个报错问题,在窗口消息处理函数中使用MessageBox(包括A和W版),由于MessageBox特性,程序会被阻塞,此时由于窗口处理函数未返回,消息分发会继续分发这个“未处理”的消息,从而导致消息重入。
下面进行分析:
MessageBox是模态对话框,Msdn中对于Dialog的解释为:
The DialogBox macro uses the CreateWindowEx function to create the dialog box. DialogBox then sends a WM_INITDIALOG message (and a WM_SETFONT message if the template specifies the DS_SETFONT style) to the dialog box procedure. The function displays the dialog box (regardless of whether the template specifies the WS_VISIBLE style), disables the owner window, and starts its own message loop to retrieve and dispatch messages for the dialog box.
原因如下:
所有的输入型消息都被MessageBox先处理,有一些在处理完后直接被丢弃,并不会传递给所有者窗体以及其他兄弟窗体;而非输入型消息在此期间MessageBox并未做处理,而是根据消息信息直接传递给所有者窗体或其他兄弟窗体,此时虽然未点击MessageBox上的任何按钮关闭MessageBox,但是同样的消息处理函数有可能重入。
我自己的解决方法是写一个多线程的对话框函数:
DWORD WINAPI _MTMessageBox(LPVOID argv) { DWORD dwBtn; TCHAR* szText; TCHAR* szCaption; TCHAR* szMsg = (TCHAR*)argv; int iMsgLen = _tcslen(szMsg); TCHAR* szBuff = new TCHAR[iMsgLen + 1]; _tcscpy(szBuff,szMsg); for (int i=0;i<iMsgLen;i++) { if (szBuff[i]==0x02) szBuff[i]=0; } szText = new TCHAR[_tcslen(szBuff) + 1]; _stscanf(szBuff,_T("%s"),szText); szBuff+=(_tcslen(szBuff)+1); szCaption = new TCHAR[_tcslen(szBuff) + 1]; _stscanf(szBuff,_T("%s"),szCaption); szBuff+=(_tcslen(szBuff)+1); _stscanf(szBuff,_T("%d"),&dwBtn); MessageBox(NULL,szText,szCaption,dwBtn); delete szBuff; delete szText; delete szCaption; delete argv; return 0; } DWORD MultiThreadMessageBox(LPCTSTR szText,LPCTSTR szCaption,DWORD btn) { HANDLE hThread; DWORD ThreadID; TCHAR* buff = new TCHAR[MAX_PATH*4]; _stprintf(buff,_T("%s\x02%s\x02%d\x02"),szText,szCaption,btn); hThread = CreateThread(0,0,_MTMessageBox,(PVOID)buff,0,&ThreadID); return 1; }
使用MultiThreadMessageBox就行了,同时支持ANSI和Unicode。
备注:原作者写的MultiThreadMessageBox存在一些问题。比如“delete szBuff;”需要修改为“delete [] szBuff;”,否则会泄露内存。
下面是我重新实现了一下:
typedef struct { TCHAR text[MAX_PATH]; TCHAR caption[MAX_PATH]; UINT type; }MessageBoxData; DWORD WINAPI MessageBoxThread(LPVOID parameter) { MessageBoxData * message_box_data = static_cast<MessageBoxData *>(parameter); MessageBox(NULL, message_box_data->text, message_box_data->caption, message_box_data->type); delete message_box_data; message_box_data = NULL; return 0; } void MultiThreadMessageBox(LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) { MessageBoxData * message_box_data = new MessageBoxData; _tcscpy(message_box_data->text ,lpText); _tcscpy(message_box_data->caption, lpCaption); message_box_data->type = uType; HANDLE handle_thread = CreateThread(0, 0, MessageBoxThread, (LPVOID)message_box_data, 0, NULL); if (NULL != handle_thread) CloseHandle(handle_thread); }