div.main {margin-left: 20pt; margin-right: 20pt} Как долго простаивает операционная система.
Автор: Paul DiLascia
Скачать
исходник к статье - 21 Кб (Компилятор: Visual C++ 6.0)
Вы задавались когда-нибудь вопросом, как можно определить сколько
времени бездействует операционная система ? Или, например, как аська
определяет, что пользователя нет за компьютером ? Проще говоря, как
определить, сколько времени не трогали мышку или не стучали по клавиатуре
?
В Windows 2000 для этих целей предусмотрена системная функция
GetLastInputInfo, которая заполняет структуру LASTINPUTINFO нужной нам
информацией: LASTINPUTINFO lpi;
lpi.cbSize = sizeof(lpi);
GetLastInputInfo(&lpi);
После вызова GetLastInputInfo, lpi.dwTime содержит количество
миллисекунд, прошедших с момента прекращения каких либо действий
пользователя. С одной стороны, это то, что нам нужно, но с другой - эта
функция доступна только в Windows 2000 - но не в Windows 9x и не в Windows
NT 4.0. Надо бы подумать, что в таком случае можно предпринять.
Наверное, проще всего будет написать свою версию GetLastInputInfo, а
именно поставить глобальную ловушку (systemwide hook). Ловушка, это
callback-функция, которую Windows вызывает каждый раз, когда случается
что-нибудь интересное, например, когда пользователь что-нибудь напечатает
на клавиатуре. Универсальной ловушки не существует, однако ловушкой на
клавиатуру и ловушкой на мышь мы спокойно сможем перехватить весь
пользовательский ввод. Единственное, что может раздражать в таком случае,
это то, что ловушки должны жить в DLL.
Итак, для этой цели я написал простенькую DLL, которую назвал IdleUI и
которая имеет всего три функции: BOOL IdleUIInit()
void IdleUITerm();
DWORD IdleUIGetLastInputTime();
Как Вы наверное уже догадались, IdleUIInit служит для инициализации, а
IdleUITerm для завершения. Их можно спокойно вызывать из приложения MFC в
функциях InitInstance и ExitInstance соответственно. Как только
проинициализируется IdleUI, можно вызвать третью функцию
IdleUIGetLastInputTime, чтобы получить количество тиков, прошедших с
момента последнего пользовательского ввода, как это делает
GetLastInputInfo.
Чтобы протестировать свою DLL, я написал программку TestIdleUI. Она
вызывает IdleUIInit и IdleUITerm как описано выше и при этом отображает
количество секунд бездействия клавиатуры и мышки. void CMainFrame::OnPaint()
{
CPaintDC dc(this);
CString s;
DWORD nsec = (GetTickCount()-
IdleUIGetLastInputTime())/1000;
s.Format(
"No mouse or keyboard input for %d seconds.",nsec);
CRect rc;
GetClientRect(&rc);
dc.DrawText(s, &rc,
DT_CENTER|DT_VCENTER|DT_SINGLELINE);
}
Как это выглядит, можно посмотреть на рисунке 1. Для обеспечения
непрерывности процесса отсчёта, в TestIdleUI используется односекундный
таймер, который обновляет клиентскую область каждую секунду. void CMainFrame::OnTimer(UINT)
{
Invalidate();
UpdateWindow();
}
Что может быть проще? Запускаем TestIdleUI и наблюдаем по секундам, как
Ваш компьютер бездействует. Достаточно сдвинуть мышку и щёлкнуть по
клавиатуре, как счётчик сбросится в ноль.
Рисунок 1 TestIdleUI
Как работает IdleUI? Когда Вы вызваете IdleUIInit, то она инсталлирует
две ловушки: одну для мышки, а вторую для клавиатуры. HHOOK g_hHookKbd;
HHOOK g_hHookMouse;
g_hHookKbd = SetWindowsHookEx(WH_KEYBOARD,
MyKbdHook,
hInst, 0);
g_hHookMouse = SetWindowsHookEx(WH_MOUSE,
MyMouseHook,
hInst, 0);
Теперь если пользователь двигает мышку или нажимает клавишу на
клавиатуре, Windows вызывает одну из этих ловушек и соответствующую ей
функцию, которая записывает время: LRESULT CALLBACK MyMouseHook(int code,
WPARAM wp,
LPARAM lp)
{
if (code==HC_ACTION) {
// уведомляем счётчик тиков
g_dwLastInputTick = GetTickCount();
}
return ::CallNextHookEx(g_hHookMouse,
code, wp, lp);
}
Аналогичная операция делается для MyKbdHook. IdleUIGetLastInputTime
возвращает g_dwLastInputTick, а IdleUITerm деинсталлирует обе ловушки.
Есть только одна тонкость. Обычно, когда Вы создаёте DLL, то компилятор
помечает статические данные как нерасшаренные (nonshared), что означает,
что каждый процесс, который вызывает DLL получает свою собственную копию
данных - в данном случае g_hHookKbd, g_hHookMouse, и g_dwLastInputTick. Но
нам-то нужен один и только один экземпляр этих данных. Поэтому необходимо
сделать данные доступными. Для этого необходимо поместить их в специальный
сегмент, который помен как доступный (shared). В коде это выглялит
так: #pragma data_seg (".IdleUI") // или любое другое имя
HHOOK g_hHookKbd = NULL;
HHOOK g_hHookMouse = NULL;
DWORD g_dwLastInputTick = 0;
#pragma data_seg ()
Этот код указывает компилятору, что три переменные необходимо поместить
в сегмент данных с именем .IdleUI. Затем необходимо добавить следующую
строчку в IdleUI.def, сделав тем самым сегмент расшаренным
(доступным): // в IdleUI.def
SECTIONS .IdleUI READ WRITE SHARED
Вот собственно и всё. Удачи!
|