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
 }