OSDN Git Service

fix invalid file permission
[yamy/yamy.git] / mayu.cpp
1 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 // mayu.cpp
3
4
5 #define APSTUDIO_INVOKED
6
7 #include "misc.h"
8 #include "compiler_specific_func.h"
9 #include "dlginvestigate.h"
10 #include "dlglog.h"
11 #include "dlgsetting.h"
12 #include "dlgversion.h"
13 #include "engine.h"
14 #include "errormessage.h"
15 #include "focus.h"
16 #include "function.h"
17 #include "hook.h"
18 #include "mayu.h"
19 #include "mayuipc.h"
20 #include "mayurc.h"
21 #include "msgstream.h"
22 #include "multithread.h"
23 #include "registry.h"
24 #include "setting.h"
25 #include "target.h"
26 #include "windowstool.h"
27 #include "vk2tchar.h"
28 #include <process.h>
29 #include <time.h>
30 #include <commctrl.h>
31 #include <wtsapi32.h>
32
33
34 ///
35 #define ID_MENUITEM_reloadBegin _APS_NEXT_COMMAND_VALUE
36
37
38 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39 // Mayu
40
41
42 ///
43 class Mayu
44 {
45   HWND m_hwndTaskTray;                          /// tasktray window
46   HWND m_hwndLog;                               /// log dialog
47   HWND m_hwndInvestigate;                       /// investigate dialog
48   HWND m_hwndVersion;                           /// version dialog
49   
50   UINT m_WM_TaskbarRestart;                     /** window message sent when
51                                                     taskber restarts */
52   UINT m_WM_MayuIPC;                            /** IPC message sent from
53                                                     other applications */
54   NOTIFYICONDATA m_ni;                          /// taskbar icon data
55   HICON m_tasktrayIcon[2];                      /// taskbar icon
56   bool m_canUseTasktrayBaloon;                  /// 
57
58   tomsgstream m_log;                            /** log stream (output to log
59                                                     dialog's edit) */
60 #ifdef LOG_TO_FILE
61   tofstream m_logFile;
62 #endif // LOG_TO_FILE
63
64   HMENU m_hMenuTaskTray;                        /// tasktray menu
65   STARTUPINFO m_si;
66   PROCESS_INFORMATION m_pi;
67   HANDLE m_mutex;
68 #ifdef USE_MAILSLOT
69   HANDLE m_hNotifyMailslot;                     /// mailslot to receive notify
70   HANDLE m_hNotifyEvent;                        /// event on receive notify
71   OVERLAPPED m_olNotify;                        /// 
72   BYTE m_notifyBuf[NOTIFY_MESSAGE_SIZE];
73 #endif // USE_MAILSLOT
74   
75   Setting *m_setting;                           /// current setting
76   bool m_isSettingDialogOpened;                 /// is setting dialog opened ?
77   
78   Engine m_engine;                              /// engine
79
80   bool m_usingSN;                  /// using WTSRegisterSessionNotification() ?
81   time_t m_startTime;                           /// mayu started at ...
82
83   enum
84   { 
85     WM_APP_taskTrayNotify = WM_APP + 101,       ///
86     WM_APP_msgStreamNotify = WM_APP + 102,      ///
87     ID_TaskTrayIcon = 1,                        ///
88   };
89
90 private:
91 #ifdef USE_MAILSLOT
92   static VOID CALLBACK mailslotProc(DWORD i_code, DWORD i_len, LPOVERLAPPED i_ol)
93   {
94     Mayu *pThis;
95
96     pThis = reinterpret_cast<Mayu*>(CONTAINING_RECORD(i_ol, Mayu, m_olNotify));
97     pThis->mailslotHandler(i_code, i_len);
98     return;
99   }
100
101   BOOL mailslotHandler(DWORD i_code, DWORD i_len)
102   {
103     BOOL result;
104
105     if (i_len)
106     {
107       COPYDATASTRUCT cd;
108
109       cd.dwData = reinterpret_cast<Notify *>(m_notifyBuf)->m_type;
110       cd.cbData = i_len;
111       cd.lpData = m_notifyBuf;
112       notifyHandler(&cd);
113     }
114
115     memset(m_notifyBuf, 0, sizeof(m_notifyBuf));
116     result = ReadFileEx(m_hNotifyMailslot, m_notifyBuf, sizeof(m_notifyBuf),
117                         &m_olNotify, Mayu::mailslotProc);
118     return result;
119   }
120 #endif // USE_MAILSLOT
121
122   /// register class for tasktray
123   ATOM Register_tasktray()
124   {
125     WNDCLASS wc;
126     wc.style         = 0;
127     wc.lpfnWndProc   = tasktray_wndProc;
128     wc.cbClsExtra    = 0;
129     wc.cbWndExtra    = sizeof(Mayu *);
130     wc.hInstance     = g_hInst;
131     wc.hIcon         = NULL;
132     wc.hCursor       = NULL;
133     wc.hbrBackground = NULL;
134     wc.lpszMenuName  = NULL;
135     wc.lpszClassName = _T("mayuTasktray");
136     return RegisterClass(&wc);
137   }
138
139   /// notify handler
140   BOOL notifyHandler(COPYDATASTRUCT *cd)
141   {
142       switch (cd->dwData)
143       {
144         case Notify::Type_setFocus:
145         case Notify::Type_name:
146         {
147           NotifySetFocus *n = (NotifySetFocus *)cd->lpData;
148           n->m_className[NUMBER_OF(n->m_className) - 1] = _T('\0');
149           n->m_titleName[NUMBER_OF(n->m_titleName) - 1] = _T('\0');
150           
151           if (n->m_type == Notify::Type_setFocus)
152             m_engine.setFocus(reinterpret_cast<HWND>(n->m_hwnd), n->m_threadId,
153                               n->m_className, n->m_titleName, false);
154
155           {
156             Acquire a(&m_log, 1);
157             m_log << _T("HWND:\t") << std::hex
158                   << n->m_hwnd
159                   << std::dec << std::endl;
160             m_log << _T("THREADID:") << static_cast<int>(n->m_threadId)
161                   << std::endl;
162           }
163           Acquire a(&m_log, (n->m_type == Notify::Type_name) ? 0 : 1);
164           m_log << _T("CLASS:\t") << n->m_className << std::endl;
165           m_log << _T("TITLE:\t") << n->m_titleName << std::endl;
166           
167           bool isMDI = true;
168           HWND hwnd = getToplevelWindow(reinterpret_cast<HWND>(n->m_hwnd), &isMDI);
169           RECT rc;
170           if (isMDI)
171           {
172             getChildWindowRect(hwnd, &rc);
173             m_log << _T("MDI Window Position/Size: (")
174                   << rc.left << _T(", ") << rc.top << _T(") / (")
175                   << rcWidth(&rc) << _T("x") << rcHeight(&rc) << _T(")")
176                   << std::endl;
177             hwnd = getToplevelWindow(reinterpret_cast<HWND>(n->m_hwnd), NULL);
178           }
179           
180           GetWindowRect(hwnd, &rc);
181           m_log << _T("Toplevel Window Position/Size: (")
182                 << rc.left << _T(", ") << rc.top << _T(") / (")
183                 << rcWidth(&rc) << _T("x") << rcHeight(&rc) << _T(")")
184                 << std::endl;
185
186           SystemParametersInfo(SPI_GETWORKAREA, 0, (void *)&rc, FALSE);
187           m_log << _T("Desktop Window Position/Size: (")
188                 << rc.left << _T(", ") << rc.top << _T(") / (")
189                 << rcWidth(&rc) << _T("x") << rcHeight(&rc) << _T(")")
190                 << std::endl;
191           
192           m_log << std::endl;
193           break;
194         }
195         
196         case Notify::Type_lockState:
197         {
198           NotifyLockState *n = (NotifyLockState *)cd->lpData;
199           m_engine.setLockState(n->m_isNumLockToggled,
200                                 n->m_isCapsLockToggled,
201                                 n->m_isScrollLockToggled,
202                                 n->m_isKanaLockToggled,
203                                 n->m_isImeLockToggled,
204                                 n->m_isImeCompToggled);
205 #if 0
206           Acquire a(&m_log, 0);
207           if (n->m_isKanaLockToggled) {
208             m_log << _T("Notify::Type_lockState Kana on  : ");
209           } else {
210             m_log << _T("Notify::Type_lockState Kana off : ");
211           }
212           m_log << n->m_debugParam << ", "
213                 << g_hookData->m_correctKanaLockHandling << std::endl;
214 #endif
215           break;
216         }
217
218         case Notify::Type_sync:
219         {
220           m_engine.syncNotify();
221           break;
222         }
223
224         case Notify::Type_threadDetach:
225         {
226           NotifyThreadDetach *n = (NotifyThreadDetach *)cd->lpData;
227           m_engine.threadDetachNotify(n->m_threadId);
228           break;
229         }
230
231         case Notify::Type_command:
232         {
233           NotifyCommand *n = (NotifyCommand *)cd->lpData;
234           m_engine.commandNotify(n->m_hwnd, n->m_message,
235                                  n->m_wParam, n->m_lParam);
236           break;
237         }
238
239         case Notify::Type_show:
240         {
241           NotifyShow *n = (NotifyShow *)cd->lpData;
242           switch (n->m_show)
243           {
244             case NotifyShow::Show_Maximized:
245               m_engine.setShow(true, false, n->m_isMDI);
246               break;
247             case NotifyShow::Show_Minimized:
248               m_engine.setShow(false, true, n->m_isMDI);
249               break;
250             case NotifyShow::Show_Normal:
251             default:
252               m_engine.setShow(false, false, n->m_isMDI);
253               break;
254           }       
255           break;
256         }
257
258         case Notify::Type_log:
259         {
260           Acquire a(&m_log, 1);
261           NotifyLog *n = (NotifyLog *)cd->lpData;
262           m_log << _T("hook log: ") << n->m_msg << std::endl;
263           break;
264         }
265       }
266       return true;
267   }
268
269   /// window procedure for tasktray
270   static LRESULT CALLBACK
271   tasktray_wndProc(HWND i_hwnd, UINT i_message,
272                    WPARAM i_wParam, LPARAM i_lParam)
273   {
274 #ifdef MAYU64
275     Mayu *This = reinterpret_cast<Mayu *>(GetWindowLongPtr(i_hwnd, 0));
276 #else
277     Mayu *This = reinterpret_cast<Mayu *>(GetWindowLong(i_hwnd, 0));
278 #endif
279
280     if (!This)
281       switch (i_message)
282       {
283         case WM_CREATE:
284           This = reinterpret_cast<Mayu *>(
285             reinterpret_cast<CREATESTRUCT *>(i_lParam)->lpCreateParams);
286 #ifdef MAYU64
287           SetWindowLongPtr(i_hwnd, 0, (LONG_PTR)This);
288 #else
289           SetWindowLong(i_hwnd, 0, (long)This);
290 #endif
291           return 0;
292       }
293     else
294       switch (i_message)
295       {
296         case WM_COPYDATA:
297         {
298           COPYDATASTRUCT *cd;
299           cd = reinterpret_cast<COPYDATASTRUCT *>(i_lParam);
300           return This->notifyHandler(cd);
301         }
302         case WM_QUERYENDSESSION:
303           This->m_engine.prepairQuit();
304           PostQuitMessage(0);
305           return TRUE;
306
307 #ifndef WM_WTSSESSION_CHANGE                    // WinUser.h
308 #  define WM_WTSSESSION_CHANGE            0x02B1
309 #endif
310         case WM_WTSSESSION_CHANGE:
311         {
312           const char *m = "";
313           switch (i_wParam)
314           {
315 #ifndef WTS_CONSOLE_CONNECT                     // WinUser.h
316 #  define WTS_CONSOLE_CONNECT                0x1
317 #  define WTS_CONSOLE_DISCONNECT             0x2
318 #  define WTS_REMOTE_CONNECT                 0x3
319 #  define WTS_REMOTE_DISCONNECT              0x4
320 #  define WTS_SESSION_LOGON                  0x5
321 #  define WTS_SESSION_LOGOFF                 0x6
322 #  define WTS_SESSION_LOCK                   0x7
323 #  define WTS_SESSION_UNLOCK                 0x8
324 #endif
325             case WTS_CONSOLE_CONNECT:
326               m = "WTS_CONSOLE_CONNECT";
327               if (!This->m_engine.resume()) {
328                 This->m_engine.prepairQuit();
329                 PostQuitMessage(0);
330               }
331               break;
332             case WTS_CONSOLE_DISCONNECT:
333               m = "WTS_CONSOLE_DISCONNECT";
334               This->m_engine.pause();
335               break;
336             case WTS_REMOTE_CONNECT: m = "WTS_REMOTE_CONNECT"; break;
337             case WTS_REMOTE_DISCONNECT: m = "WTS_REMOTE_DISCONNECT"; break;
338             case WTS_SESSION_LOGON:  m = "WTS_SESSION_LOGON"; break;
339             case WTS_SESSION_LOGOFF: m = "WTS_SESSION_LOGOFF"; break;
340             case WTS_SESSION_LOCK: m = "WTS_SESSION_LOCK"; break;
341             case WTS_SESSION_UNLOCK: m = "WTS_SESSION_UNLOCK"; break;
342               //case WTS_SESSION_REMOTE_CONTROL: m = "WTS_SESSION_REMOTE_CONTROL"; break;
343           }
344           This->m_log << _T("WM_WTSESSION_CHANGE(")
345                       << i_wParam << ", " << i_lParam << "): "
346                       << m << std::endl;
347           return TRUE;
348         }
349         case WM_APP_msgStreamNotify:
350         {
351           tomsgstream::StreamBuf *log =
352             reinterpret_cast<tomsgstream::StreamBuf *>(i_lParam);
353           const tstring &str = log->acquireString();
354 #ifdef LOG_TO_FILE
355           This->m_logFile << str << std::flush;
356 #endif // LOG_TO_FILE
357           editInsertTextAtLast(GetDlgItem(This->m_hwndLog, IDC_EDIT_log),
358                                str, 65000);
359           log->releaseString();
360           return 0;
361         }
362         
363         case WM_APP_taskTrayNotify:
364         {
365           if (i_wParam == ID_TaskTrayIcon)
366             switch (i_lParam)
367             {
368               case WM_RBUTTONUP:
369               {
370                 POINT p;
371                 CHECK_TRUE( GetCursorPos(&p) );
372                 SetForegroundWindow(i_hwnd);
373                 HMENU hMenuSub = GetSubMenu(This->m_hMenuTaskTray, 0);
374                 if (This->m_engine.getIsEnabled())
375                   CheckMenuItem(hMenuSub, ID_MENUITEM_disable,
376                                 MF_UNCHECKED | MF_BYCOMMAND);
377                 else
378                   CheckMenuItem(hMenuSub, ID_MENUITEM_disable,
379                                 MF_CHECKED | MF_BYCOMMAND);
380                 CHECK_TRUE( SetMenuDefaultItem(hMenuSub,
381                                           ID_MENUITEM_investigate, FALSE) );
382
383                 // create reload menu
384                 HMENU hMenuSubSub = GetSubMenu(hMenuSub, 1);
385                 Registry reg(MAYU_REGISTRY_ROOT);
386                 int mayuIndex;
387                 reg.read(_T(".mayuIndex"), &mayuIndex, 0);
388                 while (DeleteMenu(hMenuSubSub, 0, MF_BYPOSITION))
389                   ;
390                 tregex getName(_T("^([^;]*);"));
391                 for (int index = 0; ; index ++)
392                 {
393                   _TCHAR buf[100];
394                   _sntprintf(buf, NUMBER_OF(buf), _T(".mayu%d"), index);
395                   tstringi dot_mayu;
396                   if (!reg.read(buf, &dot_mayu))
397                     break;
398                   tsmatch what;
399                   if (boost::regex_search(dot_mayu, what, getName))
400                   {
401                     MENUITEMINFO mii;
402                     std::memset(&mii, 0, sizeof(mii));
403                     mii.cbSize = sizeof(mii);
404                     mii.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
405                     mii.fType = MFT_STRING;
406                     mii.fState =
407                       MFS_ENABLED | ((mayuIndex == index) ? MFS_CHECKED : 0);
408                     mii.wID = ID_MENUITEM_reloadBegin + index;
409                     tstringi name(what.str(1));
410                     mii.dwTypeData = const_cast<_TCHAR *>(name.c_str());
411                     mii.cch = name.size();
412                     
413                     InsertMenuItem(hMenuSubSub, index, TRUE, &mii);
414                   }
415                 }
416
417                 // show popup menu
418                 TrackPopupMenu(hMenuSub, TPM_LEFTALIGN,
419                                p.x, p.y, 0, i_hwnd, NULL);
420                 // TrackPopupMenu may fail (ERROR_POPUP_ALREADY_ACTIVE)
421                 break;
422               }
423               
424               case WM_LBUTTONDBLCLK:
425                 SendMessage(i_hwnd, WM_COMMAND,
426                             MAKELONG(ID_MENUITEM_investigate, 0), 0);
427                 break;
428             }
429           return 0;
430         }
431       
432         case WM_COMMAND:
433         {
434           int notify_code = HIWORD(i_wParam);
435           int id = LOWORD(i_wParam);
436           if (notify_code == 0) // menu
437             switch (id)
438             {
439               default:
440                 if (ID_MENUITEM_reloadBegin <= id)
441                 {
442                   Registry reg(MAYU_REGISTRY_ROOT);
443                   reg.write(_T(".mayuIndex"), id - ID_MENUITEM_reloadBegin);
444                   This->load();
445                 }
446                 break;
447               case ID_MENUITEM_reload:
448                 This->load();
449                 break;
450               case ID_MENUITEM_investigate:
451               {
452                 ShowWindow(This->m_hwndLog, SW_SHOW);
453                 ShowWindow(This->m_hwndInvestigate, SW_SHOW);
454                 
455                 RECT rc1, rc2;
456                 GetWindowRect(This->m_hwndInvestigate, &rc1);
457                 GetWindowRect(This->m_hwndLog, &rc2);
458
459                 MoveWindow(This->m_hwndLog, rc1.left, rc1.bottom,
460                            rcWidth(&rc1), rcHeight(&rc2), TRUE);
461                 
462                 SetForegroundWindow(This->m_hwndLog);
463                 SetForegroundWindow(This->m_hwndInvestigate);
464                 break;
465               }
466               case ID_MENUITEM_setting:
467                 if (!This->m_isSettingDialogOpened)
468                 {
469                   This->m_isSettingDialogOpened = true;
470                   if (DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DIALOG_setting),
471                                 NULL, dlgSetting_dlgProc))
472                     This->load();
473                   This->m_isSettingDialogOpened = false;
474                 }
475                 break;
476               case ID_MENUITEM_log:
477                 ShowWindow(This->m_hwndLog, SW_SHOW);
478                 SetForegroundWindow(This->m_hwndLog);
479                 break;
480               case ID_MENUITEM_check:
481               {
482                 BOOL ret;
483                 BYTE keys[256];
484                 ret = GetKeyboardState(keys);
485                 if (ret == 0)
486                 {
487                   This->m_log << _T("Check Keystate Failed(%d)")
488                               << GetLastError() << std::endl;
489                 }
490                 else
491                 {
492                   This->m_log << _T("Check Keystate: ") << std::endl;
493                   for (int i = 0; i < 0xff; i++)
494                   {
495                     USHORT asyncKey;
496                     asyncKey = GetAsyncKeyState(i);
497                     This->m_log << std::hex;
498                     if (asyncKey & 0x8000)
499                     {
500                       This->m_log << _T("  ") << VK2TCHAR[i]
501                                   << _T("(0x") << i << _T("): pressed!")
502                                   << std::endl;
503                     }
504                     if (i == 0x14 || // VK_CAPTITAL
505                         i == 0x15 || // VK_KANA
506                         i == 0x19 || // VK_KANJI
507                         i == 0x90 || // VK_NUMLOCK
508                         i == 0x91    // VK_SCROLL
509                         )
510                     {
511                       if (keys[i] & 1)
512                       {
513                         This->m_log << _T("  ") << VK2TCHAR[i]
514                                     << _T("(0x") << i << _T("): locked!")
515                                     << std::endl;
516                       }
517                     }
518                     This->m_log << std::dec;
519                   }
520                   This->m_log << std::endl;
521                 }
522                 break;
523               }
524               case ID_MENUITEM_version:
525                 ShowWindow(This->m_hwndVersion, SW_SHOW);
526                 SetForegroundWindow(This->m_hwndVersion);
527                 break;
528               case ID_MENUITEM_help:
529               {
530                 _TCHAR buf[GANA_MAX_PATH];
531                 CHECK_TRUE( GetModuleFileName(g_hInst, buf, NUMBER_OF(buf)) );
532                 tstringi helpFilename = pathRemoveFileSpec(buf);
533                 helpFilename += _T("\\");
534                 helpFilename += loadString(IDS_helpFilename);
535                 ShellExecute(NULL, _T("open"), helpFilename.c_str(),
536                              NULL, NULL, SW_SHOWNORMAL);
537                 break;
538               }
539               case ID_MENUITEM_disable:
540                 This->m_engine.enable(!This->m_engine.getIsEnabled());
541                 This->showTasktrayIcon();
542                 break;
543               case ID_MENUITEM_quit:
544                 This->m_engine.prepairQuit();
545                 PostQuitMessage(0);
546                 break;
547             }
548           return 0;
549         }
550
551         case WM_APP_engineNotify:
552         {
553           switch (i_wParam)
554           {
555             case EngineNotify_shellExecute:
556               This->m_engine.shellExecute();
557               break;
558             case EngineNotify_loadSetting:
559               This->load();
560               break;
561             case EngineNotify_helpMessage:
562               This->showHelpMessage(false);
563               if (i_lParam)
564                 This->showHelpMessage(true);
565               break;
566             case EngineNotify_showDlg:
567             {
568               // show investigate/log window
569               int sw = (i_lParam & ~MayuDialogType_mask);
570               HWND hwnd = NULL;
571               switch (static_cast<MayuDialogType>(
572                 i_lParam & MayuDialogType_mask))
573               {
574                 case MayuDialogType_investigate:
575                   hwnd = This->m_hwndInvestigate;
576                   break;
577                 case MayuDialogType_log:
578                   hwnd = This->m_hwndLog;
579                   break;
580               }
581               if (hwnd)
582               {
583                 ShowWindow(hwnd, sw);
584                 switch (sw)
585                 {
586                   case SW_SHOWNORMAL:
587                   case SW_SHOWMAXIMIZED:
588                   case SW_SHOW:
589                   case SW_RESTORE:
590                   case SW_SHOWDEFAULT:
591                     SetForegroundWindow(hwnd);
592                     break;
593                 }
594               }
595               break;
596             }
597             case EngineNotify_setForegroundWindow:
598               // FIXME: completely useless. why ?
599               setForegroundWindow(reinterpret_cast<HWND>(i_lParam));
600               {
601                 Acquire a(&This->m_log, 1);
602                 This->m_log << _T("setForegroundWindow(0x")
603                             << std::hex << i_lParam << std::dec << _T(")")
604                             << std::endl;
605               }
606               break;
607             case EngineNotify_clearLog:
608               SendMessage(This->m_hwndLog, WM_COMMAND,
609                           MAKELONG(IDC_BUTTON_clearLog, 0), 0);
610               break;
611             default:
612               break;
613           }
614           return 0;
615         }
616         
617         case WM_APP_dlglogNotify:
618         {
619           switch (i_wParam)
620           {
621             case DlgLogNotify_logCleared:
622               This->showBanner(true);
623               break;
624             default:
625               break;
626           }
627           return 0;
628         }
629
630         case WM_DESTROY:
631           if (This->m_usingSN)
632           {
633             wtsUnRegisterSessionNotification(i_hwnd);
634             This->m_usingSN = false;
635           }
636           return 0;
637         
638         default:
639           if (i_message == This->m_WM_TaskbarRestart)
640           {
641             if (This->showTasktrayIcon(true))
642             {
643               Acquire a(&This->m_log, 0);
644               This->m_log << _T("Tasktray icon is updated.") << std::endl;
645             }
646             else
647             {
648               Acquire a(&This->m_log, 1);
649               This->m_log << _T("Tasktray icon already exists.") << std::endl;
650             }
651             return 0;
652           }
653           else if (i_message == This->m_WM_MayuIPC)
654           {
655             switch (static_cast<MayuIPCCommand>(i_wParam))
656             {
657               case MayuIPCCommand_Enable:
658                 This->m_engine.enable(!!i_lParam);
659                 This->showTasktrayIcon();
660                 if (i_lParam)
661                 {
662                   Acquire a(&This->m_log, 1);
663                   This->m_log << _T("Enabled by another application.")
664                               << std::endl;
665                 }
666                 else
667                 {
668                   Acquire a(&This->m_log, 1);
669                   This->m_log << _T("Disabled by another application.")
670                               << std::endl;
671                 }
672                 break;
673             }
674           }
675       }
676     return DefWindowProc(i_hwnd, i_message, i_wParam, i_lParam);
677   }
678
679   /// load setting
680   void load()
681   {
682     Setting *newSetting = new Setting;
683
684     // set symbol
685     for (int i = 1; i < __argc; ++ i)
686     {
687       if (__targv[i][0] == _T('-') && __targv[i][1] == _T('D'))
688         newSetting->m_symbols.insert(__targv[i] + 2);
689     }
690
691     if (!SettingLoader(&m_log, &m_log).load(newSetting))
692     {
693       ShowWindow(m_hwndLog, SW_SHOW);
694       SetForegroundWindow(m_hwndLog);
695       delete newSetting;
696       Acquire a(&m_log, 0);
697       m_log << _T("error: failed to load.") << std::endl;
698       return;
699     }
700     m_log << _T("successfully loaded.") << std::endl;
701     while (!m_engine.setSetting(newSetting))
702       Sleep(1000);
703     delete m_setting;
704     m_setting = newSetting;
705   }
706
707   // show message (a baloon from the task tray icon)
708   void showHelpMessage(bool i_doesShow = true)
709   {
710     if (m_canUseTasktrayBaloon)
711     {
712       if (i_doesShow)
713       {
714         tstring helpMessage, helpTitle;
715         m_engine.getHelpMessages(&helpMessage, &helpTitle);
716         tcslcpy(m_ni.szInfo, helpMessage.c_str(), NUMBER_OF(m_ni.szInfo));
717         tcslcpy(m_ni.szInfoTitle, helpTitle.c_str(),
718                 NUMBER_OF(m_ni.szInfoTitle));
719         m_ni.dwInfoFlags = NIIF_INFO;
720       }
721       else
722         m_ni.szInfo[0] = m_ni.szInfoTitle[0] = _T('\0');
723       CHECK_TRUE( Shell_NotifyIcon(NIM_MODIFY, &m_ni) );
724     }
725   }
726   
727   // change the task tray icon
728   bool showTasktrayIcon(bool i_doesAdd = false)
729   {
730     m_ni.hIcon  = m_tasktrayIcon[m_engine.getIsEnabled() ? 1 : 0];
731     m_ni.szInfo[0] = m_ni.szInfoTitle[0] = _T('\0');
732     if (i_doesAdd) {
733       // http://support.microsoft.com/kb/418138/JA/
734       int guard = 60;
735       for (; !Shell_NotifyIcon(NIM_ADD, &m_ni) && 0 < guard; -- guard) {
736         if (Shell_NotifyIcon(NIM_MODIFY, &m_ni)) {
737           return true;
738         }
739         Sleep(1000);                            // 1sec
740       }
741       return 0 < guard;
742     } else {
743       return !!Shell_NotifyIcon(NIM_MODIFY, &m_ni);
744     }
745   }
746
747   void showBanner(bool i_isCleared)
748   {
749     time_t now;
750     time(&now);
751       
752     _TCHAR starttimebuf[1024];
753     _TCHAR timebuf[1024];
754
755 #ifdef __BORLANDC__
756 #pragma message("\t\t****\tAfter std::ostream() is called,  ")
757 #pragma message("\t\t****\tstrftime(... \"%%#c\" ...) fails.")
758 #pragma message("\t\t****\tWhy ? Bug of Borland C++ 5.5.1 ? ")
759 #pragma message("\t\t****\t                         - nayuta")
760     _tcsftime(timebuf, NUMBER_OF(timebuf), _T("%Y/%m/%d %H:%M:%S"),
761               localtime(&now));
762     _tcsftime(starttimebuf, NUMBER_OF(starttimebuf), _T("%Y/%m/%d %H:%M:%S"),
763               localtime(&m_startTime));
764 #else
765     _tcsftime(timebuf, NUMBER_OF(timebuf), _T("%#c"), localtime(&now));
766     _tcsftime(starttimebuf, NUMBER_OF(starttimebuf), _T("%#c"),
767               localtime(&m_startTime));
768 #endif
769       
770     Acquire a(&m_log, 0);
771     m_log << _T("------------------------------------------------------------") << std::endl;
772     m_log << loadString(IDS_mayu) << _T(" ") _T(VERSION);
773 #ifndef NDEBUG
774     m_log << _T(" (DEBUG)");
775 #endif
776 #ifdef _UNICODE
777     m_log << _T(" (UNICODE)");
778 #endif
779     m_log << std::endl;
780     m_log << _T("  built by ")
781           << _T(LOGNAME) << _T("@") << toLower(_T(COMPUTERNAME))
782           << _T(" (") << _T(__DATE__) <<  _T(" ")
783           << _T(__TIME__) << _T(", ")
784           << getCompilerVersionString() << _T(")") << std::endl;
785     _TCHAR modulebuf[1024];
786     CHECK_TRUE( GetModuleFileName(g_hInst, modulebuf,
787                                   NUMBER_OF(modulebuf)) );
788     m_log << _T("started at ") << starttimebuf << std::endl;
789     m_log << modulebuf << std::endl;
790     m_log << _T("------------------------------------------------------------") << std::endl;
791
792     if (i_isCleared) {
793       m_log << _T("log was cleared at ") << timebuf << std::endl;
794     } else {
795       m_log << _T("log begins at ") << timebuf << std::endl;
796     }
797   }
798
799 public:
800   ///
801   Mayu(HANDLE i_mutex)
802     : m_hwndTaskTray(NULL),
803       m_mutex(i_mutex),
804       m_hwndLog(NULL),
805       m_WM_TaskbarRestart(RegisterWindowMessage(_T("TaskbarCreated"))),
806       m_WM_MayuIPC(RegisterWindowMessage(WM_MayuIPC_NAME)),
807       m_canUseTasktrayBaloon(
808         PACKVERSION(5, 0) <= getDllVersion(_T("shlwapi.dll"))),
809       m_log(WM_APP_msgStreamNotify),
810       m_setting(NULL),
811       m_isSettingDialogOpened(false),
812       m_engine(m_log)
813   {
814 #ifdef USE_MAILSLOT
815         m_hNotifyMailslot = CreateMailslot(NOTIFY_MAILSLOT_NAME, 0, MAILSLOT_WAIT_FOREVER, (SECURITY_ATTRIBUTES *)NULL);
816         ASSERT(m_hNotifyMailslot != INVALID_HANDLE_VALUE);
817         m_hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
818         ASSERT(m_hNotifyEvent);
819         m_olNotify.Offset = 0;
820         m_olNotify.OffsetHigh = 0;
821         m_olNotify.hEvent = m_hNotifyEvent;
822 #endif // USE_MAILSLOT
823     time(&m_startTime);
824
825     CHECK_TRUE( Register_focus() );
826     CHECK_TRUE( Register_target() );
827     CHECK_TRUE( Register_tasktray() );
828
829     // change dir
830 #if 0
831     HomeDirectories pathes;
832     getHomeDirectories(&pathes);
833     for (HomeDirectories::iterator i = pathes.begin(); i != pathes.end(); ++ i)
834       if (SetCurrentDirectory(i->c_str()))
835         break;
836 #endif
837     
838     // create windows, dialogs
839     tstringi title = loadString(IDS_mayu);
840     m_hwndTaskTray = CreateWindow(_T("mayuTasktray"), title.c_str(),
841                                   WS_OVERLAPPEDWINDOW,
842                                   CW_USEDEFAULT, CW_USEDEFAULT, 
843                                   CW_USEDEFAULT, CW_USEDEFAULT, 
844                                   NULL, NULL, g_hInst, this);
845     CHECK_TRUE( m_hwndTaskTray );
846     
847     // set window handle of tasktray to hooks
848 #ifndef USE_MAILSLOT
849     g_hookData->m_hwndTaskTray = reinterpret_cast<DWORD>(m_hwndTaskTray);
850 #endif // !USE_MAILSLOT
851     CHECK_FALSE( installHooks(Engine::keyboardDetour, &m_engine) );
852     m_usingSN = wtsRegisterSessionNotification(m_hwndTaskTray,
853                                                NOTIFY_FOR_THIS_SESSION);
854
855     DlgLogData dld;
856     dld.m_log = &m_log;
857     dld.m_hwndTaskTray = m_hwndTaskTray;
858     m_hwndLog =
859       CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_DIALOG_log), NULL,
860                         dlgLog_dlgProc, (LPARAM)&dld);
861     CHECK_TRUE( m_hwndLog );
862
863     DlgInvestigateData did;
864     did.m_engine = &m_engine;
865     did.m_hwndLog = m_hwndLog;
866     m_hwndInvestigate =
867       CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_DIALOG_investigate), NULL,
868                         dlgInvestigate_dlgProc, (LPARAM)&did);
869     CHECK_TRUE( m_hwndInvestigate );
870
871     m_hwndVersion =
872       CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_DIALOG_version),
873                         NULL, dlgVersion_dlgProc,
874                         (LPARAM)m_engine.getMayudVersion().c_str());
875     CHECK_TRUE( m_hwndVersion );
876
877     // attach log
878 #ifdef LOG_TO_FILE
879     tstring path;
880     _TCHAR exePath[GANA_MAX_PATH];
881     _TCHAR exeDrive[GANA_MAX_PATH];
882     _TCHAR exeDir[GANA_MAX_PATH];
883     GetModuleFileName(NULL, exePath, GANA_MAX_PATH);
884     _tsplitpath_s(exePath, exeDrive, GANA_MAX_PATH, exeDir, GANA_MAX_PATH, NULL, 0, NULL, 0);
885     path = exeDrive;
886     path += exeDir;
887     path += _T("mayu.log");
888     m_logFile.open(path.c_str(), std::ios::app);
889     m_logFile.imbue(std::locale("japanese"));
890 #endif // LOG_TO_FILE
891     SendMessage(GetDlgItem(m_hwndLog, IDC_EDIT_log), EM_SETLIMITTEXT, 0, 0);
892     m_log.attach(m_hwndTaskTray);
893
894     // start keyboard handler thread
895     m_engine.setAssociatedWndow(m_hwndTaskTray);
896     m_engine.start();
897     
898     // show tasktray icon
899     m_tasktrayIcon[0] = loadSmallIcon(IDI_ICON_mayu_disabled);
900     m_tasktrayIcon[1] = loadSmallIcon(IDI_ICON_mayu);
901     std::memset(&m_ni, 0, sizeof(m_ni));
902     m_ni.uID    = ID_TaskTrayIcon;
903     m_ni.hWnd   = m_hwndTaskTray;
904     m_ni.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
905     m_ni.hIcon  = m_tasktrayIcon[1];
906     m_ni.uCallbackMessage = WM_APP_taskTrayNotify;
907     tstring tip = loadString(IDS_mayu);
908     tcslcpy(m_ni.szTip, tip.c_str(), NUMBER_OF(m_ni.szTip));
909     if (m_canUseTasktrayBaloon)
910     {
911       m_ni.cbSize = sizeof(m_ni);
912       m_ni.uFlags |= NIF_INFO;
913     }
914     else
915       m_ni.cbSize = NOTIFYICONDATA_V1_SIZE;
916     showTasktrayIcon(true);
917
918     // create menu
919     m_hMenuTaskTray = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENU_tasktray));
920     ASSERT(m_hMenuTaskTray);
921     
922     // set initial lock state
923     notifyLockState();
924
925     BOOL result;
926
927     ZeroMemory(&m_pi,sizeof(m_pi));
928     ZeroMemory(&m_si,sizeof(m_si));
929     m_si.cb=sizeof(m_si);
930 #ifdef _WIN64
931     result = CreateProcess(_T("yamyd"), _T("yamyd"), NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, 0, NULL, &m_si, &m_pi);
932 #endif // _WIN64
933   }
934
935   ///
936   ~Mayu()
937   {
938     ReleaseMutex(m_mutex);
939     WaitForSingleObject(m_mutex, INFINITE);
940     // first, detach log from edit control to avoid deadlock
941     m_log.detach();
942 #ifdef LOG_TO_FILE
943     m_logFile.close();
944 #endif // LOG_TO_FILE
945    
946     // stop notify from mayu.dll
947     g_hookData->m_hwndTaskTray = NULL;
948     CHECK_FALSE( uninstallHooks() );
949         SendMessage(HWND_BROADCAST, WM_NULL, 0, 0);
950     
951     // destroy windows
952     CHECK_TRUE( DestroyWindow(m_hwndVersion) );
953     CHECK_TRUE( DestroyWindow(m_hwndInvestigate) );
954     CHECK_TRUE( DestroyWindow(m_hwndLog) );
955     CHECK_TRUE( DestroyWindow(m_hwndTaskTray) );
956     
957     // destroy menu
958     DestroyMenu(m_hMenuTaskTray);
959     
960     // delete tasktray icon
961     CHECK_TRUE( Shell_NotifyIcon(NIM_DELETE, &m_ni) );
962     CHECK_TRUE( DestroyIcon(m_tasktrayIcon[1]) );
963     CHECK_TRUE( DestroyIcon(m_tasktrayIcon[0]) );
964
965     // stop keyboard handler thread
966     m_engine.stop();
967     
968     // remove setting;
969     delete m_setting;
970     
971 #ifdef USE_MAILSLOT
972     CloseHandle(m_hNotifyEvent);
973     CloseHandle(m_hNotifyMailslot);
974 #endif // USE_MAILSLOT
975   }
976
977   /// message loop
978   WPARAM messageLoop()
979   {
980     showBanner(false);
981     load();
982     
983 #ifdef USE_MAILSLOT
984     mailslotHandler(0, 0);
985     while (1)
986     {
987       HANDLE handles[] = { m_hNotifyEvent };
988       DWORD ret;
989       switch (ret = MsgWaitForMultipleObjectsEx(NUMBER_OF(handles), &handles[0],
990                                                                                                 INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE))
991       {
992         case WAIT_OBJECT_0:                     // m_hNotifyEvent
993           break;
994           
995         case WAIT_OBJECT_0 + NUMBER_OF(handles):
996         {
997           MSG msg;
998           if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0)
999           {
1000             if (msg.message == WM_QUIT)
1001             {
1002               return msg.wParam;
1003             }
1004             if (IsDialogMessage(m_hwndLog, &msg))
1005               break;
1006             if (IsDialogMessage(m_hwndInvestigate, &msg))
1007               break;
1008             if (IsDialogMessage(m_hwndVersion, &msg))
1009               break;
1010             TranslateMessage(&msg);
1011             DispatchMessage(&msg);
1012             break;
1013           }
1014           break;
1015         }
1016         
1017         case WAIT_IO_COMPLETION:
1018           break;
1019         
1020         case 0x102:
1021         default:
1022           break;
1023       }
1024     }
1025 #else // !USE_MAILSLOT
1026     MSG msg;
1027     while (0 < GetMessage(&msg, NULL, 0, 0))
1028     {
1029       if (IsDialogMessage(m_hwndLog, &msg))
1030         continue;
1031       if (IsDialogMessage(m_hwndInvestigate, &msg))
1032         continue;
1033       if (IsDialogMessage(m_hwndVersion, &msg))
1034         continue;
1035       TranslateMessage(&msg);
1036       DispatchMessage(&msg);
1037     }
1038     return msg.wParam;
1039 #endif // !USE_MAILSLOT
1040   }  
1041 };
1042
1043
1044 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1045 // Functions
1046
1047
1048 /// convert registry
1049 void convertRegistry()
1050 {
1051   Registry reg(MAYU_REGISTRY_ROOT);
1052   tstringi dot_mayu;
1053   bool doesAdd = false;
1054   DWORD index;
1055   if (reg.read(_T(".mayu"), &dot_mayu))
1056   {
1057     reg.write(_T(".mayu0"), _T(";") + dot_mayu + _T(";"));
1058     reg.remove(_T(".mayu"));
1059     doesAdd = true;
1060     index = 0;
1061   }
1062   else if (!reg.read(_T(".mayu0"), &dot_mayu))
1063   {
1064     reg.write(_T(".mayu0"), loadString(IDS_readFromHomeDirectory) + _T(";;"));
1065     doesAdd = true;
1066     index = 1;
1067   }
1068   if (doesAdd)
1069   {
1070     Registry commonreg(HKEY_LOCAL_MACHINE, _T("Software\\GANAware\\mayu"));
1071     tstringi dir, layout;
1072     if (commonreg.read(_T("dir"), &dir) &&
1073         commonreg.read(_T("layout"), &layout))
1074     {
1075       tstringi tmp = _T(";") + dir + _T("\\dot.mayu");
1076       if (layout == _T("109"))
1077       {
1078         reg.write(_T(".mayu1"), loadString(IDS_109Emacs) + tmp
1079                   + _T(";-DUSE109") _T(";-DUSEdefault"));
1080         reg.write(_T(".mayu2"), loadString(IDS_104on109Emacs) + tmp
1081                   + _T(";-DUSE109") _T(";-DUSEdefault") _T(";-DUSE104on109"));
1082         reg.write(_T(".mayu3"), loadString(IDS_109) + tmp
1083                   + _T(";-DUSE109"));
1084         reg.write(_T(".mayu4"), loadString(IDS_104on109) + tmp
1085                   + _T(";-DUSE109") _T(";-DUSE104on109"));
1086       }
1087       else
1088       {
1089         reg.write(_T(".mayu1"), loadString(IDS_104Emacs) + tmp
1090                   + _T(";-DUSE104") _T(";-DUSEdefault"));
1091         reg.write(_T(".mayu2"), loadString(IDS_109on104Emacs) + tmp
1092                   + _T(";-DUSE104") _T(";-DUSEdefault") _T(";-DUSE109on104"));
1093         reg.write(_T(".mayu3"), loadString(IDS_104) + tmp
1094                   + _T(";-DUSE104"));
1095         reg.write(_T(".mayu4"), loadString(IDS_109on104) + tmp
1096                   + _T(";-DUSE104") _T(";-DUSE109on104"));
1097       }
1098       reg.write(_T(".mayuIndex"), index);
1099     }
1100   }
1101 }
1102
1103
1104 /// main
1105 int WINAPI _tWinMain(HINSTANCE i_hInstance, HINSTANCE /* i_hPrevInstance */,
1106                      LPTSTR /* i_lpszCmdLine */, int /* i_nCmdShow */)
1107 {
1108   g_hInst = i_hInstance;
1109
1110   // set locale
1111   CHECK_TRUE( _tsetlocale(LC_ALL, _T("")) );
1112
1113   // common controls
1114 #if defined(_WIN95)
1115   InitCommonControls();
1116 #else
1117   INITCOMMONCONTROLSEX icc;
1118   icc.dwSize = sizeof(icc);
1119   icc.dwICC = ICC_LISTVIEW_CLASSES;
1120   CHECK_TRUE( InitCommonControlsEx(&icc) );
1121 #endif
1122
1123   // convert old registry to new registry
1124 #ifndef USE_INI
1125   convertRegistry();
1126 #endif // !USE_INI
1127   
1128   // is another mayu running ?
1129   HANDLE mutex = CreateMutex((SECURITY_ATTRIBUTES *)NULL, TRUE,
1130                              MUTEX_MAYU_EXCLUSIVE_RUNNING);
1131   if (GetLastError() == ERROR_ALREADY_EXISTS)
1132   {
1133     // another mayu already running
1134     tstring text = loadString(IDS_mayuAlreadyExists);
1135     tstring title = loadString(IDS_mayu);
1136     if (g_hookData) {
1137         UINT WM_TaskbarRestart = RegisterWindowMessage(_T("TaskbarCreated"));
1138         PostMessage(reinterpret_cast<HWND>(g_hookData->m_hwndTaskTray),
1139                     WM_TaskbarRestart, 0, 0);
1140     }
1141     MessageBox((HWND)NULL, text.c_str(), title.c_str(), MB_OK | MB_ICONSTOP);
1142     return 1;
1143   }
1144
1145   // check remote desktop
1146   DWORD sessionId;
1147   if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId) ||
1148       wtsGetActiveConsoleSessionId() != sessionId)
1149   {
1150     tstring text = loadString(IDS_executedInRemoteDesktop);
1151     tstring title = loadString(IDS_mayu);
1152     MessageBox((HWND)NULL, text.c_str(), title.c_str(), MB_OK | MB_ICONSTOP);
1153     return 1;
1154   }
1155   
1156   try
1157   {
1158     Mayu(mutex).messageLoop();
1159   }
1160   catch (ErrorMessage &i_e)
1161   {
1162     tstring title = loadString(IDS_mayu);
1163     MessageBox((HWND)NULL, i_e.getMessage().c_str(), title.c_str(),
1164                MB_OK | MB_ICONSTOP);
1165   }
1166   
1167   CHECK_TRUE( CloseHandle(mutex) );
1168   return 0;
1169 }