Figure 2 IdleUI and TestIdleUI
IdleUI.h
////////////////////////////////////////////////////////////////
// February 2000 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// This program compiles with Visual C++ 6.0 on Windows 98
//
// See IdleUI.h
//
#include <afxwin.h> // MFC core and standard components
#define DLLEXPORT __declspec(dllexport)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////
// App object for this DLL
//
class CTestIdleUIDll : public CWinApp {
public:
CTestIdleUIDll() { }
~CTestIdleUIDll() { }
} theDll; // one-and-only, normal (per-process) static global
////////////////
// The following global data is SHARED among all instances of the DLL
// (processes); i.e., these are systemwide globals.
//
#pragma data_seg (".IdleUI") // you must define as SHARED in .def
HHOOK g_hHookKbd = NULL; // one instance for all processes
HHOOK g_hHookMouse = NULL; // one instance for all processes
DWORD g_dwLastInputTick = 0; // tick time of last input event
#pragma data_seg ()
/////////////////
// Get tick count of last keyboard or mouse event
//
DLLEXPORT DWORD IdleUIGetLastInputTime()
{
return g_dwLastInputTick;
}
/////////////////
// Keyboard hook: record tick count
//
LRESULT CALLBACK MyKbdHook(int code, WPARAM wParam, LPARAM lParam)
{
if (code==HC_ACTION) {
g_dwLastInputTick = GetTickCount();
}
return ::CallNextHookEx(g_hHookKbd, code, wParam, lParam);
}
/////////////////
// Mouse hook: record tick count
//
LRESULT CALLBACK MyMouseHook(int code, WPARAM wParam, LPARAM lParam)
{
if (code==HC_ACTION) {
g_dwLastInputTick = GetTickCount();
}
return ::CallNextHookEx(g_hHookMouse, code, wParam, lParam);
}
//////////////////
// Initialize DLL: install kbd/mouse hooks.
//
DLLEXPORT BOOL IdleUIInit()
{
if (g_hHookKbd == NULL) {
HINSTANCE hInst = theDll.m_hInstance;
g_hHookKbd = SetWindowsHookEx(WH_KEYBOARD, MyKbdHook, hInst, 0);
g_hHookMouse = SetWindowsHookEx(WH_MOUSE, MyMouseHook, hInst, 0);
g_dwLastInputTick = GetTickCount(); // init count
}
ASSERT(g_hHookKbd);
ASSERT(g_hHookMouse);
return TRUE;
}
//////////////////
// Terminate DLL: remove hooks.
//
DLLEXPORT void IdleUITerm()
{
BOOL bRet1 = UnhookWindowsHookEx(g_hHookKbd);
BOOL bRet2 = UnhookWindowsHookEx(g_hHookMouse);
ASSERT(bRet1 && bRet2);
}
IdleUI.cpp
////////////////////////////////////////////////////////////////
// February 2000 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// This program compiles with Visual C++ 6.0 on Windows 98
//
// See IdleUI.h
//
#include <afxwin.h> // MFC core and standard components
#define DLLEXPORT __declspec(dllexport)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////
// App object for this DLL
//
class CTestIdleUIDll : public CWinApp {
public:
CTestIdleUIDll() { }
~CTestIdleUIDll() { }
} theDll; // one-and-only, normal (per-process) static global
////////////////
// The following global data is SHARED among all instances of the DLL
// (processes); i.e., these are systemwide globals.
//
#pragma data_seg (".IdleUI") // you must define as SHARED in .def
HHOOK g_hHookKbd = NULL; // one instance for all processes
HHOOK g_hHookMouse = NULL; // one instance for all processes
DWORD g_dwLastInputTick = 0; // tick time of last input event
#pragma data_seg ()
/////////////////
// Get tick count of last keyboard or mouse event
//
DLLEXPORT DWORD IdleUIGetLastInputTime()
{
return g_dwLastInputTick;
}
/////////////////
// Keyboard hook: record tick count
//
LRESULT CALLBACK MyKbdHook(int code, WPARAM wParam, LPARAM lParam)
{
if (code==HC_ACTION) {
g_dwLastInputTick = GetTickCount();
}
return ::CallNextHookEx(g_hHookKbd, code, wParam, lParam);
}
/////////////////
// Mouse hook: record tick count
//
LRESULT CALLBACK MyMouseHook(int code, WPARAM wParam, LPARAM lParam)
{
if (code==HC_ACTION) {
g_dwLastInputTick = GetTickCount();
}
return ::CallNextHookEx(g_hHookMouse, code, wParam, lParam);
}
//////////////////
// Initialize DLL: install kbd/mouse hooks.
//
DLLEXPORT BOOL IdleUIInit()
{
if (g_hHookKbd == NULL) {
HINSTANCE hInst = theDll.m_hInstance;
g_hHookKbd = SetWindowsHookEx(WH_KEYBOARD, MyKbdHook, hInst, 0);
g_hHookMouse = SetWindowsHookEx(WH_MOUSE, MyMouseHook, hInst, 0);
g_dwLastInputTick = GetTickCount(); // init count
}
ASSERT(g_hHookKbd);
ASSERT(g_hHookMouse);
return TRUE;
}
//////////////////
// Terminate DLL: remove hooks.
//
DLLEXPORT void IdleUITerm()
{
BOOL bRet1 = UnhookWindowsHookEx(g_hHookKbd);
BOOL bRet2 = UnhookWindowsHookEx(g_hHookMouse);
ASSERT(bRet1 && bRet2);
}
TestIdleUI.h
////////////////////////////////////////////////////////////////
// February 2000 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// This program compiles with Visual C++ 6.0 on Windows 98
//
#include "resource.h" // main symbols
class CMyApp : public CWinApp {
public:
CMyApp() { }
virtual BOOL InitInstance();
virtual int ExitInstance();
protected:
//{{AFX_MSG(CMyApp)
afx_msg void OnAppAbout();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
TestIdleUI.cpp
////////////////////////////////////////////////////////////////
// February 2000 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// This program compiles with Visual C++ 6.0 on Windows 98
//
// TestIdleUI illustrates how to use the IdleUI DLL to keep track of how
// long the user interface has been idle.
#include <afxwin.h> // MFC core and standard components
#include "resource.h" // UI ID's
#include "IdleUI.h" // Idle UI DLL
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
////////////////
// CMyApp
//
class CMyApp : public CWinApp {
public:
CMyApp() { }
virtual BOOL InitInstance();
virtual int ExitInstance();
protected:
//{{AFX_MSG(CMyApp)
afx_msg void OnAppAbout();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//////////////////
// Main frame window sets timer and paints message
// of how long UI has been idle.
//
class CMainFrame : public CFrameWnd {
public:
CMainFrame() { }
virtual ~CMainFrame() { }
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
DECLARE_DYNCREATE(CMainFrame)
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
////////////////////////////////////////////////////////////////
// CMyApp
//
CMyApp theApp;
BEGIN_MESSAGE_MAP(CMyApp, CWinApp)
//{{AFX_MSG_MAP(CMyApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
//}}AFX_MSG_MAP
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()
//////////////////
// App is starting up: initialize IdleUI DLL and load main window.
//
BOOL CMyApp::InitInstance()
{
// Initialize DLL
if (!IdleUIInit()) {
AfxMessageBox(_T("Error initializing DLL -- Aborting"));
return FALSE;
}
// Create main frame and show it
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
m_pMainWnd = pMainFrame;
return TRUE;
}
////////////////
// Application shutting down: terminate idle time DLL
//
int CMyApp::ExitInstance()
{
IdleUITerm(); // terminate DLL (unhook)
return CWinApp::ExitInstance();
}
//////////////////
// Run About dialog
//
void CMyApp::OnAppAbout()
{
CDialog(IDD_ABOUTBOX).DoModal();
}
////////////////////////////////////////////////////////////////
// CMainFrame
//
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_PAINT()
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//////////////////
// Before creating window, set size to something reasonable.
//
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
BOOL bRet = CFrameWnd::PreCreateWindow(cs);
cs.cx = 400; // seems good..
cs.cy = 75; // ..ditto
return bRet;
}
/////////////////
// Create main window: set a timer
//
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
SetTimer(1, 1000, NULL); // 1000 msec = 1 second
return 0;
}
//////////////////
// Paint: paint message showing number of seconds idle.
//
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);
}
//////////////////
// Got timer click: refresh.
//
void CMainFrame::OnTimer(UINT nIDEvent)
{
Invalidate(); // invalidate and..
UpdateWindow(); // force repaint
}