线程安全的定时消息框






3.43/5 (4投票s)
2000 年 11 月 10 日

225994

3455
系统消息框,
代码是关于什么的
这是为了使用系统的 MessageBox(无需创建额外的对话框)。在没有用户响应一段时间后,对话框将自动关闭。提供了一个可选的文本,显示剩余的交互时间。它也是 MSDN Q181934 的替代方案(该方案无法正常工作)。
基础
在调用 MessageBox
之前,会安装一个计时器。在计时器的回调过程中,消息框中的消息文本会被替换(可选)。
当应该关闭对话框时,会向 MessageBox 窗口发布一个 WM_COMMAND
。
计时器回调
我决定将所有与 MessageBox
相关的的信息(标题、消息、标志...)收集到一个类中。然后就出现了问题,只有静态计时器回调过程才能使用 SetTimer()
函数安装:无法访问类成员(因为它静态)。
为了解决这个问题,我插入了一个 Map 来存储与计时器 ID 对应的类。
标题
class CDlgTimedMessageBox { public: // ... UINT ShowMessageBox(BOOL *pbStoppedByUser=NULL); // ... static void CALLBACK GlobalTimerProc(HWND hwnd, UINT uiMsg, UINT idEvent, DWORD dwTime); void LocalTimerProc(void); // ... static CMapPtrToPtr m_mapTimerIdToClassMe; // to call the messagebox within one line ! static UINT TimedMessageBox(...); protected: UINT m_idTimer; };
来源
UINT CDlgTimedMessageBox::ShowMessageBox(BOOL *pbStoppedByUser) { // ... m_idTimer = ::SetTimer(NULL, 0, 1000, (TIMERPROC) CDlgTimedMessageBox::GlobalTimerProc); CDlgTimedMessageBox::m_mapTimerIdToClassMe.SetAt((void*)m_idTimer, this); // ... ::MessageBox(...) // ... } void CALLBACK CDlgTimedMessageBox::GlobalTimerProc(HWND hwnd, UINT uiMsg, UINT idEvent, DWORD dwTime) { CDlgTimedMessageBox *pMe = NULL; // Find the corresponding class by the timer-id CDlgTimedMessageBox::m_mapTimerIdToClassMe.Lookup((void*)idEvent, (void *&) pMe); if( pMe!=NULL ) pMe->LocalTimerProc(); } void CDlgTimedMessageBox::LocalTimerProc(void) { // find the Message-Box-window // Calculate time since start if( too long running ) { // Stop MessageBox } else { // replace text of MessageBox } }
查找 MessageBox
hWnd = ::GetWindow(::GetDesktopWindow(), GW_CHILD); while( (hWnd!=NULL) && (m_hMsgBox==NULL) ) { pWnd = CWnd::FromHandle(hWnd); pWnd->GetWindowText(title); if( AfxIsDescendant(m_hParent, hWnd) && ::IsWindowVisible(hWnd) && (m_Title.CompareNoCase(title)==0) ) { m_hMsgBox = hWnd; break; } hWnd = ::GetWindow(hWnd, GW_HWNDNEXT); }
将其放入一个函数中
有一个函数你可以用来调用消息框,而无需创建类实例:CDlgTimedMessageBox::TimedMessageBox()
会为你完成剩下的事情!
UINT CDlgTimedMessageBox::TimedMessageBox(UINT flags, LPCTSTR ptszMessage,
LPCTSTR ptszTitle,
DWORD dwTimeout, UINT dDefaultReturn,
LPCTSTR ptszMessageTimer, HWND hwndParent,
BOOL *pbStoppedByUser)
{
CDlgTimedMessageBox msgBox(flags, ptszMessage, ptszTitle,
dwTimeout, dDefaultReturn,
ptszMessageTimer, hwndParent);
return msgBox.ShowMessageBox(pbStoppedByUser);
}
你可以传递一个 BOOL *
来获取信息,如果用户按下了按钮。
线程安全
Map 访问被 CCriticalSection
包围。
示例
BOOL stoppedByUser; UINT erg; erg = CDlgTimedMessageBox::TimedMessageBox(MB_YESNO|MB_ICONHAND, "Please press a button", "box-title", 5000, IDYES, "\nin the next %lu sec !", NULL, &stoppedByUser);
历史
5.11.2000 发布到 Code Project
14.11.2000 修复了 NT4 的 bug
05.02.2001 报告了在 MBYES 创建时出现的 bug:默认按钮变成了 IDCANCEL
05.02.2001 全局函数移动到类作用域