Blog. Just Blog

MessageBox с таймером обратного отсчета

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
MessageBox с таймером обратного отсчета
MessageBox с таймером обратного отсчета

Наверняка вы сталкивались с диалоговыми окнами и окнами сообщений, в которых какое-то действие может быть выполнено только после истечения некоторого времени или которые сами продолжат работу через какой-то интервал. Это, например, могут быть инсталляторы, дающие пользователю возможность передумать, но начинающие установку обновлений через несколько секунд, если подтверждения от пользователя не было получено. Или уведомление о завершении какой-либо операции, которое пользователь прочитает и закроет, если в данный момент он находится у монитора. То есть в таких ситуациях, когда и пользователя надо о чем-то поставить в известность, но при этом обязательной ответной реакции от него ждать не требуется, так как с большой вероятностью он сделает именно так же или у него вообще не будет других вариантов. Примерно такое же поведение используют триальные окна, когда кнопка "OK" блокируется и вам приходится какое-то время наблюдать предложение метнуться в кассу.

Диалоговые окна мы рассматривать не будем, так как там сразу же настраиваются функции обработки и все делается элементарно. А вот обычный MessageBox в качестве примера будет интересен. Первым делом надо получить хэндл окна MessageBox. Делается это следующим образом:
  1.         ; Получить хэндл текущего потока
  2.         invoke  GetCurrentThreadId
  3.         ; Установить перехватчик событий
  4.         invoke  SetWindowsHookEx,WH_CBT,MBHookProc,NULL,eax
  5.         mov     [hMBHook],eax
  6.  
  7.         invoke  MessageBox,HWND_DESKTOP,szMess,szTitle,MB_OK
Прямо перед открытием окна сообщения надо установить хук с типом WH_CBT на поток, который это окно будет открывать. В момент создания окна сообщения хук сработает, это будет событие HCBT_ACTIVATE. Все остальные события надо перенаправлять дальше следующему обработчику.
  1. ;---------------------------------------------
  2. ; Перехватчик открытия MessageBox
  3. ;---------------------------------------------
  4. proc MBHookProc nCode:DWORD,wParam:DWORD,lParam:DWORD
  5.         ; Сохранить все регистры
  6.         pusha
  7.  
  8.         cmp     [nCode],0
  9.         jb      @f
  10.         cmp     [nCode],HCBT_ACTIVATE
  11.         jne     @f
  12.  
  13.         ; Начальное значение таймера
  14.         mov     [dTimer],10
  15.  
  16.         ; Установить таймер окна MessageBox
  17.         invoke  SetTimer,[wParam],1,1000,TimerProc
  18.  
  19.         ; Сформировать начальную строку
  20.         invoke  wsprintf,buff,mask,[dTimer]
  21.         add     esp,12
  22.         ; Установить текст на кнопку
  23.         invoke  GetDlgItem,[wParam],IDOK
  24.         invoke  SetWindowText,eax,buff
  25.  
  26.         ; Снять перехватчик
  27.         invoke  UnhookWindowsHookEx,[hMBHook]
  28.  
  29.         ; Восстановить регистры
  30.         popa
  31.  
  32.         xor     eax,eax
  33.         ret
  34. @@:
  35.         ; Восстановить регистры
  36.         popa
  37.  
  38.         ; Передать управление следующему обработчику
  39.         invoke  CallNextHookEx,[hMBHook],[nCode],[wParam],[lParam]
  40.         ret
  41. endp
