Blog. Just Blog

Проверка и обнаружение зависших приложений

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Иногда для работы требуется определение зависших приложений, окна которых не отвечают на сообщения. Для этого есть два способа. Первый - официально документированный, через функцию SendMessageTimeOut. Особенность ее работы заключается в том, что после отправки сообщения окну она ждет ответ заданное время, и, если ответа от приложения не последовало, то возвращает FALSE. Вот пример использования функции. Нужные константы, как обычно, в FASM не определены, пришлось брать их из других источников.
  1.         ...
  2.         ; Определить таймаут 50 миллисекунд
  3.         TIMEOUT = 50
  4.         ; Определить константу SMTO_ABORTIFHUNG
  5.         SMTO_ABORTIFHUNG = 2
  6.         ; hwnd - хэндл проверяемого окна
  7.         invoke  SendMessageTimeout,[hwnd],NULL,0,0,SMTO_ABORTIFHUNG,TIMEOUT,NULL
  8.         ; Если вернулся 0, то приложение "висит"
  9.         or      eax,eax
  10.         jz      app_hung_up
  11.         ...
Минус использования этой функции в том, что при частой проверке большого количества окон зависших приложений, каждый раз будет отрабатываться таймаут для каждого такого окна, что суммарно может дать снижение производительности вашего приложения. Но повторюсь, Microsoft официально рекомендует к использованию именно эту функцию.

Второй способ - недокументированный. В Windows есть особые функции, которые используются, например, во встроенном Диспетчере задач. Для операционных систем Windows 2000 и выше это IsHungAppWindow, а для систем Windows 9x и Windows ME - функция IsHungThread (она вообще никак не документирована на MSDN). Разница между ними в том, что IsHungAppWindow работает с хэндлом окна, а IsHungThread с хэндлом потока окна. Поскольку в системной библиотеке user32.dll одновменно может быть только одна из этих функций, то включать их в секцию импорта нельзя, иначе ваше приложение вылетит с ошибкой. Также есть информация, что они вообще недоступны через экспорт. Стало быть воспользуемся штатными функциями API GetModuleHandle и GetProcAddress, но предварительно в секции данных определим нужные нам переменные:
  1. section '.data' data readable writeable
  2.         ...
  3. hUser   dd ?    ; Хэндл библиотеки user32.dll
  4. hIHW    dd ?    ; Адрес функции IsHungAppWindow (Win2k и выше)
  5. hIHT    dd ?    ; Адрес функции IsHungThread (Win9x)
  6.  
  7. strUser db      'user32.dll',0
  8. strIHW  db      'IsHungAppWindow',0
  9. strIHT  db      'IsHungThread',0
  10.         ...
Перед использованием надо получить все необходимые адреса функций:
  1. section '.code' code readable executable
  2.         ...
  3.         mov     [hIHW],0          ; Инициализировать переменные
  4.         mov     [hIHT],0
  5.  
  6.         ; Получить хэндл библиотеки user32.dll
  7.         invoke  GetModuleHandle,strUser
  8.         mov     [hUser],eax
  9.  
  10. chk_WinNT:
  11.         ; Попытаться получить адрес функции IsHungAppWindow
  12.         invoke  GetProcAddress,[hUser],strIHW
  13.         or      eax,eax
  14.         jz      chk_Win9X
  15.         mov     [hIHW],eax        ; Сохранить адрес функции
  16.         jmp     @f
  17.  
  18. chk_Win9X:
  19.         ; Попытаться получить адрес функции IsHungThread
  20.         invoke  GetProcAddress,[hUser],strIHT
  21.         or      eax,eax
  22.         jz      loc_fatal_error
  23.         mov     [hIHT],eax        ; Сохранить адрес функции
  24.         jmp     @f
  25.  
  26. loc_fatal_error:
  27.         ; По какой-то немыслимой причине не найдено ни одной функции 
  28.         ...
  29. @@:
  30.         ; Продолжить обработку
  31.         ...
И теперь сама функция проверки окна приложения на предмет его зависания. Для большей универсальности я дополнил ее кодом первого документированного способа, он будет вызываться в случае, когда не определена ни одна из функций второго способа. Если время выполнения функции имеет критическое значение, например, при частом обновлении большого списка процессов или окон, то предпочтительнее воспользоваться именно этим ее вариантом.
  1. ;----------------------------------------------------------------
  2. ; Процедура проверки окна на зависание его родительского приложения
  3. ; Предварительно должны быть определены переменные hIHW или hIHT
  4. ; с адресами функций IsHungAppWindow или IsHungThread
  5. ;
  6. ; На входе:
  7. ;   hwnd - хэндл проверяемого окна
  8. ; На выходе:
  9. ;   EAX = 0 - приложение работает или такое окно не найдено
  10. ;   EAX = 1 - приложение зависло и не отвечает
  11. ;----------------------------------------------------------------
  12. proc IsAppHung hwnd:dword
  13.         local   tmp:DWORD     ; Временная переменная
  14.  
  15.         pusha
  16.  
  17.         ; Такое окно есть?
  18.         invoke  IsWindow,[hwnd]
  19.         or      eax,eax
  20.         jz      .loc_ret
  21. .chk_WinNT:
  22.         ; Адрес функции IsHungAppWindow известен?
  23.         cmp     [hIHW],0
  24.         je      .chk_Win9x
  25.         stdcall [hIHW],[hwnd]
  26.         jmp     .loc_ret
  27. .chk_Win9x:
  28.         ; Адрес функции IsHungThread известен?
  29.         cmp     [hIHT],0
  30.         je      .chk_All
  31.         invoke  GetWindowThreadProcessId,[hwnd],NULL
  32.         stdcall [hIHT],eax
  33.         jmp     .loc_ret
  34. .chk_All:
  35.         ; Определить таймаут 50 миллисекунд
  36.         TIMEOUT = 50
  37.         ; Определить константу SMTO_ABORTIFHUNG
  38.         SMTO_ABORTIFHUNG = 2
  39.         invoke  SendMessageTimeout,[hwnd],NULL,0,0,SMTO_ABORTIFHUNG,TIMEOUT,NULL
  40.         sub     eax,1
  41.         neg     eax
  42. .loc_ret:
  43.         ; Сохранить значение
  44.         mov     [tmp],eax
  45.  
  46.         ; Восстановить регистры и сохраненное значение
  47.         popa
  48.         mov     eax,[tmp]
  49.  
  50.         ret
  51. endp
