AfxMessageBox with Auto-close

Introduction

This article describes how to make the MFC function AfxMessageBox to dispay a message box that is automatically dismissed after a given time.
Auto-close message box

For that purpose we can do the following:

  1. Override CWinApp::DoMessageBox virtual function for customzing the default AfxMessageBox.
  2. Set a CBT hook to catch the message box creation.
  3. In the CBT hook procedure, subclass the message box window procedure.
  4. In the message box procedure, handle WM_INITDIALOG message and set a timer.
  5. Handle WM_TIMER to automatically close the message box.

Next are simplified code snippets that show how to implement the steps described above. CAutoMessageBox is a class designed to implement the message box with auto-close (sets CBT hook, subclasses the message box and so on).
More detailed code can be found in the demo application attached at the end of the article.

1. Override CWinApp::DoMessageBox

int CDemoApp::DoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt) 
{
   CAutoMessageBox msgBox;

   if(MB_TIMED & nType)
   {
      // ...
       VERIFY(msgBox.Hook(nTimeOut, bShowProgress));
      // ...
   }
   // call base class method for doing the default work.
   return CWinApp::DoMessageBox(lpszPrompt, nType, nIDPrompt);
}

2. Set the CBT hook

BOOL CAutoMessageBox::Hook(UINT nTimeOut, BOOL bShowProgress)
{
   // ...
   DWORD dwThreadID = ::GetCurrentThreadId();
   HINSTANCE hInstance = AfxGetInstanceHandle();

   m_hHook = ::SetWindowsHookEx(WH_CBT, &CAutoMessageBox::HookProc, hInstance, dwThreadID);

   return m_hHook != NULL;
}

3. Subclass the message box window procedure

LRESULT CALLBACK CAutoMessageBox::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
   switch(nCode)
   {
   case HCBT_CREATEWND: // a window is about to be created
      {
         LPCBT_CREATEWND lpCbtCreate = (LPCBT_CREATEWND)lParam;
         HWND hWnd = (HWND)wParam;

         if(WC_DIALOG == lpCbtCreate->lpcs->lpszClass && m_pWndThis->GetSafeHwnd() == NULL)
         {
            // Keep in mind the message box handle to subclass it later
            m_hWndThis = hWnd;
         }
         else if(NULL != m_hWndThis && NULL == m_pWndThis->GetSafeHwnd())
         {
            m_pWndThis->SubclassWindow(m_hWndThis);
         }
      }
      break;
   case HCBT_DESTROYWND: // a window is about to be destroyed
      {
         if(m_pWndThis->GetSafeHwnd() == (HWND)wParam) // it's our messge box
         {
            // so set back its default procedure
            m_pWndThis->UnsubclassWindow();
         }
      }
      break;
   }
   return ::CallNextHookEx(m_hHook, nCode, wParam, lParam);
}

4. Handle WM_INITDIALOG and set a timer

LRESULT CAutoMessageBox::OnInitDialog(WPARAM wParam, LPARAM lParam)
{
   if(m_nTimeOut > 0)
   {
      SetTimer(0, m_nTimerStep, NULL);
   }
   // ...
   return TRUE;
}

5. Handle WM_TIMER and close the message box

void CAutoMessageBox::OnTimer(UINT nIDEvent) 
{
   m_nElapsed += m_nTimerStep;
   // ...
   if(m_nElapsed >= m_nTimeOut)
   {
      KillTimer(0);
      EndDialog(IDTIMEOUT);
   }
}

Demo application

You can find attached here a simple dialog-based application for demo purpose.

[Demo]AfxMessageBox_with_auto_close

Just choose the options (time out, message box style and so on) then click “Say Hello!” button. If “Auto close” is checked, a message box with time-out will be shown.

Resources

See also

Downloads

7 thoughts on “AfxMessageBox with Auto-close”

  1. Hi Ovidiu
    is there a way by which we could change the time out message,
    i.e. the lapse value of timer..e.g we have set the timer of 10 sec so the text of message would get changed as 10,9,8,7 ,6 and so on.
    thanks

    Reply
  2. Hi Ovidiu,
    Under which free or open source software license can I use the source code from the zip file in the “Downloads” sections of this page?

    Regards,
    Swadhin

    Reply

Leave a Comment