Blog. Just Blog

Определение времени бездействия системы

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Иногда приложениям требуется узнать время бездействия системы, то есть интервал времени, прошедший с момента когда пользователь последний раз пошевелил мышкой или нажал какую-нибудь кнопку на клавиатуре. Для определения время бездействия системы в системах Windows 2000 и старше используется функция API GetLastInputInfo. Она возвращает количество миллисекунд (тиков таймера), прошедшее от старта системы до момента последнего ввода. Время бездействия вычисляется как арифметическая разница между данными, возвращаемыми функцией GetTickCount и данными из GetLastInputInfo. В FASM, как обычно, ничего из нужных структур не определено, лезем в MSDN:
  1. section '.data' data readable writeable
  2.  
  3. struct  LASTINPUTINFO
  4.         cbSize   dd ?   ; Размер структуры
  5.         dwTime   dd ?   ; Время бездействия
  6. ends
  7.  
  8. lii     LASTINPUTINFO  
Получение времени бездействия системы:
  1.         ...
  2.         ; Получить время последнего ввода
  3.         mov     [lii.cbSize],sizeof.LASTINPUTINFO
  4.         invoke  GetLastInputInfo,lii
  5.  
  6.         ; Получить текущее время в миллисекундах
  7.         invoke  GetTickCount
  8.  
  9.         ; EAX - время бездействия системы в миллисекундах
  10.         sub     eax,[lii.dwTime]
  11.         ...
Это был самый простой способ, работающий на всех новых системах. В старых операционках типа Windows 9x функция GetLastInputInfo отсутствует, поэтому там придется использовать другой, более громоздкий способ с применением глобальных системных хуков.

Чтобы перехват получился глобальным, придется загружать в память DLL с нужными функциями и устанавливать хуки уже на нее. Хуки будем ставить двух типов: через WH_MOUSE на мышь и через WH_KEYBOARD на клавиатуру. При наступлении любого из этих событий в переменную, находящуюся в shared-памяти DLL, будет записываться значение из функции GetTickCount, а при вызове какой-нибудь заранее определенной функции, это значение будет передаваться нашему приложению. Полный текст DLL я тут приводить не буду, можете посмотреть его, скачав прилагаемый файл с исходниками. В основное приложение DLL подгружается через импорт, при инициализации DLL ставятся глобальные хуки, а при выходе из приложения хуки должны обязательно сниматься.
  1.         ...
  2.         ; Получить время последнего ввода
  3.         invoke  GetLastInputTime
  4.         mov     ebx,eax
  5.  
  6.         ; Получить текущее время в миллисекундах
  7.         invoke  GetTickCount
  8.  
  9.         ; EAX - время бездействия системы в миллисекундах
  10.         sub     eax,ebx
  11.         ...
  12.         ...
  13.         ...
  14. .wmclose:
  15.         ; Снять глобальные хуки
  16.         invoke  UnloadDll
  17.         ...
Этот метод универсальный и работает на всех системах. Но приходится таскать за собой дополнительную DLL, к установке глобальных хуков некоторые "как-бы-антивирусы" и мониторы могут отнестись с подозрением, ну и вдобавок получается много лишнего кода. Выбор оставляю за вами.

Следующий способ считается устаревшим и не рекомендуется к использованию. Но знать о нем, как мне кажется, будет полезным. Он основан на использовании функции BeginIdleDetection. Функция BeginIdleDetection не импортируется по имени, ее адрес надо получать из библиотеки msidle.dll с помощью функции GetProcAddress, причем использовать ее ординал 3.
  1.         ; Загрузить библиотеку msidle.dll
  2.         invoke  LoadLibrary,msidle
  3.         ; Получить адрес функции BeginIdleDetection
  4.         invoke  GetProcAddress,eax,3
  5.         ; Установить обработчик бездействия на 1 минуту
  6.         stdcall eax,IdleCallback,1,0
Для обработки бездействия устанавливается callback-функция, которая сработает через указанное количество минут бездействия. При этом, даже если указать нулевое количество минут, время все равно будет установлено как минимум на 1 минуту. Вот пример такой функции обработчика:
  1. STATE_USER_IDLE_BEGIN = 1
  2. STATE_USER_IDLE_END   = 2
  3.  
  4. ;---------------------------------------------------
  5. ; Обработчик бездействия системы
  6. ;---------------------------------------------------
  7. proc IdleCallback dwState:DWORD
  8.         pusha
  9.         cmp     [dwState],STATE_USER_IDLE_BEGIN
  10.         jne     @f
  11.  
  12.         ; Получить текущее время в миллисекундах
  13.         invoke  GetTickCount
  14.         mov     [old_tick],eax
  15.         ; Активировать счетчик
  16.         mov     [idlestate],1
  17.         jmp     .loc_ret
  18. @@:
  19.         cmp     [dwState],STATE_USER_IDLE_END
  20.         jne     .loc_ret
  21.         ; Деактивировать счетчик
  22.         mov     [idlestate],0
  23.  
  24. .loc_ret:
  25.         popa
  26.         ret
  27. endp
Callback-функция при своем вызове получает в единственном параметре новое состояние бездействия, то есть включение режима бездействия по прошествии заданного времени или его отключение после любой пользовательской активности. Если мониторинг больше не требуется, его можно отключить с помощью функции EndIdleDetection. Способ крайне неудобный и не гибкий, не зря он перенесен в список устаревших.

