C Windows API确定用户是不是在一段时间内不活动
Posted
技术标签:
【中文标题】C Windows API确定用户是不是在一段时间内不活动【英文标题】:C Windows API determine if user inactive for certain periodC Windows API确定用户是否在一段时间内不活动 【发布时间】:2020-04-24 00:11:37 【问题描述】:所以我创建了一个带有阻塞消息事件循环的基本程序(在等待时几乎不使用 CPU)并等待用户更改前台窗口,然后执行一些代码:
#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;
我还想检查用户是否在一段时间内处于非活动状态(没有鼠标/键盘输入),但要保持较低的资源使用率。有几种方法可以解决这个问题,我能想到:
让阻塞事件循环检查是否有鼠标或键盘输入将某种计时器重置为零,并在同一循环内检查鼠标输入是否导致前台窗口更改(这可能导致如果鼠标单击事件和前台窗口更改之间存在延迟(意味着不会捕获前台窗口更改),则会出现问题。当用户输入计时器完成指定时间时触发事件。
李>在单独的线程上或与前台窗口更改事件异步运行鼠标和键盘活动事件计时器。当计时器完成触发事件时(在单独的线程上运行或异步运行以确保不会错过前台窗口更改事件)。
在单独的线程上或异步地,每隔几秒检查一次 GetLastInputInfo() 函数以查看是否已过不活动阈值时间。
可以这样调用:
LASTINPUTINFO li;
li.cbSize = sizeof(LASTINPUTINFO);
GetLastInputInfo(&li);
牢记最低的资源使用率,在检查前台窗口更改的同时,最好采用什么方法来执行鼠标/键盘不活动检查。
【问题讨论】:
使用 SetTimer() 所以 GetMessage() 不会阻塞。 您可以在发送消息的线程上安装一个计时器。无需为此启动另一个线程。虽然尚不清楚,但您实际上在调用什么GetMessage
。我不知道有 5 个参数的 GetMessage
函数。
WH_KEYBOARD_LL
& WH_MOUSE_LL
如果您想针对非活动案例进行优化。只会在键盘/鼠标事件上消耗资源。
@ale:这不会优化任何东西。它只是添加代码来记录,系统已经为您记录的内容,并在调用GetLastInputInfo
时返回给您。它也无法考虑来自既不是键盘也不是鼠标的设备的用户输入。
@mic:不要设置“每隔几秒检查一次的计时器”。为您想要的不活动超时设置一个计时器。拨打GetLastInputInfo
,查看不活动超时是否超时。如果没有,则设置一个计时器,其剩余的非活动超时时间相对于自上次输入以来经过的时间。重复。
【参考方案1】:
您可以设置一个计时器(请参阅SetTimer)以在任意超时到期时调用用户定义的回调。这可以让您摆脱阻塞的GetMessage
循环。
回调可以检查最后输入的时间戳,并将其与当前时间戳进行比较。如果该时间间隔超过所需的不活动超时,它可以执行必要的步骤。否则,它会在剩余的超时时间内重新启动计时器。
以下代码说明了这一点:
#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);
整个逻辑都包含在TimerProc
中。要触发不活动计时器,StartInactivityTimer
会启动一个立即到期的计时器。当TimerProc
获得控制权时,它会执行所需的计算,并重新启动计时器,或调用超时过程TimeoutExpired
。
这个实现有两个优点:第一,整个定时器重启逻辑在一个地方。更重要的是,在第一次调用时评估不活动条件。如果在不活动时间间隔内调用 StartInactivityTimer
而没有任何用户输入,它会立即执行 TimeoutExpired
。
还要注意,区间计算使用无符号整数运算,特别是减法。由于在 C 和 C++ 中都明确定义了无符号整数“下溢”,因此该解决方案不受 GetTickCount 的返回值在大约 49.7 天后回绕为 0 的影响。
【讨论】:
以上是关于C Windows API确定用户是不是在一段时间内不活动的主要内容,如果未能解决你的问题,请参考以下文章
UILocalNotification 在一段时间后重复自己