Figure 1   LISWatch

resource.h


 //{{NO_DEPENDENCIES}}
 // Microsoft Developer Studio-generated include file.
 // Used by LISWatch.rc
 //
 #define IDD_LISWATCH                    101
 #define IDC_EYES                        101
 #define IDI_LISWATCH                    102
 #define IDC_WNDFOCUS                    1000
 #define IDC_WNDACTIVE                   1001
 #define IDC_WNDFOREGRND                 1002
 #define IDC_THREADID                    1003
 #define IDC_WNDCAPTURE                  1004
 
 // Next default values for new objects
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        105
 #define _APS_NEXT_COMMAND_VALUE         40001
 #define _APS_NEXT_CONTROL_VALUE         1004
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
LISWatch.rc

 //Microsoft Developer Studio-generated resource script.
 //
 #include "resource.h"
 
 #define APSTUDIO_READONLY_SYMBOLS
 /////////////////////////////////////////////////////////////////////////////
 //
 // Generated from the TEXTINCLUDE 2 resource.
 //
 #include "afxres.h"
 
 /////////////////////////////////////////////////////////////////////////////
 #undef APSTUDIO_READONLY_SYMBOLS
 
 /////////////////////////////////////////////////////////////////////////////
 // English (U.S.) resources
 
 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
 #ifdef _WIN32
 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 #pragma code_page(1252)
 #endif //_WIN32
 
 #ifdef APSTUDIO_INVOKED
 /////////////////////////////////////////////////////////////////////////////
 // TEXTINCLUDE
 
 1 TEXTINCLUDE DISCARDABLE 
 BEGIN
     "resource.h\0"
 END
 
 2 TEXTINCLUDE DISCARDABLE 
 BEGIN
     "#include ""afxres.h""\r\n"
     "\0"
 END
 
 3 TEXTINCLUDE DISCARDABLE 
 BEGIN
     "\r\n"
     "\0"
 END
 
 #endif    // APSTUDIO_INVOKED
 
 /////////////////////////////////////////////////////////////////////////////
 // Dialog
 
 IDD_LISWATCH DIALOG DISCARDABLE  32768, 5, 240, 42
 STYLE WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 CAPTION "LISWatch"
 FONT 8, "MS Sans Serif"
 BEGIN
     LTEXT           "Thread:",IDC_STATIC,4,0,31,8
     LTEXT           "ThreadId",IDC_THREADID,36,0,200,8
     LTEXT           "Focus:",IDC_STATIC,4,8,31,8
     LTEXT           "Focus window",IDC_WNDFOCUS,36,8,200,8
     LTEXT           "Active:",IDC_STATIC,4,16,31,8
     LTEXT           "Active window",IDC_WNDACTIVE,36,16,200,8
     LTEXT           "Capture:",IDC_STATIC,4,24,31,8
     LTEXT           "Capture window",IDC_WNDCAPTURE,36,24,200,8
     LTEXT           "Foregrnd:",IDC_STATIC,4,32,31,8
     LTEXT           "Foreground window",IDC_WNDFOREGRND,36,32,200,8
 END
 
 /////////////////////////////////////////////////////////////////////////////
 // Icon
 
 // Icon with lowest ID value placed first to ensure application icon
 // remains consistent on all systems.
 IDI_LISWATCH            ICON    DISCARDABLE     "LISWatch.ico"
 
 /////////////////////////////////////////////////////////////////////////////
 // Cursor
 
 IDC_EYES                CURSOR  DISCARDABLE     "Eyes.cur"
 
 /////////////////////////////////////////////////////////////////////////////
 // DESIGNINFO
 
 #ifdef APSTUDIO_INVOKED
 GUIDELINES DESIGNINFO DISCARDABLE 
 BEGIN
     IDD_LISWATCH, DIALOG
     BEGIN
         RIGHTMARGIN, 190
     END
 END
 #endif    // APSTUDIO_INVOKED
 
 #endif    // English (U.S.) resources
 /////////////////////////////////////////////////////////////////////////////
 
 #ifndef APSTUDIO_INVOKED
 /////////////////////////////////////////////////////////////////////////////
 // Generated from the TEXTINCLUDE 3 resource.
 
 /////////////////////////////////////////////////////////////////////////////
 #endif    // not APSTUDIO_INVOKED
