Click here to Skip to main content
Click here to Skip to main content

A thread-safe timed message box

By , 12 Feb 2001
 

What is the code for

This is for using the systems MessageBox (no extra dialog creation needed). After a time with no user-response the dialog is automatically closed. An optional text showing the time left to interact is provided. It's also a replacement for MSDN Q181934 (doesn't work)

Basics

Before calling MessageBox, a timer is installed. In the timer's callback-procedure the message-text from the box is replaced (optional).

When the box should be closed, a WM_COMMAND is posted to the MessageBox Window.

Timer-Callback

I decided to collect all the information corresponding to the MessageBox (title, Message, Flags ...) in a class. Then there is the problem that only a static timer-callback-procedure can be installed with the SetTimer()-function: one can not access the class-members (as it is static).

To solve this problem, I inserted a Map to store the classes corresponding to a timer-id:

Header:

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;
};

Source:

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    
    }    
}

Finding the 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);
}

 

Put it in one function

There is one function you can use to call the messagebox without creating a class-instance: CDlgTimedMessageBox::TimedMessageBox() does the rest for you!

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);
}

You can pass a BOOL * to get the info, if the user has pressed a button.

 

Thread-Safe

The map-access is enclosed by a CCriticalSection.

Sample

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);

History

5.11.2000     posted to Code Project
14.11.2000   bugfixes for NT4
05.02.2001   bug reported when created with MBYES: defbutton then is IDCANCEL
05.02.2001   global function went to class-scope

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Markus Loibl
Germany Germany
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1memberSudhindra Sathya19 Nov '10 - 1:26 
not up to the level that a novice cud also atleast understand after reading the explaination of the code
QuestionHow to show other icons in the messagebox..memberJigar Mehta23 Nov '04 - 20:48 
Hye,
I want to information icon and error icon in the messagebox, so which constants should I use.. Please enumerate the constants in your articles... to help people effectively use it...
 
Thanks for a great class..
 
Rose | [Rose]
 
Jigar Mehta
(jigarmehta@gatescorp.com)
 
Software Developer
Gates Information Systems
India
GeneralCompiling errorsmembershalala7 Oct '03 - 21:11 
I created a MFC-AppWizard exe project(Dialog-based) and added the files "timedmsgbox.cpp" and "timedmsgbox.h" to it,then changed the afximpl.h path as: #include "C:\\Program Files\\Microsoft Visual Studio\\VC98\\MFC\\SRC\\afximpl.h"
My computer's OS is WindowsXP sp2
Compiling tool is VC6
The compiling result is the following
How can I solve this problem?
 

 

Compiling...
timedmsgbox.cpp
d:\projects\timedmsgboxtest\timedmsgbox.h(44) : error C2146: syntax error : missing ';' before identifier 'm_sectMap'
d:\projects\timedmsgboxtest\timedmsgbox.h(44) : error C2501: 'm_sectMap' : missing storage-class or type specifiers
D:\projects\TimedMsgBoxTest\timedmsgbox.cpp(28) : error C2039: 'm_sectMap' : is not a member of 'CDlgTimedMessageBox'
d:\projects\timedmsgboxtest\timedmsgbox.h(23) : see declaration of 'CDlgTimedMessageBox'
D:\projects\TimedMsgBoxTest\timedmsgbox.cpp(28) : error C2146: syntax error : missing ';' before identifier 'm_sectMap'
D:\projects\TimedMsgBoxTest\timedmsgbox.cpp(28) : error C2501: 'CCriticalSection' : missing storage-class or type specifiers
D:\projects\TimedMsgBoxTest\timedmsgbox.cpp(28) : fatal error C1004: unexpected end of file found
Error executing cl.exe.
 
TimedMsgBoxTest.exe - 6 error(s), 0 warning(s)

GeneralRe: Compiling errorsmembercristitomi21 Mar '07 - 6:35 
Hi!
Just copy / paste the file "afximpl.h" from the SRC folder of MFC folder from Visual Studio directory.
Then you will get a clean compile.
Best regards,
Christian.
Generaltimed out MessageBoxmemberXeena2 Oct '02 - 6:53 
Hello,
 
