[MFC] Cum facem AfxMessageBox cu auto-close?

Despre MFC, ATL si alte biblioteci C++ de la Microsoft (forum moderat)
Post Reply
User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3776
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

[MFC] Cum facem AfxMessageBox cu auto-close?

Post by Ovidiu Cucu » 28 May 2012, 21:46

Intrebare
Cum facem un message box cu inchidere automata dupa un interval de timp?
Auto-close message box.jpg
Auto-close message box.jpg (6.82 KiB) Viewed 4447 times
Raspuns
Atat CWnd::MessageBox cat si AfxMessageBox folosesc in cele din urma ::DialogBox. Aceasta creaza un dialog modal asupra caruia nu mai avem control din program.
Solutii ca FindWindow urmata de stiu eu ce comenzi trimise cu SendMessage/PostMessage sunt mai mult niste carpeli (dar daca mai e un dialog cu acelasi caption pe fatzau? dar daca nu contine butonul cutare? dar daca...)

Mai sigur si mai elegant este cu ajutorul unui hook in care prindem momentul cand se creeaza messagebox-ul ca sa-l subclasam (sa-i "furam" procedura fereastra).
Odata ce avem procedura fereasta, putem face harcea-parcea din el, inclusiv sa-i bagam pe gat un timer care sa-l inchida automat.

Pornim prin a suprascrie CWinApp::DoMessageBox (functie care este apelata din AfxMessageBox) si instalam hook-ul inainte de apelul metodei din clasa de baza.
Ca sa nu incarcam prea tare clasa aplicatiei, putem face treaba intr-o clasa derivata din CDialog (sau CWnd), sa-i zicem CAutoMessageBox.

Exemplu

Code: Select all

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

Code: Select all

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

Code: Select all

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

Code: Select all

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

Code: Select all

void CAutoMessageBox::OnTimer(UINT nIDEvent) 
{
   m_nElapsed += m_nTimerStep;
   // ...
   if(m_nElapsed >= m_nTimeOut)
   {
      KillTimer(0);
      EndDialog(IDTIMEOUT);
   }
}
Am pus mai sus doar fragmente. Amanunte de implementare se gasesc in aplicatia demo atasata.
AutoClose_MessageBox.zip
(12.71 KiB) Downloaded 435 times
Vezi si:



Post Reply