OSDN Git Service

enable command notify on x64
[yamy/yamy.git] / hook.cpp
1 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
2 // hook.cpp\r
3 \r
4 \r
5 #define _HOOK_CPP\r
6 \r
7 #include "misc.h"\r
8 \r
9 #include "hook.h"\r
10 #include "stringtool.h"\r
11 \r
12 #include <locale.h>\r
13 #include <imm.h>\r
14 #include <richedit.h>\r
15 \r
16 \r
17 ///\r
18 #define HOOK_DATA_NAME _T("{08D6E55C-5103-4e00-8209-A1C4AB13BBEF}") _T(VERSION)\r
19 #ifdef _WIN64\r
20 #define HOOK_DATA_NAME_ARCH _T("{290C0D51-8AEE-403d-9172-E43D46270996}") _T(VERSION)\r
21 #else // !_WIN64\r
22 #define HOOK_DATA_NAME_ARCH _T("{716A5DEB-CB02-4438-ABC8-D00E48673E45}") _T(VERSION)\r
23 #endif // !_WIN64\r
24 \r
25 // Some applications use different values for below messages\r
26 // when double click of title bar.\r
27 #define SC_MAXIMIZE2 (SC_MAXIMIZE + 2)\r
28 #define SC_MINIMIZE2 (SC_MINIMIZE + 2)\r
29 #define SC_RESTORE2 (SC_RESTORE + 2)\r
30 \r
31 // Debug Macros\r
32 #ifdef NDEBUG\r
33 #define HOOK_RPT0(msg)\r
34 #define HOOK_RPT1(msg, arg1)\r
35 #define HOOK_RPT2(msg, arg1, arg2)\r
36 #define HOOK_RPT3(msg, arg1, arg2, arg3)\r
37 #define HOOK_RPT4(msg, arg1, arg2, arg3, arg4)\r
38 #define HOOK_RPT5(msg, arg1, arg2, arg3, arg4, arg5)\r
39 #else\r
40 #define HOOK_RPT0(msg) if (g.m_isLogging) { _RPT0(_CRT_WARN, msg); }\r
41 #define HOOK_RPT1(msg, arg1) if (g.m_isLogging) { _RPT1(_CRT_WARN, msg, arg1); }\r
42 #define HOOK_RPT2(msg, arg1, arg2) if (g.m_isLogging) { _RPT2(_CRT_WARN, msg, arg1, arg2); }\r
43 #define HOOK_RPT3(msg, arg1, arg2, arg3) if (g.m_isLogging) { _RPT3(_CRT_WARN, msg, arg1, arg2, arg3); }\r
44 #define HOOK_RPT4(msg, arg1, arg2, arg3, arg4) if (g.m_isLogging) { _RPT4(_CRT_WARN, msg, arg1, arg2, arg3, arg4); }\r
45 #define HOOK_RPT5(msg, arg1, arg2, arg3, arg4, arg5) if (g.m_isLogging) { _RPT5(_CRT_WARN, msg, arg1, arg2, arg3, arg4, arg5); }\r
46 #endif\r
47 \r
48 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
49 // Global Variables\r
50 \r
51 \r
52 DllExport HookData *g_hookData;                 ///\r
53 \r
54 ///\r
55 class HookDataArch\r
56 {\r
57 public:\r
58         HHOOK m_hHookGetMessage;                        ///\r
59         HHOOK m_hHookCallWndProc;                       ///\r
60 };\r
61 \r
62 static HookDataArch *s_hookDataArch;\r
63 \r
64 struct Globals {\r
65         HANDLE m_hHookData;                             ///\r
66         HANDLE m_hHookDataArch;                 ///\r
67         HWND m_hwndFocus;                               ///\r
68         HINSTANCE m_hInstDLL;                           ///\r
69         bool m_isInMenu;                                ///\r
70         UINT m_WM_MAYU_MESSAGE;                 ///\r
71         bool m_isImeLock;                               ///\r
72         bool m_isImeCompositioning;                     ///\r
73         HHOOK m_hHookMouseProc;                 ///\r
74         HHOOK m_hHookKeyboardProc;                      ///\r
75         INPUT_DETOUR m_keyboardDetour;\r
76         INPUT_DETOUR m_mouseDetour;\r
77         Engine *m_engine;\r
78         DWORD m_hwndTaskTray;                           ///\r
79         HANDLE m_hMailslot;\r
80         bool m_isInitialized;\r
81 #ifdef HOOK_LOG_TO_FILE\r
82         HANDLE m_logFile;\r
83 #endif // HOOK_LOG_TO_FILE\r
84 #ifndef NDEBUG\r
85         bool m_isLogging;\r
86         _TCHAR m_moduleName[GANA_MAX_PATH];\r
87 #endif // !NDEBUG\r
88 };\r
89 \r
90 static Globals g;\r
91 \r
92 \r
93 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
94 // Prototypes\r
95 \r
96 \r
97 static void notifyThreadDetach();\r
98 static void notifyShow(NotifyShow::Show i_show, bool i_isMDI);\r
99 static void notifyLog(_TCHAR *i_msg);\r
100 static bool mapHookData(bool i_isYamy);\r
101 static void unmapHookData();\r
102 static bool initialize(bool i_isYamy);\r
103 \r
104 \r
105 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
106 // Functions\r
107 \r
108 \r
109 #ifdef HOOK_LOG_TO_FILE\r
110 static void WriteToLog(const char *data)\r
111 {\r
112         char buf[1024];\r
113         DWORD count;\r
114 \r
115         WideCharToMultiByte(CP_THREAD_ACP, 0, g.m_moduleName, -1, buf, NUMBER_OF(buf), NULL, NULL);\r
116         strcat(buf, ": ");\r
117         strcat(buf, data);\r
118         SetFilePointer(g.m_logFile, 0, NULL, FILE_END);\r
119         WriteFile(g.m_logFile, buf, strlen(buf), &count, NULL);\r
120         FlushFileBuffers(g.m_logFile);\r
121 }\r
122 #else // !HOOK_LOG_TO_FILE\r
123 #define WriteToLog(data)\r
124 #endif // !HOOK_LOG_TO_FILE\r
125 \r
126 bool initialize(bool i_isYamy)\r
127 {\r
128 #ifndef NDEBUG\r
129         _TCHAR path[GANA_MAX_PATH];\r
130         GetModuleFileName(NULL, path, GANA_MAX_PATH);\r
131         _tsplitpath_s(path, NULL, 0, NULL, 0, g.m_moduleName, GANA_MAX_PATH, NULL, 0);\r
132         if (_tcsnicmp(g.m_moduleName, _T("Dbgview"), sizeof(_T("Dbgview"))/sizeof(_TCHAR)) != 0 &&\r
133                         _tcsnicmp(g.m_moduleName, _T("windbg"), sizeof(_T("windbg"))/sizeof(_TCHAR)) != 0) {\r
134                 g.m_isLogging = true;\r
135         }\r
136 #endif // !NDEBUG\r
137 #ifdef HOOK_LOG_TO_FILE\r
138         _TCHAR logFileName[GANA_MAX_PATH];\r
139         GetEnvironmentVariable(_T("USERPROFILE"), logFileName, NUMBER_OF(logFileName));\r
140         _tcsncat(logFileName, _T("\\AppData\\LocalLow\\yamydll.txt"), _tcslen(_T("\\AppData\\LocalLow\\yamydll.log")));\r
141         g.m_logFile = CreateFile(logFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,\r
142                 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);\r
143 #endif // HOOK_LOG_TO_FILE\r
144         WriteToLog("try to open mailslot\r\n");\r
145         g.m_hMailslot =\r
146                 CreateFile(NOTIFY_MAILSLOT_NAME, GENERIC_WRITE,\r
147                                    FILE_SHARE_READ | FILE_SHARE_WRITE,\r
148                                    (SECURITY_ATTRIBUTES *)NULL, OPEN_EXISTING,\r
149                                    FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);\r
150         if (g.m_hMailslot == INVALID_HANDLE_VALUE) {\r
151                 HOOK_RPT2("MAYU: %S create mailslot failed(0x%08x)\r\n", g.m_moduleName, GetLastError());\r
152                 WriteToLog("open mailslot NG\r\n");\r
153         } else {\r
154                 HOOK_RPT1("MAYU: %S create mailslot successed\r\n", g.m_moduleName);\r
155                 WriteToLog("open mailslot OK\r\n");\r
156         }\r
157         if (!mapHookData(i_isYamy))\r
158                 return false;\r
159         _tsetlocale(LC_ALL, _T(""));\r
160         g.m_WM_MAYU_MESSAGE =\r
161                 RegisterWindowMessage(addSessionId(WM_MAYU_MESSAGE_NAME).c_str());\r
162         g.m_hwndTaskTray = g_hookData->m_hwndTaskTray;\r
163         g.m_isInitialized = true;\r
164         return true;\r
165 }\r
166 \r
167 /// EntryPoint\r
168 BOOL WINAPI DllMain(HINSTANCE i_hInstDLL, DWORD i_fdwReason,\r
169                                         LPVOID /* i_lpvReserved */)\r
170 {\r
171         switch (i_fdwReason) {\r
172         case DLL_PROCESS_ATTACH: {\r
173 #ifndef NDEBUG\r
174                 g.m_isLogging = false;\r
175 #endif // !NDEBUG\r
176                 g.m_isInitialized = false;\r
177                 g.m_hInstDLL = i_hInstDLL;\r
178                 break;\r
179         }\r
180         case DLL_THREAD_ATTACH:\r
181                 break;\r
182         case DLL_PROCESS_DETACH:\r
183                 notifyThreadDetach();\r
184                 unmapHookData();\r
185                 if (g.m_hMailslot != INVALID_HANDLE_VALUE) {\r
186                         CloseHandle(g.m_hMailslot);\r
187                         g.m_hMailslot = INVALID_HANDLE_VALUE;\r
188                 }\r
189 #ifdef HOOK_LOG_TO_FILE\r
190                 if (g.m_logFile != INVALID_HANDLE_VALUE) {\r
191                         CloseHandle(g.m_logFile);\r
192                         g.m_logFile = INVALID_HANDLE_VALUE;\r
193                 }\r
194 #endif // HOOK_LOG_TO_FILE\r
195                 break;\r
196         case DLL_THREAD_DETACH:\r
197                 notifyThreadDetach();\r
198                 break;\r
199         default:\r
200                 break;\r
201         }\r
202         return TRUE;\r
203 }\r
204 \r
205 \r
206 /// map hook data\r
207 static bool mapHookData(bool i_isYamy)\r
208 {\r
209         DWORD access = FILE_MAP_READ;\r
210 \r
211         if (i_isYamy) {\r
212                 access |= FILE_MAP_WRITE;\r
213                 g.m_hHookData = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,   0, sizeof(HookData), addSessionId(HOOK_DATA_NAME).c_str());\r
214                 g.m_hHookDataArch = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,       0, sizeof(HookDataArch), addSessionId(HOOK_DATA_NAME_ARCH).c_str());\r
215         } else {\r
216                 g.m_hHookData = OpenFileMapping(access, FALSE, addSessionId(HOOK_DATA_NAME).c_str());\r
217                 g.m_hHookDataArch = OpenFileMapping(access, FALSE, addSessionId(HOOK_DATA_NAME_ARCH).c_str());\r
218         }\r
219 \r
220         if (g.m_hHookData == NULL || g.m_hHookDataArch == NULL) {\r
221                 unmapHookData();\r
222                 return false;\r
223         }\r
224 \r
225         g_hookData = (HookData *)MapViewOfFile(g.m_hHookData, access, 0, 0, sizeof(HookData));\r
226         s_hookDataArch = (HookDataArch *)MapViewOfFile(g.m_hHookDataArch, access, 0, 0, sizeof(HookDataArch));\r
227         if (g_hookData == NULL || s_hookDataArch == NULL) {\r
228                 unmapHookData();\r
229                 return false;\r
230         }\r
231 \r
232         return true;\r
233 }\r
234 \r
235 \r
236 /// unmap hook data\r
237 static void unmapHookData()\r
238 {\r
239         if (g_hookData)\r
240                 UnmapViewOfFile(g_hookData);\r
241         g_hookData = NULL;\r
242         if (g.m_hHookData)\r
243                 CloseHandle(g.m_hHookData);\r
244         g.m_hHookData = NULL;\r
245         if (s_hookDataArch)\r
246                 UnmapViewOfFile(s_hookDataArch);\r
247         s_hookDataArch = NULL;\r
248         if (g.m_hHookDataArch)\r
249                 CloseHandle(g.m_hHookDataArch);\r
250         g.m_hHookDataArch = NULL;\r
251 }\r
252 \r
253 \r
254 /// notify\r
255 DllExport bool notify(void *i_data, size_t i_dataSize)\r
256 {\r
257         COPYDATASTRUCT cd;\r
258 #ifdef MAYU64\r
259         DWORD_PTR result;\r
260 #else  // MAYU64\r
261         DWORD result;\r
262 #endif // MAYU64\r
263 \r
264         DWORD len;\r
265         if (g.m_hMailslot != INVALID_HANDLE_VALUE) {\r
266                 BOOL ret;\r
267                 ret = WriteFile(g.m_hMailslot, i_data, i_dataSize, &len, NULL);\r
268 #ifndef NDEBUG\r
269                 if (ret == 0) {\r
270                         HOOK_RPT2("MAYU: %S WriteFile to mailslot failed(0x%08x)\r\n", g.m_moduleName, GetLastError());\r
271                 } else {\r
272                         HOOK_RPT1("MAYU: %S WriteFile to mailslot successed\r\n", g.m_moduleName);\r
273                 }\r
274 #endif // !NDEBUG\r
275         } else {\r
276                 cd.dwData = reinterpret_cast<Notify *>(i_data)->m_type;\r
277                 cd.cbData = i_dataSize;\r
278                 cd.lpData = i_data;\r
279                 if (g.m_hwndTaskTray == 0 || cd.dwData == Notify::Type_threadDetach)\r
280                         return false;\r
281                 if (!SendMessageTimeout(reinterpret_cast<HWND>(g.m_hwndTaskTray),\r
282                                                                 WM_COPYDATA, NULL, reinterpret_cast<LPARAM>(&cd),\r
283                                                                 SMTO_ABORTIFHUNG | SMTO_NORMAL, 5000, &result)) {\r
284                         _RPT0(_CRT_WARN, "MAYU: SendMessageTimeout() timeouted\r\n");\r
285                         return false;\r
286                 }\r
287         }\r
288         return true;\r
289 }\r
290 \r
291 \r
292 /// get class name and title name\r
293 static void getClassNameTitleName(HWND i_hwnd, bool i_isInMenu,\r
294                                                                   tstringi *o_className,\r
295                                                                   tstring *o_titleName)\r
296 {\r
297         tstringi &className = *o_className;\r
298         tstring &titleName = *o_titleName;\r
299 \r
300         bool isTheFirstTime = true;\r
301 \r
302         if (i_isInMenu) {\r
303                 className = titleName = _T("MENU");\r
304                 isTheFirstTime = false;\r
305         }\r
306 \r
307         while (true) {\r
308                 _TCHAR buf[MAX(GANA_MAX_PATH, GANA_MAX_ATOM_LENGTH)];\r
309 \r
310                 // get class name\r
311                 if (i_hwnd)\r
312                         GetClassName(i_hwnd, buf, NUMBER_OF(buf));\r
313                 else\r
314                         GetModuleFileName(GetModuleHandle(NULL), buf, NUMBER_OF(buf));\r
315                 buf[NUMBER_OF(buf) - 1] = _T('\0');\r
316                 if (isTheFirstTime)\r
317                         className = buf;\r
318                 else\r
319                         className = tstringi(buf) + _T(":") + className;\r
320 \r
321                 // get title name\r
322                 if (i_hwnd) {\r
323                         GetWindowText(i_hwnd, buf, NUMBER_OF(buf));\r
324                         buf[NUMBER_OF(buf) - 1] = _T('\0');\r
325                         for (_TCHAR *b = buf; *b; ++ b)\r
326                                 if (_istlead(*b) && b[1])\r
327                                         b ++;\r
328                                 else if (_istcntrl(*b))\r
329                                         *b = _T('?');\r
330                 }\r
331                 if (isTheFirstTime)\r
332                         titleName = buf;\r
333                 else\r
334                         titleName = tstring(buf) + _T(":") + titleName;\r
335 \r
336                 // next loop or exit\r
337                 if (!i_hwnd)\r
338                         break;\r
339                 i_hwnd = GetParent(i_hwnd);\r
340                 isTheFirstTime = false;\r
341         }\r
342 }\r
343 \r
344 \r
345 /// update show\r
346 static void updateShow(HWND i_hwnd, NotifyShow::Show i_show)\r
347 {\r
348         bool isMDI = false;\r
349 \r
350         if (!i_hwnd)\r
351                 return;\r
352 \r
353 #ifdef MAYU64\r
354         LONG_PTR style = GetWindowLongPtr(i_hwnd, GWL_STYLE);\r
355 #else\r
356         LONG style = GetWindowLong(i_hwnd, GWL_STYLE);\r
357 #endif\r
358         if (!(style & WS_MAXIMIZEBOX) && !(style & WS_MAXIMIZEBOX))\r
359                 return; // ignore window that has neither maximize or minimize button\r
360 \r
361         if (style & WS_CHILD) {\r
362 #ifdef MAYU64\r
363                 LONG_PTR exStyle = GetWindowLongPtr(i_hwnd, GWL_EXSTYLE);\r
364 #else\r
365                 LONG exStyle = GetWindowLong(i_hwnd, GWL_EXSTYLE);\r
366 #endif\r
367                 if (exStyle & WS_EX_MDICHILD) {\r
368                         isMDI = true;\r
369                 } else\r
370                         return; // ignore non-MDI child window case\r
371         }\r
372 \r
373         notifyShow(i_show, isMDI);\r
374 }\r
375 \r
376 \r
377 /// notify WM_Targetted\r
378 static void notifyName(HWND i_hwnd, Notify::Type i_type = Notify::Type_name)\r
379 {\r
380         tstringi className;\r
381         tstring titleName;\r
382         getClassNameTitleName(i_hwnd, g.m_isInMenu, &className, &titleName);\r
383 \r
384         NotifySetFocus *nfc = new NotifySetFocus;\r
385         nfc->m_type = i_type;\r
386         nfc->m_threadId = GetCurrentThreadId();\r
387         nfc->m_hwnd = reinterpret_cast<DWORD>(i_hwnd);\r
388         tcslcpy(nfc->m_className, className.c_str(), NUMBER_OF(nfc->m_className));\r
389         tcslcpy(nfc->m_titleName, titleName.c_str(), NUMBER_OF(nfc->m_titleName));\r
390 \r
391         notify(nfc, sizeof(*nfc));\r
392         delete nfc;\r
393 }\r
394 \r
395 \r
396 /// notify WM_SETFOCUS\r
397 static void notifySetFocus(bool i_doesForce = false)\r
398 {\r
399         HWND hwnd = GetFocus();\r
400         if (i_doesForce || hwnd != g.m_hwndFocus) {\r
401                 g.m_hwndFocus = hwnd;\r
402                 notifyName(hwnd, Notify::Type_setFocus);\r
403         }\r
404 }\r
405 \r
406 \r
407 /// notify sync\r
408 static void notifySync()\r
409 {\r
410         Notify n;\r
411         n.m_type = Notify::Type_sync;\r
412         notify(&n, sizeof(n));\r
413 }\r
414 \r
415 \r
416 /// notify DLL_THREAD_DETACH\r
417 static void notifyThreadDetach()\r
418 {\r
419         NotifyThreadDetach ntd;\r
420         ntd.m_type = Notify::Type_threadDetach;\r
421         ntd.m_threadId = GetCurrentThreadId();\r
422         notify(&ntd, sizeof(ntd));\r
423 }\r
424 \r
425 \r
426 /// notify WM_COMMAND, WM_SYSCOMMAND\r
427 static void notifyCommand(\r
428         HWND i_hwnd, UINT i_message, WPARAM i_wParam, LPARAM i_lParam)\r
429 {\r
430         if (g_hookData->m_doesNotifyCommand) {\r
431 #ifdef _WIN64\r
432                 NotifyCommand64 ntc;\r
433                 ntc.m_type = Notify::Type_command64;\r
434 #else // !_WIN64\r
435                 NotifyCommand32 ntc;\r
436                 ntc.m_type = Notify::Type_command32;\r
437 #endif // !_WIN64\r
438                 ntc.m_hwnd = i_hwnd;\r
439                 ntc.m_message = i_message;\r
440                 ntc.m_wParam = i_wParam;\r
441                 ntc.m_lParam = i_lParam;\r
442                 notify(&ntc, sizeof(ntc));\r
443         }\r
444 }\r
445 \r
446 \r
447 /// notify show of current window\r
448 static void notifyShow(NotifyShow::Show i_show, bool i_isMDI)\r
449 {\r
450         NotifyShow ns;\r
451         ns.m_type = Notify::Type_show;\r
452         ns.m_show = i_show;\r
453         ns.m_isMDI = i_isMDI;\r
454         notify(&ns, sizeof(ns));\r
455 }\r
456 \r
457 \r
458 /// notify log\r
459 static void notifyLog(_TCHAR *i_msg)\r
460 {\r
461         NotifyLog nl;\r
462         nl.m_type = Notify::Type_log;\r
463         tcslcpy(nl.m_msg, i_msg, NUMBER_OF(nl.m_msg));\r
464         notify(&nl, sizeof(nl));\r
465 }\r
466 \r
467 \r
468 /// &Recenter\r
469 static void funcRecenter(HWND i_hwnd)\r
470 {\r
471         _TCHAR buf[MAX(GANA_MAX_PATH, GANA_MAX_ATOM_LENGTH)];\r
472         GetClassName(i_hwnd, buf, NUMBER_OF(buf));\r
473         bool isEdit;\r
474         if (_tcsicmp(buf, _T("Edit")) == 0)\r
475                 isEdit = true;\r
476         else if (_tcsnicmp(buf, _T("RichEdit"), 8) == 0)\r
477                 isEdit = false;\r
478         else\r
479                 return; // this function only works for Edit control\r
480 \r
481 #ifdef MAYU64\r
482         LONG_PTR style = GetWindowLongPtr(i_hwnd, GWL_STYLE);\r
483 #else\r
484         LONG style = GetWindowLong(i_hwnd, GWL_STYLE);\r
485 #endif\r
486         if (!(style & ES_MULTILINE))\r
487                 return; // this function only works for multi line Edit control\r
488 \r
489         RECT rc;\r
490         GetClientRect(i_hwnd, &rc);\r
491         POINTL p = { (rc.right + rc.left) / 2, (rc.top + rc.bottom) / 2 };\r
492         int line;\r
493         if (isEdit) {\r
494                 line = SendMessage(i_hwnd, EM_CHARFROMPOS, 0, MAKELPARAM(p.x, p.y));\r
495                 line = HIWORD(line);\r
496         } else {\r
497                 int ci = SendMessage(i_hwnd, EM_CHARFROMPOS, 0, (LPARAM)&p);\r
498                 line = SendMessage(i_hwnd, EM_EXLINEFROMCHAR, 0, ci);\r
499         }\r
500         int caretLine = SendMessage(i_hwnd, EM_LINEFROMCHAR, -1, 0);\r
501         SendMessage(i_hwnd, EM_LINESCROLL, 0, caretLine - line);\r
502 }\r
503 \r
504 \r
505 // &SetImeStatus\r
506 static void funcSetImeStatus(HWND i_hwnd, int i_status)\r
507 {\r
508         HIMC hIMC;\r
509 \r
510         hIMC = ImmGetContext(i_hwnd);\r
511         if (hIMC == INVALID_HANDLE_VALUE)\r
512                 return;\r
513 \r
514         if (i_status < 0)\r
515                 i_status = !ImmGetOpenStatus(hIMC);\r
516 \r
517         ImmSetOpenStatus(hIMC, i_status);\r
518         ImmReleaseContext(i_hwnd, hIMC);\r
519 }\r
520 \r
521 \r
522 // &SetImeString\r
523 static void funcSetImeString(HWND i_hwnd, int i_size)\r
524 {\r
525         _TCHAR *buf = new _TCHAR(i_size);\r
526         DWORD len = 0;\r
527         _TCHAR ImeDesc[GANA_MAX_ATOM_LENGTH];\r
528         UINT ImeDescLen;\r
529         DWORD error;\r
530         DWORD denom = 1;\r
531         HANDLE hPipe\r
532         = CreateFile(addSessionId(HOOK_PIPE_NAME).c_str(), GENERIC_READ,\r
533                                  FILE_SHARE_READ, (SECURITY_ATTRIBUTES *)NULL,\r
534                                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);\r
535         error = ReadFile(hPipe, buf, i_size, &len, NULL);\r
536         CloseHandle(hPipe);\r
537 \r
538         ImeDescLen = ImmGetDescription(GetKeyboardLayout(0),\r
539                                                                    ImeDesc, sizeof(ImeDesc));\r
540         if (_tcsncmp(ImeDesc, _T("SKKIME"), ImeDescLen) > 0)\r
541                 denom = sizeof(_TCHAR);\r
542 \r
543         HIMC hIMC = ImmGetContext(i_hwnd);\r
544         if (hIMC == INVALID_HANDLE_VALUE)\r
545                 return;\r
546 \r
547         int status = ImmGetOpenStatus(hIMC);\r
548         ImmSetCompositionString(hIMC, SCS_SETSTR, buf, len / denom, NULL, 0);\r
549         delete buf;\r
550         ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);\r
551         if (!status)\r
552                 ImmSetOpenStatus(hIMC, status);\r
553         ImmReleaseContext(i_hwnd, hIMC);\r
554 }\r
555 \r
556 /// notify lock state\r
557 /*DllExport*/\r
558 void notifyLockState(int i_cause)\r
559 {\r
560         NotifyLockState n;\r
561         n.m_type = Notify::Type_lockState;\r
562         n.m_isNumLockToggled = !!(GetKeyState(VK_NUMLOCK) & 1);\r
563         n.m_isCapsLockToggled = !!(GetKeyState(VK_CAPITAL) & 1);\r
564         n.m_isScrollLockToggled = !!(GetKeyState(VK_SCROLL) & 1);\r
565         n.m_isKanaLockToggled = !!(GetKeyState(VK_KANA) & 1);\r
566         n.m_isImeLockToggled = g.m_isImeLock;\r
567         n.m_isImeCompToggled = g.m_isImeCompositioning;\r
568         n.m_debugParam = i_cause;\r
569         notify(&n, sizeof(n));\r
570 }\r
571 \r
572 DllExport void notifyLockState()\r
573 {\r
574         notifyLockState(9);\r
575 }\r
576 \r
577 \r
578 /// hook of GetMessage\r
579 LRESULT CALLBACK getMessageProc(int i_nCode, WPARAM i_wParam, LPARAM i_lParam)\r
580 {\r
581         if (!g.m_isInitialized)\r
582                 initialize(false);\r
583 \r
584         if (!g_hookData)\r
585                 return 0;\r
586 \r
587         MSG &msg = (*(MSG *)i_lParam);\r
588 \r
589         if (i_wParam != PM_REMOVE)\r
590                 goto finally;\r
591 \r
592         switch (msg.message) {\r
593         case WM_COMMAND:\r
594         case WM_SYSCOMMAND:\r
595                 notifyCommand(msg.hwnd, msg.message, msg.wParam, msg.lParam);\r
596                 break;\r
597         case WM_KEYDOWN:\r
598         case WM_KEYUP:\r
599         case WM_SYSKEYDOWN:\r
600         case WM_SYSKEYUP: {\r
601                 if (HIMC hIMC = ImmGetContext(msg.hwnd)) {\r
602                         bool prev = g.m_isImeLock;\r
603                         g.m_isImeLock = !!ImmGetOpenStatus(hIMC);\r
604                         ImmReleaseContext(msg.hwnd, hIMC);\r
605                         if (prev != g.m_isImeLock) {\r
606                                 notifyLockState(1);\r
607                         }\r
608                 }\r
609                 int nVirtKey = (int)msg.wParam;\r
610                 // int repeatCount = (msg.lParam & 0xffff);\r
611                 BYTE scanCode   = (BYTE)((msg.lParam >> 16) & 0xff);\r
612                 bool isExtended = !!(msg.lParam & (1 << 24));\r
613                 // bool isAltDown  = !!(msg.lParam & (1 << 29));\r
614                 // bool isKeyup    = !!(msg.lParam & (1 << 31));\r
615 \r
616                 if (nVirtKey == VK_CAPITAL ||\r
617                                 nVirtKey == VK_NUMLOCK ||\r
618                                 nVirtKey == VK_KANA ||\r
619                                 nVirtKey == VK_SCROLL)\r
620                         notifyLockState(2);\r
621                 else if (scanCode == g_hookData->m_syncKey &&\r
622                                  isExtended == g_hookData->m_syncKeyIsExtended)\r
623                         notifySync();\r
624                 break;\r
625         }\r
626         case WM_IME_STARTCOMPOSITION:\r
627                 g.m_isImeCompositioning = true;\r
628                 notifyLockState(3);\r
629                 break;\r
630         case WM_IME_ENDCOMPOSITION:\r
631                 g.m_isImeCompositioning = false;\r
632                 notifyLockState(4);\r
633                 break;\r
634         default:\r
635                 if (i_wParam == PM_REMOVE && msg.message == g.m_WM_MAYU_MESSAGE) {\r
636                         switch (msg.wParam) {\r
637                         case MayuMessage_notifyName:\r
638                                 notifyName(msg.hwnd);\r
639                                 break;\r
640                         case MayuMessage_funcRecenter:\r
641                                 funcRecenter(msg.hwnd);\r
642                                 break;\r
643                         case MayuMessage_funcSetImeStatus:\r
644                                 funcSetImeStatus(msg.hwnd, msg.lParam);\r
645                                 break;\r
646                         case MayuMessage_funcSetImeString:\r
647                                 funcSetImeString(msg.hwnd, msg.lParam);\r
648                                 break;\r
649                         }\r
650                 }\r
651                 break;\r
652         }\r
653 finally:\r
654         return CallNextHookEx(s_hookDataArch->m_hHookGetMessage,\r
655                                                   i_nCode, i_wParam, i_lParam);\r
656 }\r
657 \r
658 \r
659 /// hook of SendMessage\r
660 LRESULT CALLBACK callWndProc(int i_nCode, WPARAM i_wParam, LPARAM i_lParam)\r
661 {\r
662         if (!g.m_isInitialized)\r
663                 initialize(false);\r
664 \r
665         if (!g_hookData)\r
666                 return 0;\r
667 \r
668         CWPSTRUCT &cwps = *(CWPSTRUCT *)i_lParam;\r
669 \r
670         if (0 <= i_nCode) {\r
671                 switch (cwps.message) {\r
672                 case WM_ACTIVATEAPP:\r
673                 case WM_NCACTIVATE:\r
674                         if (i_wParam)\r
675                                 notifySetFocus();\r
676                         break;\r
677                 case WM_SYSCOMMAND:\r
678                         switch (cwps.wParam) {\r
679                         case SC_MAXIMIZE:\r
680                         case SC_MAXIMIZE2:\r
681                                 updateShow(cwps.hwnd, NotifyShow::Show_Maximized);\r
682                                 break;\r
683                         case SC_MINIMIZE:\r
684                         case SC_MINIMIZE2:\r
685                                 updateShow(cwps.hwnd, NotifyShow::Show_Minimized);\r
686                                 break;\r
687                         case SC_RESTORE:\r
688                         case SC_RESTORE2:\r
689                                 updateShow(cwps.hwnd, NotifyShow::Show_Normal);\r
690                                 break;\r
691                         default:\r
692                                 break;\r
693                         }\r
694                         /* through below */\r
695                 case WM_COMMAND:\r
696                         notifyCommand(cwps.hwnd, cwps.message, cwps.wParam, cwps.lParam);\r
697                         break;\r
698                 case WM_SIZE:\r
699                         switch (cwps.wParam) {\r
700                         case SIZE_MAXIMIZED:\r
701                                 updateShow(cwps.hwnd, NotifyShow::Show_Maximized);\r
702                                 break;\r
703                         case SIZE_MINIMIZED:\r
704                                 updateShow(cwps.hwnd, NotifyShow::Show_Minimized);\r
705                                 break;\r
706                         case SIZE_RESTORED:\r
707                                 updateShow(cwps.hwnd, NotifyShow::Show_Normal);\r
708                                 break;\r
709                         default:\r
710                                 break;\r
711                         }\r
712                         break;\r
713                 case WM_MOUSEACTIVATE:\r
714                         notifySetFocus();\r
715                         break;\r
716                 case WM_ACTIVATE:\r
717                         if (LOWORD(cwps.wParam) != WA_INACTIVE) {\r
718                                 notifySetFocus();\r
719                                 if (HIWORD(cwps.wParam)) { // check minimized flag\r
720                                         // minimized flag on\r
721                                         notifyShow(NotifyShow::Show_Minimized, false);\r
722                                         //notifyShow(NotifyShow::Show_Normal, true);\r
723                                 }\r
724                         }\r
725                         break;\r
726                 case WM_ENTERMENULOOP:\r
727                         g.m_isInMenu = true;\r
728                         notifySetFocus(true);\r
729                         break;\r
730                 case WM_EXITMENULOOP:\r
731                         g.m_isInMenu = false;\r
732                         notifySetFocus(true);\r
733                         break;\r
734                 case WM_SETFOCUS:\r
735                         g.m_isInMenu = false;\r
736                         // for kana\r
737                         if (g_hookData->m_correctKanaLockHandling) {\r
738                                 if (HIMC hIMC = ImmGetContext(cwps.hwnd)) {\r
739                                         bool status = !!ImmGetOpenStatus(hIMC);\r
740                                         // this code set the VK_KANA state correctly.\r
741                                         ImmSetOpenStatus(hIMC, !status);\r
742                                         ImmSetOpenStatus(hIMC, status);\r
743                                         ImmReleaseContext(cwps.hwnd, hIMC);\r
744                                 }\r
745                         }\r
746                         notifySetFocus();\r
747                         notifyLockState(5);\r
748                         break;\r
749                 case WM_IME_STARTCOMPOSITION:\r
750                         g.m_isImeCompositioning = true;\r
751                         notifyLockState(6);\r
752                         break;\r
753                 case WM_IME_ENDCOMPOSITION:\r
754                         g.m_isImeCompositioning = false;\r
755                         notifyLockState(7);\r
756                         break;\r
757                 case WM_IME_NOTIFY:\r
758                         if (cwps.wParam == IMN_SETOPENSTATUS)\r
759                                 if (HIMC hIMC = ImmGetContext(cwps.hwnd)) {\r
760                                         g.m_isImeLock = !!ImmGetOpenStatus(hIMC);\r
761                                         ImmReleaseContext(cwps.hwnd, hIMC);\r
762                                         notifyLockState(8);\r
763                                 }\r
764                         break;\r
765                 }\r
766         }\r
767         return CallNextHookEx(s_hookDataArch->m_hHookCallWndProc, i_nCode,\r
768                                                   i_wParam, i_lParam);\r
769 }\r
770 \r
771 \r
772 static LRESULT CALLBACK lowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)\r
773 {\r
774         if (!g.m_isInitialized)\r
775                 initialize(false);\r
776 \r
777         if (!g_hookData || nCode < 0)\r
778                 goto through;\r
779 \r
780         if (g.m_mouseDetour && g.m_engine) {\r
781                 unsigned int result;\r
782                 result = g.m_mouseDetour(g.m_engine, wParam, lParam);\r
783                 if (result) {\r
784                         return 1;\r
785                 }\r
786         }\r
787 \r
788 through:\r
789         return CallNextHookEx(g.m_hHookMouseProc,\r
790                                                   nCode, wParam, lParam);\r
791 }\r
792 \r
793 \r
794 static LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)\r
795 {\r
796         KBDLLHOOKSTRUCT *pKbll = (KBDLLHOOKSTRUCT*)lParam;\r
797 \r
798         if (!g.m_isInitialized)\r
799                 initialize(false);\r
800 \r
801         if (!g_hookData || nCode < 0)\r
802                 goto through;\r
803 \r
804         if (g.m_keyboardDetour && g.m_engine) {\r
805                 unsigned int result;\r
806                 result = g.m_keyboardDetour(g.m_engine, wParam, lParam);\r
807                 if (result) {\r
808                         return 1;\r
809                 }\r
810         }\r
811 through:\r
812         return CallNextHookEx(g.m_hHookKeyboardProc,\r
813                                                   nCode, wParam, lParam);\r
814 }\r
815 \r
816 \r
817 /// install message hook\r
818 DllExport int installMessageHook(DWORD i_hwndTaskTray)\r
819 {\r
820         if (!g.m_isInitialized)\r
821                 initialize(true);\r
822 \r
823         if (i_hwndTaskTray) {\r
824                 g_hookData->m_hwndTaskTray = i_hwndTaskTray;\r
825         }\r
826         g.m_hwndTaskTray = g_hookData->m_hwndTaskTray;\r
827         s_hookDataArch->m_hHookGetMessage =\r
828                 SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)getMessageProc,\r
829                                                  g.m_hInstDLL, 0);\r
830         s_hookDataArch->m_hHookCallWndProc =\r
831                 SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)callWndProc, g.m_hInstDLL, 0);\r
832         return 0;\r
833 }\r
834 \r
835 \r
836 /// uninstall message hook\r
837 DllExport int uninstallMessageHook()\r
838 {\r
839         if (s_hookDataArch->m_hHookGetMessage)\r
840                 UnhookWindowsHookEx(s_hookDataArch->m_hHookGetMessage);\r
841         s_hookDataArch->m_hHookGetMessage = NULL;\r
842         if (s_hookDataArch->m_hHookCallWndProc)\r
843                 UnhookWindowsHookEx(s_hookDataArch->m_hHookCallWndProc);\r
844         s_hookDataArch->m_hHookCallWndProc = NULL;\r
845         g.m_hwndTaskTray = 0;\r
846         return 0;\r
847 }\r
848 \r
849 \r
850 /// install keyboard hook\r
851 DllExport int installKeyboardHook(INPUT_DETOUR i_keyboardDetour, Engine *i_engine, bool i_install)\r
852 {\r
853         if (i_install) {\r
854                 if (!g.m_isInitialized)\r
855                         initialize(true);\r
856 \r
857                 g.m_keyboardDetour = i_keyboardDetour;\r
858                 g.m_engine = i_engine;\r
859                 g.m_hHookKeyboardProc =\r
860                         SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)lowLevelKeyboardProc,\r
861                                                          g.m_hInstDLL, 0);\r
862         } else {\r
863                 if (g.m_hHookKeyboardProc)\r
864                         UnhookWindowsHookEx(g.m_hHookKeyboardProc);\r
865                 g.m_hHookKeyboardProc = NULL;\r
866         }\r
867         return 0;\r
868 }\r
869 \r
870 \r
871 /// install mouse hook\r
872 DllExport int installMouseHook(INPUT_DETOUR i_mouseDetour, Engine *i_engine, bool i_install)\r
873 {\r
874         if (i_install) {\r
875                 if (!g.m_isInitialized)\r
876                         initialize(true);\r
877 \r
878                 g.m_mouseDetour = i_mouseDetour;\r
879                 g.m_engine = i_engine;\r
880                 g_hookData->m_mouseHookType = MouseHookType_None;\r
881                 g.m_hHookMouseProc =\r
882                         SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)lowLevelMouseProc,\r
883                                                          g.m_hInstDLL, 0);\r
884         } else {\r
885                 if (g.m_hHookMouseProc)\r
886                         UnhookWindowsHookEx(g.m_hHookMouseProc);\r
887                 g.m_hHookMouseProc = NULL;\r
888         }\r
889         return 0;\r
890 }\r