15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту




Таймер закрывает Message box.

Автор: Markus Loibl

  • Скачать демонстрационный проект - 17 Kb
  • Скачать исходник - 3 Kb

    Цель этого примера

    Иногда возникает необходимость показать пользователю стандартное диалоговое
    окошко типа Yes/No , и, если пользователь не даёт ответа, то по истечении определённого
    интервала времени закрыть этот диалог.

    Основы

    Перед вызовом MessageBox, необходимо проинсталировать таймер. Когда окно закрывается,
    то в окно MessageBox поступает сообщение WM_COMMAND .

    Timer-Callback

    Я решил собрать всю информацию передающуюся в MessageBox (Заголовок, Текст, Флаги ...) в классе.
    Но есть одна проблемма: с функцией  SetTimer()может быть инсталлирована только статическая
    timer-callback-процедура: которая не может получить доступа к членам класса (так как статическая).

    Для решения этой проблеммы я вставил мап(Map) для хранения сообщения класса для timer-id:

    Заголовок:

    class CDlgTimedMessageBox
    {
    public:
        // ...
        UINT         ShowMessageBox(BOOL *pbStoppedByUser=NULL);
        // ...
        static void  GlobalTimerProc(HWND hwnd, UINT uiMsg, UINT idEvent, DWORD dwTime);
        void         LocalTimerProc(void);
        // ...
        static       CMapPtrToPtr        m_mapTimerIdToClassMe;
    
    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 CDlgTimedMessageBox::GlobalTimerProc(HWND hwnd, UINT uiMsg, UINT idEvent, DWORD dwTime)
    {
        CDlgTimedMessageBox    *pMe = NULL;
        
        // Находим timer-id сообщения для класса
        CDlgTimedMessageBox::m_mapTimerIdToClassMe.Lookup((void*)idEvent, (void *&) pMe);
        
        if( pMe!=NULL )
            pMe->LocalTimerProc();
    }
    
    void CDlgTimedMessageBox::LocalTimerProc(void)
    {
        // находим Message-Box-окно
    
        // Calculate time since start
        
        if( too long running )
        {
            // Закрываем MessageBox
        }
        else
        {
            // изменяем текст 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);
    }

     

    Добавляем всё это в одну функцию

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

     

    Потокозащищённость

    Доступ к карте включён через CCriticalSection.

    Пример

    BOOL	stoppedByUser;
    UINT	erg;
    
    erg  = ::TimedMessageBox(MB_YESNO|MB_ICONHAND, 
    		"Please press a button", 
    		"box-title", 
    		5000, IDYES, 
    		"\nin the next %lu sec !", 
    		NULL, &stoppedByUser);