c
| Code for this article: Feb00CQA.exe (22KB)
Paul DiLascia is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at askpd@pobox.com or http://pobox.com/~askpd. |
| Q Is there a system call that determines how long a machine has been idle? Basically, I'm trying to figure out how an application based on Windows® 9x could determine that there has been no keyboard or mouse input for a given length of time.
Omar Naseef
A There is a system call, but it doesn't exist on Windows 9x. Windows 2000, on the other hand, provides a new function, GetLastInputInfo, that fills a LASTINPUTINFO struct with just the information you need: |
LASTINPUTINFO lpi; lpi.cbSize = sizeof(lpi); GetLastInputInfo(&lpi); |
| After calling GetLastInputInfo, lpi.dwTime contains the number of milliseconds since the last input event. This is just what you need to determine how long the system has been idle. Unfortunately, as I said, it's only available only on Windows 2000—not Windows 9x or Windows NT® 4.0. So what's a programmer to do?
|
BOOL IdleUIInit() void IdleUITerm(); DWORD IdleUIGetLastInputTime(); |
| As you might infer, IdleUIInit is the function you must call to initialize the whole shebang. IdleUITerm is the one to call when you're done. You can call these from your MFC app's InitInstance and ExitInstance functions. Once you have initialized IdleUI, you can call the third function, IdleUIGetLastInputTime, to get the tick count of the last input event—just like GetLastInputInfo.
|
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);
}
|
| Figure 1 shows TestIdleUI running. To provide continuous display, TestIdleUI sets a one-second timer and refreshes the client area every time the timer pops. |
void CMainFrame::OnTimer(UINT)
{
Invalidate();
UpdateWindow();
}
|
| What could be easier? You can run TestIdleUI and watch the seconds tick 0, 1, 2, 3, and so on as your computer does nothing. If you move the mouse or press a key, the count starts all over again from zero. I should have called it Sisyphus.
|
|
HHOOK g_hHookKbd;
HHOOK g_hHookMouse;
g_hHookKbd = SetWindowsHookEx(WH_KEYBOARD,
MyKbdHook,
hInst, 0);
g_hHookMouse = SetWindowsHookEx(WH_MOUSE,
MyMouseHook,
hInst, 0);
|
| Now when the user moves the mouse or presses a key, Windows calls one of these hooks and the hook function records the time: |
LRESULT CALLBACK MyMouseHook(int code,
WPARAM wp,
LPARAM lp)
{
if (code==HC_ACTION) {
// note the tick count
g_dwLastInputTick = GetTickCount();
}
return ::CallNextHookEx(g_hHookMouse,
code, wp, lp);
}
|
| Ditto for MyKbdHook. IdleUIGetLastInputTime returns g_dwLastInputTick, and IdleUITerm uninstalls both hooks. What could be simpler?
|
#pragma data_seg (".IdleUI") // any name you like
HHOOK g_hHookKbd = NULL;
HHOOK g_hHookMouse = NULL;
DWORD g_dwLastInputTick = 0;
#pragma data_seg ()
|
| This tells the linker to put the three variables in a data segment called .IdleUI (you can use any name you like). Then you have to add a line to IdleUI.def to make the segment shared: |
// in IdleUI.def SECTIONS .IdleUI READ WRITE SHARED |
| Figure 2 shows the final source code for IdleUI. Use it in good health.
|
浙公网安备 33010602011771号