Если очень частый вызов проверки не требуется, то код инициализации можно включить в саму функцию IsAppHung. В результате получится универсальная и самодостаточная функция, не требующая для работы вообще никаких сторонних переменных.
  1. ;----------------------------------------------------------------
  2. ; Процедура проверки окна на зависание его родительского приложения
  3. ;
  4. ; На входе:
  5. ;   hwnd - хэндл проверяемого окна
  6. ; На выходе:
  7. ;   EAX = 0 - приложение работает или такое окно не найдено
  8. ;   EAX = 1 - приложение зависло и не отвечает
  9. ;----------------------------------------------------------------
  10. proc IsAppHung hwnd:dword
  11.         local   tmp:DWORD     ; Временная переменная
  12.  
  13.         pusha
  14.  
  15.         ; Такое окно есть?
  16.         invoke  IsWindow,[hwnd]
  17.         or      eax,eax
  18.         jz      .loc_ret
  19.  
  20.         ; Получить хэндл библиотеки user32.dll
  21.         invoke  GetModuleHandle,strUser
  22.         mov     [tmp],eax
  23.  
  24. .chk_WinNT:
  25.         invoke  GetProcAddress,[tmp],strIHW
  26.         or      eax,eax
  27.         jz      .chk_Win9x
  28.         stdcall eax,[hwnd]
  29.         jmp     .loc_ret
  30. .chk_Win9x:
  31.         invoke  GetProcAddress,[tmp],strIHT
  32.         or      eax,eax
  33.         jz      .chk_All
  34.         mov     [tmp],eax
  35.         invoke  GetWindowThreadProcessId,[hwnd],NULL
  36.         stdcall [tmp],eax
  37.         jmp     .loc_ret
  38. .chk_All:
  39.         ; Определить таймаут 50 миллисекунд
  40.         TIMEOUT = 50
  41.         ; Определить константу SMTO_ABORTIFHUNG
  42.         SMTO_ABORTIFHUNG = 2
  43.         invoke  SendMessageTimeout,[hwnd],NULL,0,0,SMTO_ABORTIFHUNG,TIMEOUT,NULL
  44.         sub     eax,1
  45.         neg     eax
  46. .loc_ret:
  47.         ; Сохранить значение
  48.         mov     [tmp],eax
  49.  
  50.         ; Восстановить регистры и сохраненное значение
  51.         popa
  52.         mov     eax,[tmp]
  53.  
  54.         ret
  55.  
  56. strUser db      'user32.dll',0
  57. strIHW  db      'IsHungAppWindow',0
  58. strIHT  db      'IsHungThread',0
  59.  
  60. endp
По результатам полевых испытаний функции выявлены некоторые интересные факты. В частности выполнялся полный перебор всех top-level окон с проверкой их на предмет зависания. В процессе перебора на рабочей системе Windows XP были пойманы два системных окна с неизменными заголовками "M" и "Default IME", которые определялись как зависшие. Однако это не так, просто эти окна принадлежат к критическим системным процессам. Поэтому если ваша программа также выполняет проверку всех окон, то эти два окна в обработчике зависших процессов надо будет просто пропускать после проверки их заголовка.

Для имитации зависшего приложения можно воспользоваться программой Bad Application или программой hangup.exe из архива. В архиве примеры программ для обнаружения зависших приложений всеми тремя способами и программа-имитатор зависшего приложения. Имитаторы зависших приложений придется снимать только через менеджер процессов.

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

Hangup.Detection.Demo.zip (7,166 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
Eugeny (02.01.2011 в 15:59):
Какая прелесть этот сайт, зачитался. Глубокая благодарность автору за
популяризацию и снятия забрала таинственности с тёмных сил.
Shukuchi (09.11.2009 в 03:36):
Рад что наткнулся на ваш блог - кладезь интересной, ценной и такой редкой!!! в наши дни информации. Fasm лучший! ;) Спасибо за ваш труд!)
P.S. сорри за оффтоп.

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

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

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