我写了一些代码,利用定时器检查最后一个用户输入的时间,并将其打印到Visual Studio的调试控制台。
#include <Windows.h>
#include <stdio.h>
DWORD InactiveThreshold; // The allowable amount of inactive time milliseconds
BOOL Inactive;
SYSTEMTIME CurrentTime(char *ddmmyyyy, char *hhmmss)
{
SYSTEMTIME lt;
GetLocalTime(<);
snprintf(ddmmyyyy, 11, "%d/%d/%d", lt.wDay, lt.wMonth, lt.wYear);
snprintf(hhmmss, 9, "%d:%d:%d", lt.wHour, lt.wMinute, lt.wSecond);
return lt;
}
void DebugOutput(char *ddmmyyyy, char *hhmmss)
{
OutputDebugString(TEXT("\n\n"));
OutputDebugString(ddmmyyyy);
OutputDebugString(TEXT(" -- "));
OutputDebugString(hhmmss);
OutputDebugString(TEXT(" : "));
OutputDebugString(TEXT("Inactive"));
}
void CALLBACK TimerProc(HWND hwnd, UINT unt, UINT_PTR id, DWORD current_time)
{
LASTINPUTINFO li;
SYSTEMTIME lt;
DWORD TimeSinceInput, RemainingTime;
char ddmmyyyy[11], hhmmss[9];
// Timers continue to expire, so we need to stop it first.
KillTimer(NULL, id);
li.cbSize = sizeof(LASTINPUTINFO);
GetLastInputInfo(&li);
TimeSinceInput = current_time - li.dwTime;
if (TimeSinceInput > InactiveThreshold)
{
// [Also find a way to check if audio or video was playing during the user inactive period]
if (!(Inactive))
{
// Set flag so it only outputs that it is inactive once during an inactive period
Inactive = TRUE;
lt = CurrentTime(ddmmyyyy, hhmmss);
// [Find way to print the start of the inactive period as timestamp,
// subtracting InactiveThreshold from current time]
DebugOutput(ddmmyyyy, hhmmss);
}
// [Change below so once Inactive, stop timer and instead set event loop
// to check for user input(keyboard / mouse) to end the inactive period
// and output timestamp of the end of the inactive period]
SetTimer(NULL, 0, InactiveThreshold, &TimerProc);
}
else
{
Inactive = FALSE;
RemainingTime = InactiveThreshold - TimeSinceInput;
SetTimer(NULL, 0, RemainingTime, &TimerProc);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
Inactive = FALSE;
InactiveThreshold = 30000; // 30 seconds
SetTimer(NULL, 0, InactiveThreshold, &TimerProc);
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
DispatchMessage(&msg);
}
return 0;
}
理想情况下,它应该打印非活动期开始的时间戳,然后当用户再次活动时打印非活动期结束的时间戳。然而有三个主要的问题(也在代码中用方括号注释/[]表示)。
GetLastInputInfo()不考虑用户是否在播放媒体(例如:在Google Chrome浏览器中观看视频或收听音频)。
据我所知,系统本身只跟踪活动,因为无论从什么地方都可以获得。GetLastInputInfo()
.
像 "用户正在观看视频 "这样的情况是通过发送 WM_SYSCOMMAND
与 SC_SCREENSAVE
或 SC_MONITORPOWER
所以,如果应用程序吞下了这样的消息,而不是传给了你,屏幕就不会空白。DefWindowProc
. 或者通过 SetThreadExecutionState
与 ES_DISPLAY_REQUIRED
背景音频等情况由 SetThreadExecutionState
与 ES_SYSTEM_REQUIRED
.
这都意味着用户的不活动确实发生了,但一些其他条件阻止了对这种不活动采取行动。
恐怕一个应用程序无法可靠地检测到所有这些条件。