C Windows API определяет, неактивен ли пользователь в течение определенного периода - PullRequest
0 голосов
/ 24 апреля 2020

Итак, я создал базовую c программу с блокирующим событием сообщения l oop (чтобы практически не использовать ЦП во время ожидания), и ждет, когда пользователь изменит окно переднего плана, затем выполняет некоторый код:

#include <Windows.h>

VOID ExitFunction()
{
    // Do Something
}

BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
{
    switch (dwCtrlType)
    {
        case CTRL_SHUTDOWN_EVENT:
            ExitFunction();
            return TRUE;
        case CTRL_LOGOFF_EVENT:
            ExitFunction();
            return TRUE;
            //default:
                //We don't care about this event
                //Default handler is used
    }
    return FALSE;
}

VOID CALLBACK WinEventProcCallback(HWINEVENTHOOK hWinEventHook, DWORD dwEvent, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
{
    if (dwEvent == EVENT_SYSTEM_FOREGROUND)
    {
        // Do Stuff
    }
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    HWINEVENTHOOK WindowChangeEvent;

    SetConsoleCtrlHandler(HandlerRoutine, TRUE);
    WindowChangeEvent = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, NULL, WinEventProcCallback, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);

    while (GetMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    ExitFunction();

    return 0;
}

Я также хочу включить проверку того, был ли пользователь неактивным в течение определенного времени (без ввода с клавиатуры или мыши), но при этом использование ресурсов было низким. Есть несколько способов подойти к этому, о которых я могу подумать:

  • Иметь событие блокировки l oop, чтобы проверить, был ли ввод с клавиатуры или мыши, который сбрасывает какой-то таймер обратно в ноль, а также проверяет в том же l oop, что ввод мыши привел к изменению окна переднего плана (что может вызвать проблемы, если существует задержка между событием щелчка мыши и изменением окна переднего плана (то есть изменение окна переднего плана выиграно) не будут захвачены.) Инициируйте событие, когда таймер пользовательского ввода завершит заданное время.

  • Запустите таймер событий активности мыши и клавиатуры в отдельном потоке или асинхронно на переднем плане Событие смены окна. Когда таймер завершил запуск события (запускается в отдельном потоке или асинхронно, чтобы убедиться, что событие изменения окна переднего плана не пропущено).

  • В отдельном потоке или асинхронно, проверяйте каждые несколько секунд функцию GetLastInputInfo (), чтобы увидеть, если Время порога активности истекло.

Это можно назвать так:

LASTINPUTINFO li;
li.cbSize = sizeof(LASTINPUTINFO);
GetLastInputInfo(&li);

Принимая во внимание минимальное использование ресурсов, какой способ лучше всего реализовать для мыши / проверка неактивности клавиатуры, а также проверка изменений окна переднего плана.

1 Ответ

1 голос
/ 24 апреля 2020

Можно установить таймер (см. SetTimer ), чтобы пользовательский обратный вызов вызывался по истечении произвольного тайм-аута. Это позволяет снять блокировку GetMessage l oop.

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

Следующий код иллюстрирует это:

#include <Windows.h>

#include <iostream>


static const DWORD timeout_in_ms { 5 * 1000 };


void TimeoutExpired() { std::wcout << L"Timeout elapsed" << std::endl; }

void CALLBACK TimerProc(HWND, UINT, UINT_PTR id, DWORD current_time)
{
    // Timers are periodic, but we want it to fire only once.
    KillTimer(nullptr, id);

    LASTINPUTINFO lii { sizeof(lii) };
    GetLastInputInfo(&lii);

    auto const time_since_input { current_time - lii.dwTime };
    if (time_since_input < timeout_in_ms)
    {
        // User input was recorded inside the timeout interval -> restart timer.
        auto const remaining_time { timeout_in_ms - time_since_input };
        SetTimer(nullptr, 0, remaining_time, &TimerProc);
    }
    else
    {
        TimeoutExpired();
    }
}

void StartInactivityTimer()
{
    // Start a timer that expires immediately;
    // the TimerProc will do the required adjustments and
    // restart the timer if necessary.
    SetTimer(nullptr, 0, 0, &TimerProc);
}

int wmain()
{
    StartInactivityTimer();

    MSG msg {};
    while (GetMessageW(&msg, nullptr, 0, 0) > 0)
    {
        DispatchMessageW(&msg);
    }
}

Весь лог c содержится в TimerProc. Чтобы запустить таймер неактивности, StartInactivityTimer запускает таймер, который истекает немедленно. Когда TimerProc берет на себя управление, он выполняет необходимые вычисления и либо перезапускает таймер, либо вызывает процедуру тайм-аута, TimeoutExpired.

Эта реализация имеет два преимущества: во-первых, весь таймер перезапускает логи c находится в одном месте. Что еще более важно, состояние неактивности оценивается при первом вызове. Если StartInactivityTimer вызывается без какого-либо пользовательского ввода в интервале неактивности, он мгновенно выполняет TimeoutExpired.

Также обратите внимание, что для вычисления интервала используется целое число без знака c, а именно вычитание. Благодаря тому, что целое число без знака 'underflow' хорошо определено как в C, так и в C ++, это решение неуязвимо для GetTickCount , возвращая значение 0 после приблизительно 49,7 дней.

...