В нашем обработчике хука в параметре wParam система сообщит хэндл окна сообщения, ради чего вся эта движуха и затевалось. Теперь надо будет выполнить следующие действия: инициализировать начальное значение счетчика, при помощи функции SetTimer добавить таймер к процедуре-обработчику окна сообщения с указанием функции его обработки, при необходимости записать на нужную кнопку сформированную строку с начальным значением счетчика, а затем снять хук, так как он нам больше не нужен. Для красоты можно сперва получить текст из нужной кнопки, чтобы использовать его для формирования строки. Это будет особенно хорошо смотреться в случае запуска вашей программы на системах с разными языками интерфейса. В случае с триальным окном в функции-перехватчике дополнительно дизаблится кнопка "OK" и отключается кнопка Close.
  1. ;---------------------------------------------
  2. ; Функция таймера для MessageBox
  3. ;---------------------------------------------
  4. proc TimerProc hwnd:DWORD,uMsg:DWORD,idEvent:DWORD,dwTime:DWORD
  5.         pusha
  6.  
  7.         ; Время вышло?
  8.         cmp     [dTimer],1
  9.         ja      @f
  10.  
  11.         ; Закрыть окно по истечении времени
  12.         invoke  DestroyWindow,[hwnd]
  13.         jmp     .loc_exit
  14. @@:
  15.         ; Уменьшить таймер
  16.         dec     [dTimer]
  17.         invoke  wsprintf,buff,mask,[dTimer]
  18.         add     esp,12
  19.         ; Установить текст на кнопку
  20.         invoke  GetDlgItem,[hwnd],2
  21.         invoke  SetWindowText,eax,buff
  22. .loc_exit:
  23.         popa
  24.         ret
  25. endp
В нашем примере, согласно настроек таймера, функция будет вызываться каждую секунду. При каждом вызове счетчик будет уменьшаться на единицу, по истечении времени окно сообщения будет автоматически закрыто с помощью функции DestroyWindow, а в случае триального окна просто разблокируются все отключенные при открытии элементы. Во время ожидания каждую секунду будет формироваться строка с текущим значением счетчика и выводиться на кнопку. Пользователь также может прервать отсчет в любой момент, нажав кнопку самостоятельно, конечно, если при этом она доступна.

Если же красота с тикающим таймером не требуется, а надо просто вывести сообщение, которое через заданный промежуток времени должно исчезнуть, то можно воспользоваться функцией MessageBoxTimeout. По какой-то причине она относится к недокументированным, хотя совершенно спокойно импортируется из user32.dll. Вызов аналогичен функции MessageBox, только добавляются два дополнительных параметра - идентификатор языка и таймаут в миллисекундах.
  1.         ; MessageBox закроется автоматически через 5 секунд
  2.         invoke  MessageBoxTimeout,HWND_DESKTOP,szMess,szTitle,MB_OK,0,5000
По истечении отведенного времени окно сообщения автоматически закрывается с кодом возврата MB_TIMEDOUT.

В приложении примеры программы с исходными текстами, первая открывает MessageBox, а через 10 секунд автоматически его закрывает, если не было реакции со стороны пользователя, вторая программа имитирует триальное окно, третья использует функцию MessageBoxTimeout для создания и автоматического закрытия окна сообщения.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

Countdown.MessageBox.Demo.zip (5,137 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 2068 | Комментариев: 6

Метки: Assembler, окна
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (01.02.2021 в 15:15):
А еще способ из статьи прекрасно работает с окнами сообщений, которые создает MessageBoxIndirect. MessageBoxTimeout не позволяет создавать сообщения с пользовательскими иконками в окне без дополнительных танцев с бубнами.
user (01.02.2021 в 02:35):
Полезная статья.
Собирался как раз приторочить такую функцию к паре утилит,
но собирался делать это с помошью MessageBoxTimeout.
Теперь подумаю
ManHunter (31.01.2021 в 14:14):
Дополнил статью описанием MessageBoxTimeout, в архив добавлен пример.
ManHunter (31.01.2021 в 08:38):
MessageBoxTimeout решает задачу только с автозакрытием окна сообщения. А тут и красивый счетчик, и включение-отключение кнопок, если это все реализовывать, то получатся те же яйца, только чуть с боку.
Но пример с MessageBoxTimeout, пожалуй, добавлю. Лишним не будет.
brute (31.01.2021 в 06:55):
DRON, пока читал заголовок, сам пытался вспомнить эту функцию, т.к. помню, что была. На счёт недокументированности - не замечал этого, кажется, она среди прочих в mdsn.
DRON (31.01.2021 в 01:17):
Для таких целей существует недокументированный MessageBoxTimeout:
https://www.codeproject.com/Ar...xTimeout-API
Но он без обратного отсчёта, просто возвращает MB_TIMEDOUT через заданное время.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0078 сек.) / Память: 4.5 Mb
Наверх