I am very new to this windows programming. I working as a co-op. I have to implement a project, where the msg box is coming if the user is inputting something it does the work accordingly, which its doing currently. But the window should disappear after 15 seconds, if the user is not inputting anything. Can someone, Please explain this to me in layman's term how to implement this. The given example is good but somewhat confusing. Your help will be highly appreciated.
 
Thanks,
GeneralbottonsmemberAnonymous17 Jun '02 - 5:29 
Can you tell me how to display no buttons in the message box?
GeneralIf you like this then....memberAnonymous12 Mar '02 - 7:23 
You'll love this: http://www.gipsysoft.com/messagebox/
General"Fire and Forget" additionmemberM.Lansdaal12 Feb '02 - 14:53 
I was glad to see this class and glad that someone had done a nice job on it.   However, like everything, I needed something a little bit beyond what was provided - namely modeless operation with a more general interface (than was used in the multi-threaded example of the demo project).  
 
I made the following changes:
 
//timedmsgbox.h
     UINT          ShowMessageBoxThreaded(void);
 
     //Internal function to show the messagebox from a thread.
     static UINT ThreadedMessageBoxShow(LPVOID pParam); //in "Protected"
 
//timedmsgbox.cpp
UINT CDlgTimedMessageBox::ThreadedMessageBoxShow(LPVOID pParam)
{
     CDlgTimedMessageBox* pThis = (CDlgTimedMessageBox*) pParam;
     pThis->ShowMessageBox();
     return 0;
}
 

UINT CDlgTimedMessageBox::ShowMessageBoxThreaded(void)
{
     AfxBeginThread(ThreadedMessageBoxShow, this, THREAD_PRIORITY_NORMAL, 0, 0);
     return 0;
}
 
//timedmsgbox.cpp (Changed implementation on the destructor)
CDlgTimedMessageBox::~CDlgTimedMessageBox()
{
     //Force the timer to stop if using a threaded show and the CDlgTimedMessageBox instance is
     //     being destroyed before the timer has timed out or the user has clicked the button.
     CDlgTimedMessageBox::m_sectMap.Lock();
     {
          ::KillTimer(NULL, m_idTimer);
          m_idTimer = 0;
          CDlgTimedMessageBox::m_mapTimerIdToClassMe.RemoveKey((void*)m_idTimer);
     }
     CDlgTimedMessageBox::m_sectMap.Unlock();
 
     //Kill the window
     ::PostMessage(m_hMsgBox, WM_COMMAND, (WPARAM) m_DefaultReturn, (LPARAM) m_hDefaultButton);
 
}
 

//MsgBoxDemoDlg.cpp (OnButtonThreaded changed to this implementation)
void CMsgBoxDemoDlg::OnButtonThread()
{
     UpdateData(TRUE);
 
     CDlgTimedMessageBox msgBox( MB_OK,
                    "Please press a button",
                    "Multi-Threaded",
                    5000,
                    IDOK,
                    "\nin the next %lu sec !",
                    this->m_hWnd );
 
     msgBox.ShowMessageBoxThreaded();
     ShowAMsgBox("Single-Threaded", 7000, m_hWnd, m_bStatus);
     MessageBox("Continuing on...", "FYI", MB_OK);
}
 

GeneralTimermemberJoachim12 Nov '01 - 2:59 
Hi
 
I converted the source so it runs under Windows CE 3.0 Smile | :)
But one problem. The messagebox timer doesn't count down. Frown | :(
It always says 5 seconds.. and when never closes the box before I tap close with the pen.
 


GeneralRe: Timermembermloibl12 Nov '01 - 21:10 
Currently I don't own Win CE, so debugging and testing is imposible for me.
Maybe the MsgBox-handle coan't be found!
 
Is there a CE-simulator or so in order to test and develop for CE within a normal Windows NT or XP ??

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 13 Feb 2001
Article Copyright 2000 by Markus Loibl
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid