Cum facem un message box cu inchidere automata dupa un interval de timp?
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);
}
}