1 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 #include "stringtool.h"
18 #define HOOK_DATA_NAME _T("{08D6E55C-5103-4e00-8209-A1C4AB13BBEF}") _T(VERSION)
20 #define HOOK_DATA_NAME_ARCH _T("{290C0D51-8AEE-403d-9172-E43D46270996}") _T(VERSION)
22 #define HOOK_DATA_NAME_ARCH _T("{716A5DEB-CB02-4438-ABC8-D00E48673E45}") _T(VERSION)
25 // Some applications use different values for below messages
26 // when double click of title bar.
27 #define SC_MAXIMIZE2 (SC_MAXIMIZE + 2)
28 #define SC_MINIMIZE2 (SC_MINIMIZE + 2)
29 #define SC_RESTORE2 (SC_RESTORE + 2)
33 #define HOOK_RPT0(msg)
34 #define HOOK_RPT1(msg, arg1)
35 #define HOOK_RPT2(msg, arg1, arg2)
37 #define HOOK_RPT0(msg) if (g.m_isLogging) { _RPT0(_CRT_WARN, msg); }
38 #define HOOK_RPT1(msg, arg1) if (g.m_isLogging) { _RPT1(_CRT_WARN, msg, arg1); }
39 #define HOOK_RPT2(msg, arg1, arg2) if (g.m_isLogging) { _RPT2(_CRT_WARN, msg, arg1, arg2); }
42 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46 DllExport HookData *g_hookData; ///
52 HHOOK m_hHookGetMessage; ///
53 HHOOK m_hHookCallWndProc; ///
56 static HookDataArch *s_hookDataArch;
60 HANDLE m_hHookData; ///
61 HANDLE m_hHookDataArch; ///
63 HINSTANCE m_hInstDLL; ///
65 UINT m_WM_MAYU_MESSAGE; ///
67 bool m_isImeCompositioning; ///
68 HHOOK m_hHookMouseProc; ///
70 HHOOK m_hHookKeyboardProc; ///
71 KEYBOARD_DETOUR m_keyboardDetour;
74 DWORD m_hwndTaskTray; ///
79 _TCHAR m_moduleName[GANA_MAX_PATH];
86 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90 static void notifyThreadDetach();
91 static void notifyShow(NotifyShow::Show i_show, bool i_isMDI);
92 static void notifyLog(_TCHAR *i_msg);
93 static bool mapHookData();
94 static void unmapHookData();
95 static bool initialize();
98 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104 _TCHAR path[GANA_MAX_PATH];
105 GetModuleFileName(NULL, path, GANA_MAX_PATH);
106 _tsplitpath_s(path, NULL, 0, NULL, 0, g.m_moduleName, GANA_MAX_PATH, NULL, 0);
107 if (_tcsncmp(g.m_moduleName, _T("Dbgview"), sizeof(_T("Dbgview"))/sizeof(_TCHAR)) != 0 &&
108 _tcsncmp(g.m_moduleName, _T("windbg"), sizeof(_T("windbg"))/sizeof(_TCHAR)) != 0)
110 g.m_isLogging = true;
115 CreateFile(NOTIFY_MAILSLOT_NAME, GENERIC_WRITE,
116 FILE_SHARE_READ | FILE_SHARE_WRITE,
117 (SECURITY_ATTRIBUTES *)NULL, OPEN_EXISTING,
118 FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
119 if (g.m_hMailslot == INVALID_HANDLE_VALUE)
121 HOOK_RPT2("MAYU: %S create mailslot failed(0x%08x)\r\n", g.m_moduleName, GetLastError());
125 HOOK_RPT1("MAYU: %S create mailslot successed\r\n", g.m_moduleName);
127 #endif //USE_MAILSLOT
130 _tsetlocale(LC_ALL, _T(""));
131 g.m_WM_MAYU_MESSAGE =
132 RegisterWindowMessage(addSessionId(WM_MAYU_MESSAGE_NAME).c_str());
133 g.m_hwndTaskTray = g_hookData->m_hwndTaskTray;
134 g.m_isInitialized = true;
139 BOOL WINAPI DllMain(HINSTANCE i_hInstDLL, DWORD i_fdwReason,
140 LPVOID /* i_lpvReserved */)
144 case DLL_PROCESS_ATTACH:
147 g.m_isLogging = false;
149 g.m_isInitialized = false;
150 g.m_hInstDLL = i_hInstDLL;
153 case DLL_THREAD_ATTACH:
155 case DLL_PROCESS_DETACH:
156 notifyThreadDetach();
159 if (g.m_hMailslot != INVALID_HANDLE_VALUE)
161 CloseHandle(g.m_hMailslot);
162 g.m_hMailslot = INVALID_HANDLE_VALUE;
164 #endif //USE_MAILSLOT
166 case DLL_THREAD_DETACH:
167 notifyThreadDetach();
177 static bool mapHookData()
179 g.m_hHookData = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
181 addSessionId(HOOK_DATA_NAME).c_str());
189 (HookData *)MapViewOfFile(g.m_hHookData, FILE_MAP_READ | FILE_MAP_WRITE,
190 0, 0, sizeof(HookData));
197 g.m_hHookDataArch = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
198 0, sizeof(HookDataArch),
199 addSessionId(HOOK_DATA_NAME_ARCH).c_str());
200 if (!g.m_hHookDataArch)
207 (HookDataArch *)MapViewOfFile(g.m_hHookDataArch, FILE_MAP_READ | FILE_MAP_WRITE,
208 0, 0, sizeof(HookDataArch));
220 static void unmapHookData()
223 UnmapViewOfFile(g_hookData);
226 CloseHandle(g.m_hHookData);
227 g.m_hHookData = NULL;
229 UnmapViewOfFile(s_hookDataArch);
230 s_hookDataArch = NULL;
231 if (g.m_hHookDataArch)
232 CloseHandle(g.m_hHookDataArch);
233 g.m_hHookDataArch = NULL;
238 DllExport bool notify(void *i_data, size_t i_dataSize)
249 if (g.m_hMailslot != INVALID_HANDLE_VALUE)
252 ret = WriteFile(g.m_hMailslot, i_data, i_dataSize, &len, NULL);
256 HOOK_RPT2("MAYU: %S WriteFile to mailslot failed(0x%08x)\r\n", g.m_moduleName, GetLastError());
260 HOOK_RPT1("MAYU: %S WriteFile to mailslot successed\r\n", g.m_moduleName);
264 #else // !USE_MAILSLOT
265 cd.dwData = reinterpret_cast<Notify *>(i_data)->m_type;
266 cd.cbData = i_dataSize;
268 if (g.m_hwndTaskTray == 0)
270 if (!SendMessageTimeout(reinterpret_cast<HWND>(g.m_hwndTaskTray),
271 WM_COPYDATA, NULL, reinterpret_cast<LPARAM>(&cd),
272 SMTO_ABORTIFHUNG | SMTO_NORMAL, 5000, &result))
274 _RPT0(_CRT_WARN, "MAYU: SendMessageTimeout() timeouted\r\n");
277 #endif // !USE_MAILSLOT
282 /// get class name and title name
283 static void getClassNameTitleName(HWND i_hwnd, bool i_isInMenu,
284 tstringi *o_className,
285 tstring *o_titleName)
287 tstringi &className = *o_className;
288 tstring &titleName = *o_titleName;
290 bool isTheFirstTime = true;
294 className = titleName = _T("MENU");
295 isTheFirstTime = false;
300 _TCHAR buf[MAX(GANA_MAX_PATH, GANA_MAX_ATOM_LENGTH)];
304 GetClassName(i_hwnd, buf, NUMBER_OF(buf));
306 GetModuleFileName(GetModuleHandle(NULL), buf, NUMBER_OF(buf));
307 buf[NUMBER_OF(buf) - 1] = _T('\0');
311 className = tstringi(buf) + _T(":") + className;
316 GetWindowText(i_hwnd, buf, NUMBER_OF(buf));
317 buf[NUMBER_OF(buf) - 1] = _T('\0');
318 for (_TCHAR *b = buf; *b; ++ b)
319 if (_istlead(*b) && b[1])
321 else if (_istcntrl(*b))
327 titleName = tstring(buf) + _T(":") + titleName;
332 i_hwnd = GetParent(i_hwnd);
333 isTheFirstTime = false;
339 static void updateShow(HWND i_hwnd, NotifyShow::Show i_show)
347 LONG_PTR style = GetWindowLongPtr(i_hwnd, GWL_STYLE);
349 LONG style = GetWindowLong(i_hwnd, GWL_STYLE);
351 if (!(style & WS_MAXIMIZEBOX) && !(style & WS_MAXIMIZEBOX))
352 return; // ignore window that has neither maximize or minimize button
354 if (style & WS_CHILD)
357 LONG_PTR exStyle = GetWindowLongPtr(i_hwnd, GWL_EXSTYLE);
359 LONG exStyle = GetWindowLong(i_hwnd, GWL_EXSTYLE);
361 if (exStyle & WS_EX_MDICHILD)
366 return; // ignore non-MDI child window case
369 notifyShow(i_show, isMDI);
373 /// notify WM_Targetted
374 static void notifyName(HWND i_hwnd, Notify::Type i_type = Notify::Type_name)
378 getClassNameTitleName(i_hwnd, g.m_isInMenu, &className, &titleName);
380 NotifySetFocus *nfc = new NotifySetFocus;
381 nfc->m_type = i_type;
382 nfc->m_threadId = GetCurrentThreadId();
383 nfc->m_hwnd = reinterpret_cast<DWORD>(i_hwnd);
384 tcslcpy(nfc->m_className, className.c_str(), NUMBER_OF(nfc->m_className));
385 tcslcpy(nfc->m_titleName, titleName.c_str(), NUMBER_OF(nfc->m_titleName));
387 notify(nfc, sizeof(*nfc));
392 /// notify WM_SETFOCUS
393 static void notifySetFocus(bool i_doesForce = false)
395 HWND hwnd = GetFocus();
396 if (i_doesForce || hwnd != g.m_hwndFocus)
398 g.m_hwndFocus = hwnd;
399 notifyName(hwnd, Notify::Type_setFocus);
405 static void notifySync()
408 n.m_type = Notify::Type_sync;
409 notify(&n, sizeof(n));
413 /// notify DLL_THREAD_DETACH
414 static void notifyThreadDetach()
416 NotifyThreadDetach ntd;
417 ntd.m_type = Notify::Type_threadDetach;
418 ntd.m_threadId = GetCurrentThreadId();
419 notify(&ntd, sizeof(ntd));
423 /// notify WM_COMMAND, WM_SYSCOMMAND
424 static void notifyCommand(
425 HWND i_hwnd, UINT i_message, WPARAM i_wParam, LPARAM i_lParam)
428 if (g_hookData->m_doesNotifyCommand)
431 ntc.m_type = Notify::Type_command;
433 ntc.m_message = i_message;
434 ntc.m_wParam = i_wParam;
435 ntc.m_lParam = i_lParam;
436 notify(&ntc, sizeof(ntc));
442 /// notify show of current window
443 static void notifyShow(NotifyShow::Show i_show, bool i_isMDI)
446 ns.m_type = Notify::Type_show;
448 ns.m_isMDI = i_isMDI;
449 notify(&ns, sizeof(ns));
454 static void notifyLog(_TCHAR *i_msg)
457 nl.m_type = Notify::Type_log;
458 tcslcpy(nl.m_msg, i_msg, NUMBER_OF(nl.m_msg));
459 notify(&nl, sizeof(nl));
464 static void funcRecenter(HWND i_hwnd)
466 _TCHAR buf[MAX(GANA_MAX_PATH, GANA_MAX_ATOM_LENGTH)];
467 GetClassName(i_hwnd, buf, NUMBER_OF(buf));
469 if (_tcsicmp(buf, _T("Edit")) == 0)
471 else if (_tcsnicmp(buf, _T("RichEdit"), 8) == 0)
474 return; // this function only works for Edit control
477 LONG_PTR style = GetWindowLongPtr(i_hwnd, GWL_STYLE);
479 LONG style = GetWindowLong(i_hwnd, GWL_STYLE);
481 if (!(style & ES_MULTILINE))
482 return; // this function only works for multi line Edit control
485 GetClientRect(i_hwnd, &rc);
486 POINTL p = { (rc.right + rc.left) / 2, (rc.top + rc.bottom) / 2 };
490 line = SendMessage(i_hwnd, EM_CHARFROMPOS, 0, MAKELPARAM(p.x, p.y));
495 int ci = SendMessage(i_hwnd, EM_CHARFROMPOS, 0, (LPARAM)&p);
496 line = SendMessage(i_hwnd, EM_EXLINEFROMCHAR, 0, ci);
498 int caretLine = SendMessage(i_hwnd, EM_LINEFROMCHAR, -1, 0);
499 SendMessage(i_hwnd, EM_LINESCROLL, 0, caretLine - line);
504 static void funcSetImeStatus(HWND i_hwnd, int i_status)
508 hIMC = ImmGetContext(i_hwnd);
509 if (hIMC == INVALID_HANDLE_VALUE)
513 i_status = !ImmGetOpenStatus(hIMC);
515 ImmSetOpenStatus(hIMC, i_status);
516 ImmReleaseContext(i_hwnd, hIMC);
521 static void funcSetImeString(HWND i_hwnd, int i_size)
524 _TCHAR *buf = new _TCHAR(i_size);
526 _TCHAR ImeDesc[GANA_MAX_ATOM_LENGTH];
531 = CreateFile(addSessionId(HOOK_PIPE_NAME).c_str(), GENERIC_READ,
532 FILE_SHARE_READ, (SECURITY_ATTRIBUTES *)NULL,
533 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
534 error = ReadFile(hPipe, buf, i_size, &len, NULL);
537 ImeDescLen = ImmGetDescription(GetKeyboardLayout(0),
538 ImeDesc, sizeof(ImeDesc));
539 if (_tcsncmp(ImeDesc, _T("SKKIME"), ImeDescLen) > 0)
540 denom = sizeof(_TCHAR);
542 HIMC hIMC = ImmGetContext(i_hwnd);
543 if (hIMC == INVALID_HANDLE_VALUE)
546 int status = ImmGetOpenStatus(hIMC);
547 ImmSetCompositionString(hIMC, SCS_SETSTR, buf, len / denom, NULL, 0);
549 ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
551 ImmSetOpenStatus(hIMC, status);
552 ImmReleaseContext(i_hwnd, hIMC);
556 /// notify lock state
557 /*DllExport*/ void notifyLockState(int i_cause)
560 n.m_type = Notify::Type_lockState;
561 n.m_isNumLockToggled = !!(GetKeyState(VK_NUMLOCK) & 1);
562 n.m_isCapsLockToggled = !!(GetKeyState(VK_CAPITAL) & 1);
563 n.m_isScrollLockToggled = !!(GetKeyState(VK_SCROLL) & 1);
564 n.m_isKanaLockToggled = !!(GetKeyState(VK_KANA) & 1);
565 n.m_isImeLockToggled = g.m_isImeLock;
566 n.m_isImeCompToggled = g.m_isImeCompositioning;
567 n.m_debugParam = i_cause;
568 notify(&n, sizeof(n));
571 DllExport void notifyLockState()
577 /// hook of GetMessage
578 LRESULT CALLBACK getMessageProc(int i_nCode, WPARAM i_wParam, LPARAM i_lParam)
580 if (!g.m_isInitialized)
586 MSG &msg = (*(MSG *)i_lParam);
588 if (i_wParam != PM_REMOVE)
595 notifyCommand(msg.hwnd, msg.message, msg.wParam, msg.lParam);
602 if (HIMC hIMC = ImmGetContext(msg.hwnd))
604 bool prev = g.m_isImeLock;
605 g.m_isImeLock = !!ImmGetOpenStatus(hIMC);
606 ImmReleaseContext(msg.hwnd, hIMC);
607 if (prev != g.m_isImeLock) {
611 int nVirtKey = (int)msg.wParam;
612 // int repeatCount = (msg.lParam & 0xffff);
613 BYTE scanCode = (BYTE)((msg.lParam >> 16) & 0xff);
614 bool isExtended = !!(msg.lParam & (1 << 24));
615 // bool isAltDown = !!(msg.lParam & (1 << 29));
616 // bool isKeyup = !!(msg.lParam & (1 << 31));
618 if (nVirtKey == VK_CAPITAL ||
619 nVirtKey == VK_NUMLOCK ||
620 nVirtKey == VK_KANA ||
621 nVirtKey == VK_SCROLL)
623 else if (scanCode == g_hookData->m_syncKey &&
624 isExtended == g_hookData->m_syncKeyIsExtended)
628 case WM_IME_STARTCOMPOSITION:
629 g.m_isImeCompositioning = true;
632 case WM_IME_ENDCOMPOSITION:
633 g.m_isImeCompositioning = false;
637 if (i_wParam == PM_REMOVE && msg.message == g.m_WM_MAYU_MESSAGE)
641 case MayuMessage_notifyName:
642 notifyName(msg.hwnd);
644 case MayuMessage_funcRecenter:
645 funcRecenter(msg.hwnd);
647 case MayuMessage_funcSetImeStatus:
648 funcSetImeStatus(msg.hwnd, msg.lParam);
650 case MayuMessage_funcSetImeString:
651 funcSetImeString(msg.hwnd, msg.lParam);
658 return CallNextHookEx(s_hookDataArch->m_hHookGetMessage,
659 i_nCode, i_wParam, i_lParam);
663 /// hook of SendMessage
664 LRESULT CALLBACK callWndProc(int i_nCode, WPARAM i_wParam, LPARAM i_lParam)
666 if (!g.m_isInitialized)
672 CWPSTRUCT &cwps = *(CWPSTRUCT *)i_lParam;
676 switch (cwps.message)
688 updateShow(cwps.hwnd, NotifyShow::Show_Maximized);
692 updateShow(cwps.hwnd, NotifyShow::Show_Minimized);
696 updateShow(cwps.hwnd, NotifyShow::Show_Normal);
703 notifyCommand(cwps.hwnd, cwps.message, cwps.wParam, cwps.lParam);
709 updateShow(cwps.hwnd, NotifyShow::Show_Maximized);
712 updateShow(cwps.hwnd, NotifyShow::Show_Minimized);
715 updateShow(cwps.hwnd, NotifyShow::Show_Normal);
721 case WM_MOUSEACTIVATE:
725 if (LOWORD(cwps.wParam) != WA_INACTIVE)
728 if (HIWORD(cwps.wParam)) // check minimized flag
731 notifyShow(NotifyShow::Show_Minimized, false);
732 //notifyShow(NotifyShow::Show_Normal, true);
736 case WM_ENTERMENULOOP:
738 notifySetFocus(true);
740 case WM_EXITMENULOOP:
741 g.m_isInMenu = false;
742 notifySetFocus(true);
745 g.m_isInMenu = false;
747 if (g_hookData->m_correctKanaLockHandling) {
748 if (HIMC hIMC = ImmGetContext(cwps.hwnd)) {
749 bool status = !!ImmGetOpenStatus(hIMC);
750 // this code set the VK_KANA state correctly.
751 ImmSetOpenStatus(hIMC, !status);
752 ImmSetOpenStatus(hIMC, status);
753 ImmReleaseContext(cwps.hwnd, hIMC);
759 case WM_IME_STARTCOMPOSITION:
760 g.m_isImeCompositioning = true;
763 case WM_IME_ENDCOMPOSITION:
764 g.m_isImeCompositioning = false;
768 if (cwps.wParam == IMN_SETOPENSTATUS)
769 if (HIMC hIMC = ImmGetContext(cwps.hwnd))
771 g.m_isImeLock = !!ImmGetOpenStatus(hIMC);
772 ImmReleaseContext(cwps.hwnd, hIMC);
778 return CallNextHookEx(s_hookDataArch->m_hHookCallWndProc, i_nCode,
783 static LRESULT CALLBACK lowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
785 MSLLHOOKSTRUCT *pMsll = (MSLLHOOKSTRUCT*)lParam;
786 LONG dx = pMsll->pt.x - g_hookData->m_mousePos.x;
787 LONG dy = pMsll->pt.y - g_hookData->m_mousePos.y;
788 HWND target = reinterpret_cast<HWND>(g_hookData->m_hwndMouseHookTarget);
790 if (!g.m_isInitialized)
793 if (!g_hookData || nCode < 0 || wParam != WM_MOUSEMOVE)
796 switch (g_hookData->m_mouseHookType)
798 case MouseHookType_Wheel:
799 // For this type, g_hookData->m_mouseHookParam means
800 // translate rate mouse move to wheel.
801 mouse_event(MOUSEEVENTF_WHEEL, 0, 0,
802 g_hookData->m_mouseHookParam * dy, 0);
805 case MouseHookType_WindowMove:
809 if (!GetWindowRect(target, &curRect))
812 // g_hookData->m_mouseHookParam < 0 means
813 // target window to move is MDI.
814 if (g_hookData->m_mouseHookParam < 0)
816 HWND parent = GetParent(target);
817 POINT p = {curRect.left, curRect.top};
819 if (parent == NULL || !ScreenToClient(parent, &p))
826 SetWindowPos(target, NULL,
830 SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE |
831 SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
832 g_hookData->m_mousePos = pMsll->pt;
836 case MouseHookType_None:
843 return CallNextHookEx(g.m_hHookMouseProc,
844 nCode, wParam, lParam);
849 static LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
851 KBDLLHOOKSTRUCT *pKbll = (KBDLLHOOKSTRUCT*)lParam;
853 if (!g.m_isInitialized)
856 if (!g_hookData || nCode < 0)
859 if (g.m_keyboardDetour && g.m_engine)
862 result = g.m_keyboardDetour(g.m_engine, pKbll);
869 return CallNextHookEx(g.m_hHookKeyboardProc,
870 nCode, wParam, lParam);
876 DllExport int installHooks(KEYBOARD_DETOUR i_keyboardDetour, Engine *i_engine)
878 if (!g.m_isInitialized)
881 g.m_hwndTaskTray = g_hookData->m_hwndTaskTray;
882 s_hookDataArch->m_hHookGetMessage =
883 SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)getMessageProc,
885 s_hookDataArch->m_hHookCallWndProc =
886 SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)callWndProc, g.m_hInstDLL, 0);
887 g_hookData->m_mouseHookType = MouseHookType_None;
888 if (i_engine != NULL)
891 g.m_keyboardDetour = i_keyboardDetour;
892 g.m_engine = i_engine;
893 g.m_hHookKeyboardProc =
894 SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)lowLevelKeyboardProc,
898 SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)lowLevelMouseProc,
906 DllExport int uninstallHooks()
908 if (s_hookDataArch->m_hHookGetMessage)
909 UnhookWindowsHookEx(s_hookDataArch->m_hHookGetMessage);
910 s_hookDataArch->m_hHookGetMessage = NULL;
911 if (s_hookDataArch->m_hHookCallWndProc)
912 UnhookWindowsHookEx(s_hookDataArch->m_hHookCallWndProc);
913 s_hookDataArch->m_hHookCallWndProc = NULL;
914 if (g.m_hHookMouseProc)
915 UnhookWindowsHookEx(g.m_hHookMouseProc);
916 g.m_hHookMouseProc = NULL;
918 if (g.m_hHookKeyboardProc)
919 UnhookWindowsHookEx(g.m_hHookKeyboardProc);
920 g.m_hHookKeyboardProc = NULL;
922 g.m_hwndTaskTray = 0;