Еще один интересный способ определения времени бездействия системы основан на ядерной структуре KUSER_SHARED_DATA. Она едина для всех процессов и всегда располагается по фиксированному адресу в памяти. Структура огромная, в ней содержится информация о ресурсах системы и пользователе, который сейчас активен. Но из всего объема данных нас интересует только одно поле TickCountLowDeprecated, в нем записано значение системного таймера. Главная его ценность заключается в том, что значение таймера обновляется только в том случае, когда был выполнен пользовательский ввод, то есть нажата какая-нибудь клавиша на клавиатуре или было выполнено какое-то действие с мышью. Счетчик находится по фиксированному адресу 7FFE02E4h.
  1. .wminitdialog:
  2.         ; Получить начальные значения
  3.         mov     eax,dword [ds:7FFE02E4h]
  4.         mov     [old_timer],eax
  5.         invoke  GetTickCount
  6.         mov     [old_tick],eax
  7.         ...
  8.         ...
  9.  
  10. .wmidle:
  11.         ; Получить время последнего ввода
  12.         mov     eax,dword [ds:7FFE02E4h]
  13.         sub     eax,[old_timer]
  14.         ; Таймеры равны, пользовательского ввода не было
  15.         jz      @f
  16.  
  17.         ; Записать новые значения
  18.         mov     eax,dword [ds:7FFE02E4h]
  19.         mov     [old_timer],eax
  20.         invoke  GetTickCount
  21.         mov     [old_tick],eax
  22. @@:
  23.         ; Получить текущее время в миллисекундах
  24.         invoke  GetTickCount
  25.  
  26.         ; EAX - время бездействия системы в миллисекундах
  27.         sub     eax,[old_tick]
При инициализации окна в переменные записываются текущие значения ядерного таймера и счетчик тиков, затем периодически сравнивается текущее значение ядерного таймера и его сохраненного значения, если они не равны, то был пользовательский ввод, в противном случае система находится в режиме бездействия. Этот способ универсальный, работает на всех системах и для всех процессов, при этом не требует установки глобальных хуков.

В приложении программы с исходными текстами, реализующие оба описанных выше метода. В диалоговом окне они показывают счетчик секунд, прошедших с момента последней активности пользователя. Чтобы сбросить счетчик пошевелите мышкой или нажмите любую клавишу.

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

System.Idle.Time.Demo.zip (11,106 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
Лёха (12.04.2023 в 11:35):
Спасибо! Помогло.
ManHunter (06.04.2021 в 13:24):
Добавлен способ определения бездействия через BeginIdleDetection. Архив обновлен.
ManHunter (06.04.2021 в 11:19):
Добавлен еще один интересный пример с ядерным таймером, архив обновлен.
ManHunter (09.08.2019 в 17:45):
Стандартный виндовый планировщик прекрасно справляется с этой задачей. Триггер задачи на условие "При простое", "Условия" - установить на нужный интервал время бездействия, "Действия" - запуск программы. И не надо ничего городить.
Kylo (09.08.2019 в 17:34):
Уважаемый ManHunter, ознакомился со списком Ваших программ, нашел для себя много интересного. Спасибо за Ваш труд! И возникло у меня к Вам предложение по использованию определения времени бездействия системы в Вашей же программе Delayed Start. Дополнительный параметр командной строки, например -hNNN, который запускал бы приложение на определенную секунду времени бездействия. Сразу расширяются возможности применения Delayed Start. Ну а дальше можно фантазировать еще больше - "ds -h600 -c winamp.exe" - запуск музыки после 10мин бездействия пользователя и закрытие winamp.exe (параметр -c) при активности пользователя. Минус примера, нельзя оставлять громкость наполную уходя далеко от компа))
ManHunter (08.08.2019 в 16:53):
За доработками лучше обратиться куда-нибудь на фриланс. Задача не космическая, за мелкий прайс студенты с радостью нарисуют что угодно, тем более вся теория расписана.
Kylo (08.08.2019 в 16:35):
Мне бы было полезно использовать эту наработку как таймер для запуска каких либо задач. Полдня сегодня просидел в поисках реализации такого "таймера", ничего толкового не нашел. Исходники C++, VB, PS, AutoIt, у уважаемого ManHunter здесь на ASM, но уже готового решения нет. Если бы я в этом понимал, конечно использовал бы эти наработки для создания того, что мне нужно, но увы, мозгами не вышел. А так бы можно было сделать с GUI и без, оснастить дополнительными параметрами, взятием настроек из файлов и реестра. Если у Вас, уважаемый ManHunter, есть возможность, желание и время расширить функционал Вашей наработки, буду очень благодарен!
Tekton (15.10.2015 в 10:13):
Я например частенько вечером "рублюсь" при просмотре какого нибудь фильма.
Просыпаюсь под утро, а комп во всю пашет.
Вот можно применить эту наработку.
Сделать скажем таймер часа на 2 если юзер не пошевелил мыш, или клаву не потискал, чтоб комп отрубался сам.
Vovka (09.08.2010 в 22:15):
Вызов скорой помощи для онлайновых параноиков...
ManHunter (06.08.2010 в 08:01):
Дефрагментация диска при простое компа, автоблокировка оставленного без присмотра компа, мониторинг работы сотрудников. Это так, навскидку.
Isaev (06.08.2010 в 01:55):
"Определение времени бездействия системы"
А в каких случаях эта информация используется?
Что-то так и не придумал ничего полезного (

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

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

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