home *** CD-ROM | disk | FTP | other *** search
/ Chip 2003 November / Chip_2003-11_cd1.bin / software / dave / dqsd.exe / src / DQSDTools / KeyboardHook.cpp < prev    next >
C/C++ Source or Header  |  2003-01-05  |  10KB  |  386 lines

  1. // KeyboardHook.cpp : Single-thread Keyboard hook implementation
  2. //
  3.  
  4. #include "stdafx.h"
  5.  
  6. #include "DQSDTools.h"
  7. #include "KeyboardHook.h" 
  8. #include "Launcher.h" 
  9. #include "Utilities.h" 
  10.  
  11.  
  12. #define HOTKEY_WINDOW_CLASS_NAME    _T("DQSDHotKeyWindowClass")
  13. #define HOTKEY_WINDOW_NAME            _T("DQSDHotKeyWindow")
  14.  
  15. std::map< long, long > g_mapKeyCodeToCharCode;
  16.  
  17. // The handle of our hook
  18. static HHOOK hHook;
  19. static HWND  hHookedWindow;
  20.  
  21.  
  22. static LRESULT CALLBACK KeyboardProc(
  23.                               int code,       // hook code
  24.                               WPARAM wParam,  // virtual-key code
  25.                               LPARAM lParam   // keystroke-message information
  26.                               )
  27. {
  28.     if(code == HC_ACTION)
  29.     {
  30.         HWND hFocusWnd = GetFocus();
  31.         if(hFocusWnd != hHookedWindow)
  32.         {
  33.             // This is not us - don't do anything
  34.             ATLTRACE("Hooked message arrived with wrong focus (DQSD 0x%x, focus 0x%x)\n", hHookedWindow, hFocusWnd);
  35.             return CallNextHookEx(hHook, code, wParam, lParam);
  36.         }
  37.  
  38.         //-----VVVVV-----NOTE: You need to link with IMM32.LIB---
  39.         // check if the IME is up
  40.         {
  41.             HIMC himc = ImmGetContext(hFocusWnd);
  42.             bool bImmOn = (ImmGetOpenStatus (himc) != 0);
  43.             ImmReleaseContext (hFocusWnd, himc);
  44.  
  45.             if (bImmOn)
  46.             {
  47.                 // This is not us - don't do anything
  48.                 ATLTRACE("IMM is on\n");
  49.                 return CallNextHookEx(hHook, code, wParam, lParam);
  50.             }
  51.         }
  52.         //-----^^^^^^------
  53.  
  54.         bool bKeyDown = !(lParam & 0x80000000);
  55.  
  56.         if(wParam == VK_INSERT)
  57.         {
  58.             if(GetAsyncKeyState(VK_SHIFT) & 0x80000000)
  59.             {
  60.                 ATLTRACE(_T("Shift-INS: %c\n"), bKeyDown ? 'P' : 'R');
  61.                 if(bKeyDown)
  62.                 {
  63.                     // Shift-INS is paste - send a CTRL-V
  64.                     SendMessage(hFocusWnd, WM_CHAR, 'V'-'@', 0);
  65.                     return 0; 
  66.                 }
  67.             }
  68.             else if(GetAsyncKeyState(VK_CONTROL) & 0x80000000)
  69.             {
  70.                 ATLTRACE(_T("Ctrl-INS: %c\n"), bKeyDown ? 'P' : 'R');
  71.                 if(bKeyDown)
  72.                 {
  73.                     // Ctrl-INS is copy - send a CTRL-C
  74.                     SendMessage(hFocusWnd, WM_CHAR, 'C'-'@', 0);
  75.                     return 0; 
  76.                 }
  77.             }
  78.         }
  79.         else if(wParam == VK_DELETE)
  80.         {
  81.             ATLTRACE(_T("DEL: %c\n"), bKeyDown ? 'P' : 'R');
  82.  
  83.             if(GetAsyncKeyState(VK_SHIFT) & 0x80000000)
  84.             { 
  85.                 ATLTRACE(_T("Shift-DEL: %c\n"), bKeyDown ? 'P' : 'R');
  86.                 if(bKeyDown)
  87.                 {
  88.                     // Shift-DEL is cut - send a CTRL-X
  89.                     SendMessage(hFocusWnd, WM_CHAR, 'X'-'@', 0);
  90.                     return 0; 
  91.                 }
  92.             }
  93.             else
  94.             {
  95.                 // It's a DEL
  96.                 if(bKeyDown)
  97.                 {
  98.                     // Send a CTRL-D - special handler for this in search.htm
  99.                     SendMessage(hFocusWnd, WM_CHAR, 'D'-'@', 0);
  100.                     return 0; 
  101.                 }
  102.             }
  103.         }
  104.         else if(wParam == VK_UP)
  105.         {
  106.             if ( bKeyDown )
  107.             {
  108.                 // Send a CTRL-P
  109.                 SendMessage(hFocusWnd, WM_CHAR, 'P'-'@', 0);
  110.                 return 0;
  111.             }
  112.         }
  113.         else if(wParam == VK_DOWN)
  114.         {
  115.             if ( bKeyDown )
  116.             {
  117.                 // Send a CTRL-N
  118.                 SendMessage(hFocusWnd, WM_CHAR, 'N'-'@', 0);
  119.                 return 0;
  120.             }
  121.         }
  122.         else if(g_mapKeyCodeToCharCode.find( wParam ) != g_mapKeyCodeToCharCode.end() )
  123.         {
  124.             if ( bKeyDown )
  125.             {
  126.                 SendMessage(hFocusWnd, WM_CHAR, g_mapKeyCodeToCharCode[ wParam ], 0);
  127.                 return 0;
  128.             }
  129.         }
  130.         else
  131.         {
  132.             ATLTRACE(_T("Hook: %d (focus 0x%x)\n"), wParam, GetFocus());
  133.         }
  134.     }
  135.     return CallNextHookEx(hHook, code, wParam, lParam);
  136. }
  137.  
  138.  
  139. /*
  140. static BOOL CALLBACK EnumProc(
  141.                               HWND hwnd,        // handle to child window
  142.                               LPARAM lParam        // Pointer to discovered taskbar window
  143.                               )
  144. {
  145.     TCHAR className[100];
  146.     className[0] = '\0';        // In case the call fails
  147.  
  148.     // Look for the window which corresponds to our deskbar
  149.     GetClassName(hwnd, className, sizeof(className));
  150.     if(StrCmpI(className, "Internet Explorer_Server") == 0)
  151.     {
  152.         _RPT1(_CRT_WARN, "Found bar - hWnd 0x%x\n", hwnd);
  153.         *(HWND*)lParam = hwnd;
  154.         return FALSE;
  155.     }
  156.  
  157.     return TRUE;
  158. }
  159. */
  160.  
  161. //HWND hBarWnd = NULL;
  162.  
  163. LRESULT CALLBACK NotificationWndProc(
  164.     HWND hwnd,      // handle to window
  165.     UINT uMsg,      // message identifier
  166.     WPARAM wParam,  // first message parameter
  167.     LPARAM lParam   // second message parameter
  168.     )
  169. {
  170.     static int nAttempts;
  171.  
  172.     if(uMsg == WM_HOTKEY || (uMsg == WM_TIMER && wParam == 0x5744))
  173.     {
  174. //        _RPT0(_CRT_WARN, "HotKey\n");
  175. //        OutputDebugString("HotKey\n");
  176.  
  177.         if(uMsg == WM_HOTKEY)
  178.         {
  179.             nAttempts = 0;
  180.         }
  181.         else
  182.         {
  183.             nAttempts++;
  184.             if(nAttempts > 20)
  185.             {
  186.                 // We've failed in some way - kill the timer
  187.                 KillTimer(hwnd, 0x5744);
  188.             }
  189.         }
  190.  
  191.         HWND hBarWnd = g_hDQSDWindow; // UtilitiesFindDQSDWindow();
  192.         if(hBarWnd == NULL)
  193.         {
  194.             return 0;
  195.         }
  196.  
  197.         // Save the mouse position before these games...
  198.         POINT mousePoint;
  199.         GetCursorPos(&mousePoint);
  200.  
  201.         // Find our window
  202.         RECT taskBarRect;
  203.         GetWindowRect(hBarWnd, &taskBarRect);
  204.  
  205.         // We do all this larking about with the mouse because
  206.         // SetForegroundWindow is crippled nowadays, and because the DQSD edit control
  207.         // doesn't actually seem to get a proper caret if you SFW to it anyway.
  208.         ATLTRACE("TaskBarRect: %d,%d,%d,%d\n", taskBarRect.left, taskBarRect.top, taskBarRect.right, taskBarRect.bottom);
  209.         if(taskBarRect.top >= GetSystemMetrics(SM_CYSCREEN) 
  210.             || taskBarRect.bottom < 0 
  211.             || taskBarRect.left >= GetSystemMetrics(SM_CXSCREEN)
  212.             || taskBarRect.right < 0)
  213.         {
  214.             // The taskbar is auto-hidden - we need to send more than one click - one to unhide the tool bar, 
  215.             // and one to set the focus
  216.             SetTimer(hwnd, 0x5744, 50, NULL);
  217.         }
  218.         else
  219.         {
  220.             // The taskbar is not autohidden - don't send any timer messages
  221.             KillTimer(hwnd, 0x5744);
  222.  
  223.             // Test if our window is visible (rather than overlayed by something else)
  224.             // This only happens if you have a taskbar without always-on-top set
  225.             POINT topLeft;
  226.             topLeft.x = taskBarRect.left;
  227.             topLeft.y = taskBarRect.top;
  228.             HWND hTestWindow = WindowFromPoint(topLeft);
  229.             ATLTRACE("HotKey: Test window 0x%x, DQSD Window 0x%x\n", hTestWindow, hBarWnd);
  230.             if(hBarWnd != hTestWindow)
  231.             {
  232.                 SetForegroundWindow(hBarWnd);
  233.                 // Take the focus off the bar wnd, so that we get the normal 
  234.                 // 'highlight everything' behaviour when simulating a click into the bar
  235.                 SetFocus(GetParent(hBarWnd));
  236.             }
  237.         }
  238.  
  239.         // Calculate the position of a simultated mouse click
  240.         // The SendInput structure takes a position scaled 0-65536 across the primary monitor
  241.         // Hence the muldivs
  242.         INPUT mouseClick;
  243.         ZeroMemory(&mouseClick, sizeof(mouseClick));
  244.         mouseClick.type = INPUT_MOUSE;
  245.         mouseClick.mi.dx = (taskBarRect.left + taskBarRect.right) / 2;
  246.         mouseClick.mi.dx = MulDiv(mouseClick.mi.dx, 65536, GetSystemMetrics(SM_CXSCREEN));
  247.         mouseClick.mi.dy = (taskBarRect.top + taskBarRect.bottom) / 2;
  248.         mouseClick.mi.dy = MulDiv(mouseClick.mi.dy, 65536, GetSystemMetrics(SM_CYSCREEN));
  249.         mouseClick.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
  250.         SendInput(1, &mouseClick, sizeof(mouseClick));
  251.  
  252.         mouseClick.mi.dwFlags = MOUSEEVENTF_LEFTUP;
  253.         SendInput(1, &mouseClick, sizeof(mouseClick));
  254.  
  255.         // Put the mouse back where we found it, because we're nice like that
  256.         SetCursorPos(mousePoint.x, mousePoint.y);
  257.     }
  258.     else if(uMsg == WM_DESTROY)
  259.     {
  260. //        OutputDebugString("HotKey Destroy\n");
  261.         UnregisterHotKey(hwnd, GetWindowLong(hwnd, GWL_USERDATA));
  262.     }
  263.  
  264.     return DefWindowProc(hwnd, uMsg,wParam,lParam);
  265. }
  266.  
  267.  
  268. //
  269. // Install a keyboard hook on the search deskbars message handler thread
  270. //
  271. //
  272. HRESULT KeyboardHookInstall(HWND hBarWnd, HHOOK& hInstalledHook)
  273. {
  274.     // Did we find the window?
  275.     if(hBarWnd == NULL)
  276.     {
  277.         return CLauncher::Error(IDS_ERR_CANT_INSTALL_KEYBOARD_HOOK, IID_ILauncher);
  278.     }
  279.     
  280.     // Get a handle to the thread which owns the message queue for this window
  281.     DWORD threadId = GetWindowThreadProcessId(hBarWnd, NULL);
  282.  
  283.     ATLTRACE("Thread ID 0x%x\n", threadId);
  284.  
  285.     if(hHook != NULL)
  286.     {
  287.         ATLTRACE("DQSD: Keyboard Hook already installed\n");
  288.         return CLauncher::Error(IDS_ERR_CANT_INSTALL_KEYBOARD_HOOK, IID_ILauncher);
  289.     }
  290.  
  291.     hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, _Module.GetModuleInstance(), threadId);
  292.     hInstalledHook = hHook;
  293.     hHookedWindow = hBarWnd;
  294.  
  295.     ATLTRACE("hHook 0x%x\n", hHook);
  296.  
  297.     return S_OK;
  298. }
  299.  
  300. void
  301. KeyboardHookRemove(HHOOK hInstalledHook)
  302. {
  303.     if(hInstalledHook != NULL)
  304.     {
  305.         UnhookWindowsHookEx(hInstalledHook);
  306.         hHook = NULL;
  307.     }
  308. }
  309.  
  310.  
  311. //
  312. // Install a hotkey
  313. //
  314. // Return:
  315. //        HRESULT
  316. //
  317. HRESULT
  318. KeyboardInstallHotkey(int vkCode, LPCTSTR pModifierNames, HWND* phwndNotification, LPDISPATCH pDispDocument)
  319. {
  320.     HWND hBarWnd = UtilitiesFindDQSDWindow(pDispDocument);
  321.     if(hBarWnd == NULL)
  322.     {
  323.         return CLauncher::Error(IDS_ERR_HOTKEY_NO_BAR_WINDOW, IID_ILauncher, E_FAIL);
  324.     }
  325.  
  326.     // Create a window which we use to receive notifications
  327.     WNDCLASS wc;
  328.     ZeroMemory(&wc, sizeof(wc));
  329.     wc.lpfnWndProc = NotificationWndProc;
  330.     wc.lpszClassName = HOTKEY_WINDOW_CLASS_NAME;
  331.  
  332.     HWND hExistingWindow = FindWindow(wc.lpszClassName, HOTKEY_WINDOW_NAME);
  333.     if(hExistingWindow != NULL)
  334.     {
  335.         // THere's already hotkey window
  336.         ATLTRACE("HotKey - window exists\n");
  337.         DestroyWindow(hExistingWindow);
  338.     }
  339.  
  340.     RegisterClass(&wc);
  341.  
  342.     *phwndNotification = CreateWindow(wc.lpszClassName, HOTKEY_WINDOW_NAME, 0, 0, 0, 0, 0, hBarWnd, NULL, NULL, NULL);
  343.     if(*phwndNotification == NULL)
  344.     {
  345.         ATLTRACE("Failed to create a window for hotkeys (err %d)\n", GetLastError());
  346.         return CLauncher::Error(IDS_ERR_HOTKEY_WINDOW_FAILED, IID_ILauncher, E_FAIL);
  347.     }
  348.  
  349.     // Make an ID for the window and store it in the windows userdata slot
  350.     // so that it can be used to unregister the hotkey
  351.     ATOM hotKeyId = GlobalAddAtom(_T("DQSDHotKeyAtom"));
  352.     SetWindowLong(*phwndNotification, GWL_USERDATA, hotKeyId);
  353.  
  354.     // Try and work out the modifier - default to the Windows key
  355.     UINT keyModifier = 0;
  356.     if(StrStrI(pModifierNames, _T("WIN")) != NULL)
  357.     {
  358.         keyModifier |= MOD_WIN;
  359.     }
  360.     if(StrStrI(pModifierNames, _T("ALT")) != NULL)
  361.     {
  362.         keyModifier |= MOD_ALT;
  363.     }
  364.     if(StrStrI(pModifierNames, _T("SHIFT")) != NULL)
  365.     {
  366.         keyModifier |= MOD_SHIFT;
  367.     }
  368.     if(StrStrI(pModifierNames, _T("CONTROL"))!= NULL)
  369.     {
  370.         keyModifier |= MOD_CONTROL;
  371.     }
  372.     if(keyModifier == 0)
  373.     {
  374.         // Blank or invalid modifier string - use WIN
  375.         keyModifier = MOD_WIN;
  376.     }
  377.     
  378.     if(!RegisterHotKey(*phwndNotification, hotKeyId, keyModifier, vkCode))
  379.     {
  380.         ATLTRACE("Failed to register hotkey (err %d)\n", GetLastError());
  381.         return CLauncher::Error(IDS_ERR_HOTKEY_REG_FAILED, IID_ILauncher, E_FAIL);
  382.     }
  383.  
  384.     return S_OK;
  385. }
  386.