OSDN Git Service

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