LISWatch.cpp

 /******************************************************************************
 Module name: LISWatch.cpp
 Notices: Written 1997 Jeffrey Richter
 Purpose: Local-Input State (LIS) Watcher utility.
 ******************************************************************************/
 
 
 #define STRICT
 #include <windows.h>
 #include <windowsx.h>
 #pragma warning(disable: 4001)    /* Single line comment */
 #include "resource.h"
 #include <tchar.h>
 #include <stdio.h>
 
 /////////////////////////// chHANDLE_DLGMSG Macro /////////////////////////////
 
 // The normal HANDLE_MSG macro in WINDOWSX.H does not work properly for dialog
 // boxes because DlgProc's return a BOOL instead of an LRESULT (like
 // WndProcs). This chHANDLE_DLGMSG macro corrects the problem:
 #define chHANDLE_DLGMSG(hwnd, message, fn)                           \
    case (message): return (SetDlgMsgResult(hwnd, uMsg,               \
                                            HANDLE_##message((hwnd), (wParam),    
                                           (lParam), (fn))))
 
 //////////////////////////////// chDIMOF Macro ////////////////////////////////
 
 // This macro evaluates to the number of elements in an array. 
 #define chDIMOF(Array) (sizeof(Array) / sizeof(Array[0]))
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #define TIMER_DELAY (1 * 1000) // 1 second * 1000 milliseconds
 
 UINT  g_uTimerId = 1;
 DWORD g_dwThreadIdAttachTo = 0;  // 0=System-wide; Non-zero=specifc thread
 
 ///////////////////////////////////////////////////////////////////////////////
 
 BOOL LISWatch_OnInitDialog (HWND hwnd, HWND hwndFocus, LPARAM lParam) {
 
    // Update our contents periodically
    g_uTimerId = SetTimer(hwnd, g_uTimerId, TIMER_DELAY, NULL);
 
    // Make our window on top of all others
    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
 
    // Give our window an icon 
    SendMessage(hwnd, WM_SETICON, TRUE, 
                (LPARAM) LoadIcon(GetWindowInstance(hwnd), 
                MAKEINTRESOURCE(IDI_LISWATCH)));
    return(TRUE);                  // Accept default focus window.
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 void LISWatch_OnLButtonDown (HWND hwnd, BOOL fDoubleClick, 
                              int x, int y, UINT keyFlags) {
 
    // If we're attached to a thread, detach from it
    if (g_dwThreadIdAttachTo != 0)
       AttachThreadInput(GetCurrentThreadId(), g_dwThreadIdAttachTo, FALSE);
 
    // Set capture to ourself and change the mouse cursor
    SetCapture(hwnd);
    SetCursor(LoadCursor(GetModuleHandle(NULL), MAKEINTRESOURCE(IDC_EYES)));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 void LISWatch_OnLButtonUp (HWND hwnd, int x, int y, UINT keyFlags) {
 
    if (GetCapture() == hwnd) {
 
       // If we had mouse capture set, get the ID of the thread that
       // created the window that is under the mouse cursor.
       POINT pt;
       pt.x = LOWORD(GetMessagePos());
       pt.y = HIWORD(GetMessagePos());
       ReleaseCapture();
       g_dwThreadIdAttachTo = GetWindowThreadProcessId(
          ChildWindowFromPointEx(GetDesktopWindow(), pt, CWP_SKIPINVISIBLE), 
          NULL);
 
       if (g_dwThreadIdAttachTo == GetCurrentThreadId()) {
          // The mouse button is released on one of our windows, 
          // monitor local-input state on a system-wide basis
          g_dwThreadIdAttachTo = 0;
       } else {
          // The mouse button is released on a window that our thread didn't
          // create, monitor local input state for that thread only.
          AttachThreadInput(GetCurrentThreadId(), g_dwThreadIdAttachTo, TRUE);
       }
    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 static void CalcWndText (HWND hwnd, LPTSTR szBuf, int nLen) {
 
    TCHAR szClass[50], szCaption[50], szBufT[150];
 
    if (hwnd == (HWND) NULL) {
       _tcscpy(szBuf, __TEXT("(no window)")); return;
    }
 
    if (!IsWindow(hwnd)) {
       _tcscpy(szBuf, __TEXT("(invalid window)")); return;
    }
 
    GetClassName(hwnd, szClass, chDIMOF(szClass));
    GetWindowText(hwnd, szCaption, chDIMOF(szCaption));
    _stprintf(szBufT, __TEXT("[%s] %s"), (LPTSTR) szClass,
       (*szCaption == 0) ? (LPTSTR) __TEXT("(no caption)")
       : (LPTSTR) szCaption);
    _tcsncpy(szBuf, szBufT, nLen - 1);
    szBuf[nLen - 1] = 0; // Force zero-terminated string
 }
 
 //////////////////////////////////////////////////////////////
 
 void LISWatch_OnTimer (HWND hwnd, UINT id) {
 
    TCHAR szBuf[100] = __T("System-wide");
    HWND hwndForeground = GetForegroundWindow();
    DWORD dwThreadIdAttachTo = g_dwThreadIdAttachTo;
 
    if (dwThreadIdAttachTo == 0) {
       // If monitoring local input state system-wide, attach our input
       // state to the thread that created the current foreground window.
       dwThreadIdAttachTo = 
          GetWindowThreadProcessId(hwndForeground, NULL);
       AttachThreadInput(GetCurrentThreadId(), dwThreadIdAttachTo, TRUE);
    } else wsprintf(szBuf, __T("0x%08x"), dwThreadIdAttachTo);
    SetWindowText(GetDlgItem(hwnd, IDC_THREADID), szBuf);
 
    CalcWndText(GetFocus(), szBuf, chDIMOF(szBuf));
    SetWindowText(GetDlgItem(hwnd, IDC_WNDFOCUS), szBuf);
 
    CalcWndText(GetActiveWindow(), szBuf, chDIMOF(szBuf));
    SetWindowText(GetDlgItem(hwnd, IDC_WNDACTIVE), szBuf);
 
    CalcWndText(GetCapture(), szBuf, chDIMOF(szBuf));
    SetWindowText(GetDlgItem(hwnd, IDC_WNDCAPTURE), szBuf);
 
    CalcWndText(hwndForeground, szBuf, chDIMOF(szBuf));
    SetWindowText(GetDlgItem(hwnd, IDC_WNDFOREGRND), szBuf);
 
    if (g_dwThreadIdAttachTo == 0) {
       // If monitoring local input state system-wide, detach our input
       // state from the thread that created the current foreground window.
       AttachThreadInput(GetCurrentThreadId(), dwThreadIdAttachTo, FALSE);
    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 void LISWatch_OnCommand (HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) {
 
    switch (id) {
 
       case IDOK:
       case IDCANCEL:              // Allows dialog box to close
          EndDialog(hwnd, id);
          break;
    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
         
 BOOL WINAPI LISWatch_DlgProc (HWND hwnd, UINT uMsg,
    WPARAM wParam, LPARAM lParam) {
 
    switch (uMsg) {
 
       // Standard Window's messages
       chHANDLE_DLGMSG(hwnd, WM_INITDIALOG,  LISWatch_OnInitDialog);
       chHANDLE_DLGMSG(hwnd, WM_COMMAND,     LISWatch_OnCommand);
       chHANDLE_DLGMSG(hwnd, WM_TIMER,       LISWatch_OnTimer);
       chHANDLE_DLGMSG(hwnd, WM_LBUTTONDOWN, LISWatch_OnLButtonDown);
       chHANDLE_DLGMSG(hwnd, WM_LBUTTONUP,   LISWatch_OnLButtonUp);
    }
    return(FALSE);                 // We didn't process the message.
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 int WINAPI WinMain (HINSTANCE hinstExe, HINSTANCE hinstPrev,
    LPSTR lpszCmdLine, int nCmdShow) {
 
    DialogBox(hinstExe, MAKEINTRESOURCE(IDD_LISWATCH),
       NULL, LISWatch_DlgProc);
 
    return(0);
 }
 
 //////////////////////////////// End of File //////////////////////////////////

Figure 3   LISWatch_OnTimer


 void LISWatch_OnTimer (HWND hwnd, UINT id) {
 
    TCHAR szBuf[100] = __T("System-wide");
    HWND hwndForeground = GetForegroundWindow();
    DWORD dwThreadIdAttachTo = g_dwThreadIdAttachTo;
 
    if (dwThreadIdAttachTo == 0) {
       // If monitoring local input state system-wide, attach our input
       // state to the thread that created the current foreground window.
       dwThreadIdAttachTo = 
          GetWindowThreadProcessId(hwndForeground, NULL);
       AttachThreadInput(GetCurrentThreadId(), dwThreadIdAttachTo, TRUE);
    } else wsprintf(szBuf, __T("0x%08x"), dwThreadIdAttachTo);
    SetWindowText(GetDlgItem(hwnd, IDC_THREADID), szBuf);
 
    CalcWndText(GetFocus(), szBuf, chDIMOF(szBuf));
    SetWindowText(GetDlgItem(hwnd, IDC_WNDFOCUS), szBuf);
 
    CalcWndText(GetActiveWindow(), szBuf, chDIMOF(szBuf));
    SetWindowText(GetDlgItem(hwnd, IDC_WNDACTIVE), szBuf);
 
    CalcWndText(GetCapture(), szBuf, chDIMOF(szBuf));
    SetWindowText(GetDlgItem(hwnd, IDC_WNDCAPTURE), szBuf);
 
    CalcWndText(hwndForeground, szBuf, chDIMOF(szBuf));
    SetWindowText(GetDlgItem(hwnd, IDC_WNDFOREGRND), szBuf);
 
    if (g_dwThreadIdAttachTo == 0) {
       // If monitoring local input state system-wide, detach our input
       // state from the thread that created the current foreground window.
       AttachThreadInput(GetCurrentThreadId(), dwThreadIdAttachTo, FALSE);	
    }
 }