因此,我创建了一个带有阻塞消息事件循环的基本程序(等待时使用很少甚至没有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
循环。
回调可以检查最后一个输入的时间戳,并将其与当前时间戳进行比较。如果该时间间隔超过所需的不活动超时,则可以执行必要的步骤。否则,它可以在剩余的超时时间内重新启动计时器。
以下代码对此进行了说明:
#include <Windows.h>
#include <iostream>
static uintptr_t timer_id {};
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 continue to expire, so we need to stop it first.
KillTimer(nullptr, timer_id);
LASTINPUTINFO lii { sizeof(lii) };
GetLastInputInfo(&lii);
auto const time_since_input { current_time - lii.dwTime };
if (time_since_input > timeout_in_ms)
{
TimeoutExpired();
}
else
{
auto const remaining_time { timeout_in_ms - time_since_input };
timer_id = SetTimer(nullptr, 0, remaining_time, &TimerProc);
}
}
void StartInactivityTimer()
{
// Start a timer that expires immediately;
// the TimerProc will do the required adjustments and
// restart the timer if necessary.
timer_id = SetTimer(nullptr, 0, 0, &TimerProc);
}
int wmain()
{
StartInactivityTimer();
MSG msg {};
while (GetMessageW(&msg, nullptr, 0, 0) > 0)
{
DispatchMessageW(&msg);
}
}
整个逻辑包含在TimerProc
中。要触发不活动计时器,StartInactivityTimer
只需启动立即超时的计时器,然后转换为TimerProc
并执行所需的计算,然后重新启动计时器,或调用超时过程TimeoutExpired
。