OSDN Git Service

invoke yamy{64,32} and yamyd32 from same directory of yamy.exe instead of current...
[yamy/yamy.git] / mayu.cpp
index 1b9fe73..f83f19e 100644 (file)
--- a/mayu.cpp
+++ b/mayu.cpp
 #include "setting.h"\r
 #include "target.h"\r
 #include "windowstool.h"\r
+#include "fixscancodemap.h"\r
 #include "vk2tchar.h"\r
 #include <process.h>\r
 #include <time.h>\r
 #include <commctrl.h>\r
 #include <wtsapi32.h>\r
+#include <aclapi.h>\r
 \r
 \r
 ///\r
@@ -62,8 +64,11 @@ class Mayu
 #endif // LOG_TO_FILE\r
 \r
        HMENU m_hMenuTaskTray;                  /// tasktray menu\r
+#ifdef _WIN64\r
+       HANDLE m_hMutexYamyd;\r
        STARTUPINFO m_si;\r
        PROCESS_INFORMATION m_pi;\r
+#endif // _WIN64\r
        HANDLE m_mutex;\r
 #ifdef USE_MAILSLOT\r
        HANDLE m_hNotifyMailslot;                       /// mailslot to receive notify\r
@@ -71,6 +76,12 @@ class Mayu
        OVERLAPPED m_olNotify;                  ///\r
        BYTE m_notifyBuf[NOTIFY_MESSAGE_SIZE];\r
 #endif // USE_MAILSLOT\r
+       static const DWORD SESSION_LOCKED = 1<<0;\r
+       static const DWORD SESSION_DISCONNECTED = 1<<1;\r
+       static const DWORD SESSION_END_QUERIED = 1<<2;\r
+       DWORD m_sessionState;\r
+       int m_escapeNlsKeys;\r
+       FixScancodeMap m_fixScancodeMap;\r
 \r
        Setting *m_setting;                             /// current setting\r
        bool m_isSettingDialogOpened;                   /// is setting dialog opened ?\r
@@ -83,16 +94,23 @@ class Mayu
        enum {\r
                WM_APP_taskTrayNotify = WM_APP + 101,   ///\r
                WM_APP_msgStreamNotify = WM_APP + 102,  ///\r
+               WM_APP_escapeNLSKeysFailed = WM_APP + 121,      ///\r
                ID_TaskTrayIcon = 1,                    ///\r
        };\r
 \r
+       enum {\r
+               YAMY_TIMER_ESCAPE_NLS_KEYS = 0, ///\r
+       };\r
+\r
 private:\r
 #ifdef USE_MAILSLOT\r
        static VOID CALLBACK mailslotProc(DWORD i_code, DWORD i_len, LPOVERLAPPED i_ol) {\r
                Mayu *pThis;\r
 \r
-               pThis = reinterpret_cast<Mayu*>(CONTAINING_RECORD(i_ol, Mayu, m_olNotify));\r
-               pThis->mailslotHandler(i_code, i_len);\r
+               if (i_code == ERROR_SUCCESS) {\r
+                       pThis = reinterpret_cast<Mayu*>(CONTAINING_RECORD(i_ol, Mayu, m_olNotify));\r
+                       pThis->mailslotHandler(i_code, i_len);\r
+               }\r
                return;\r
        }\r
 \r
@@ -265,6 +283,10 @@ private:
                        case WM_CREATE:\r
                                This = reinterpret_cast<Mayu *>(\r
                                                   reinterpret_cast<CREATESTRUCT *>(i_lParam)->lpCreateParams);\r
+                               This->m_fixScancodeMap.init(i_hwnd, WM_APP_escapeNLSKeysFailed);\r
+                               if (This->m_escapeNlsKeys) {\r
+                                       This->m_fixScancodeMap.escape(true);\r
+                               }\r
 #ifdef MAYU64\r
                                SetWindowLongPtr(i_hwnd, 0, (LONG_PTR)This);\r
 #else\r
@@ -280,6 +302,12 @@ private:
                                return This->notifyHandler(cd);\r
                        }\r
                        case WM_QUERYENDSESSION:\r
+                               if (!This->m_sessionState) {\r
+                                       if (This->m_escapeNlsKeys && This->m_engine.getIsEnabled()) {\r
+                                               This->m_fixScancodeMap.escape(false);\r
+                                       }\r
+                               }\r
+                               This->m_sessionState |= Mayu::SESSION_END_QUERIED;\r
                                This->m_engine.prepairQuit();\r
                                PostQuitMessage(0);\r
                                return TRUE;\r
@@ -300,16 +328,45 @@ private:
 #  define WTS_SESSION_LOCK                   0x7\r
 #  define WTS_SESSION_UNLOCK                 0x8\r
 #endif\r
+                               /*\r
+                                       restore NLS keys when any bits of m_sessionState is on\r
+                                       and\r
+                                       escape NLS keys when all bits of m_sessionState cleared\r
+                               */\r
                                case WTS_CONSOLE_CONNECT:\r
+                                       This->m_sessionState &= ~Mayu::SESSION_DISCONNECTED;\r
+                                       if (!This->m_sessionState) {\r
+                                               if (This->m_escapeNlsKeys && This->m_engine.getIsEnabled()) {\r
+                                                       This->m_fixScancodeMap.escape(true);\r
+                                               }\r
+                                       }\r
                                        m = "WTS_CONSOLE_CONNECT";\r
                                        break;\r
                                case WTS_CONSOLE_DISCONNECT:\r
+                                       if (!This->m_sessionState) {\r
+                                               if (This->m_escapeNlsKeys && This->m_engine.getIsEnabled()) {\r
+                                                       This->m_fixScancodeMap.escape(false);\r
+                                               }\r
+                                       }\r
+                                       This->m_sessionState |= Mayu::SESSION_DISCONNECTED;\r
                                        m = "WTS_CONSOLE_DISCONNECT";\r
                                        break;\r
                                case WTS_REMOTE_CONNECT:\r
+                                       This->m_sessionState &= ~Mayu::SESSION_DISCONNECTED;\r
+                                       if (!This->m_sessionState) {\r
+                                               if (This->m_escapeNlsKeys && This->m_engine.getIsEnabled()) {\r
+                                                       This->m_fixScancodeMap.escape(true);\r
+                                               }\r
+                                       }\r
                                        m = "WTS_REMOTE_CONNECT";\r
                                        break;\r
                                case WTS_REMOTE_DISCONNECT:\r
+                                       if (!This->m_sessionState) {\r
+                                               if (This->m_escapeNlsKeys && This->m_engine.getIsEnabled()) {\r
+                                                       This->m_fixScancodeMap.escape(false);\r
+                                               }\r
+                                       }\r
+                                       This->m_sessionState |= Mayu::SESSION_DISCONNECTED;\r
                                        m = "WTS_REMOTE_DISCONNECT";\r
                                        break;\r
                                case WTS_SESSION_LOGON:\r
@@ -318,12 +375,26 @@ private:
                                case WTS_SESSION_LOGOFF:\r
                                        m = "WTS_SESSION_LOGOFF";\r
                                        break;\r
-                               case WTS_SESSION_LOCK:\r
+                               case WTS_SESSION_LOCK: {\r
+                                       if (!This->m_sessionState) {\r
+                                               if (This->m_escapeNlsKeys && This->m_engine.getIsEnabled()) {\r
+                                                       This->m_fixScancodeMap.escape(false);\r
+                                               }\r
+                                       }\r
+                                       This->m_sessionState |= Mayu::SESSION_LOCKED;\r
                                        m = "WTS_SESSION_LOCK";\r
                                        break;\r
-                               case WTS_SESSION_UNLOCK:\r
+                          }\r
+                               case WTS_SESSION_UNLOCK: {\r
+                                       This->m_sessionState &= ~Mayu::SESSION_LOCKED;\r
+                                       if (!This->m_sessionState) {\r
+                                               if (This->m_escapeNlsKeys && This->m_engine.getIsEnabled()) {\r
+                                                       This->m_fixScancodeMap.escape(true);\r
+                                               }\r
+                                       }\r
                                        m = "WTS_SESSION_UNLOCK";\r
                                        break;\r
+                               }\r
                                        //case WTS_SESSION_REMOTE_CONTROL: m = "WTS_SESSION_REMOTE_CONTROL"; break;\r
                                }\r
                                This->m_log << _T("WM_WTSESSION_CHANGE(")\r
@@ -408,6 +479,21 @@ private:
                                return 0;\r
                        }\r
 \r
+                       case WM_APP_escapeNLSKeysFailed:\r
+                               if (i_lParam) {\r
+                                       This->m_log << _T("escape NLS keys done code=") << i_wParam << std::endl;\r
+                                       if (i_wParam != YAMY_SUCCESS && i_wParam != YAMY_ERROR_RETRY_INJECTION_SUCCESS) {\r
+                                               int ret = This->errorDialogWithCode(IDS_escapeNlsKeysFailed, i_wParam, MB_RETRYCANCEL | MB_ICONSTOP);\r
+                                               if (ret == IDRETRY) {\r
+                                                       This->m_fixScancodeMap.escape(true);\r
+                                               }\r
+                                       }\r
+                               } else {\r
+                                       This->m_log << _T("restore NLS keys done with code=") << i_wParam << std::endl;\r
+                               }\r
+                               return 0;\r
+                               break;\r
+\r
                        case WM_COMMAND: {\r
                                int notify_code = HIWORD(i_wParam);\r
                                int id = LOWORD(i_wParam);\r
@@ -503,6 +589,11 @@ private:
                                        }\r
                                        case ID_MENUITEM_disable:\r
                                                This->m_engine.enable(!This->m_engine.getIsEnabled());\r
+                                               if (This->m_engine.getIsEnabled()) {\r
+                                                       This->m_fixScancodeMap.escape(true);\r
+                                               } else {\r
+                                                       This->m_fixScancodeMap.escape(false);\r
+                                               }\r
                                                This->showTasktrayIcon();\r
                                                break;\r
                                        case ID_MENUITEM_quit:\r
@@ -589,6 +680,11 @@ private:
                                        wtsUnRegisterSessionNotification(i_hwnd);\r
                                        This->m_usingSN = false;\r
                                }\r
+                               if (!This->m_sessionState) {\r
+                                       if (This->m_escapeNlsKeys && This->m_engine.getIsEnabled()) {\r
+                                               This->m_fixScancodeMap.escape(false);\r
+                                       }\r
+                               }\r
                                return 0;\r
 \r
                        default:\r
@@ -605,6 +701,11 @@ private:
                                        switch (static_cast<MayuIPCCommand>(i_wParam)) {\r
                                        case MayuIPCCommand_Enable:\r
                                                This->m_engine.enable(!!i_lParam);\r
+                                               if (This->m_engine.getIsEnabled()) {\r
+                                                       This->m_fixScancodeMap.escape(true);\r
+                                               } else {\r
+                                                       This->m_fixScancodeMap.escape(false);\r
+                                               }\r
                                                This->showTasktrayIcon();\r
                                                if (i_lParam) {\r
                                                        Acquire a(&This->m_log, 1);\r
@@ -733,6 +834,164 @@ private:
                }\r
        }\r
 \r
+       int errorDialogWithCode(UINT ids, int code, UINT style = MB_OK | MB_ICONSTOP)\r
+       {\r
+               _TCHAR title[1024];\r
+               _TCHAR text[1024];\r
+\r
+               _sntprintf_s(title, NUMBER_OF(title), _TRUNCATE, loadString(IDS_mayu).c_str());\r
+               _sntprintf_s(text, NUMBER_OF(text), _TRUNCATE, loadString(ids).c_str(), code);\r
+               return MessageBox((HWND)NULL, text, title, style);\r
+       }\r
+\r
+       int enableToWriteByUser(HANDLE hdl)\r
+       {\r
+               TCHAR userName[GANA_MAX_ATOM_LENGTH];\r
+               DWORD userNameSize = NUMBER_OF(userName);\r
+\r
+               SID_NAME_USE sidType;\r
+               PSID pSid = NULL;\r
+               DWORD sidSize = 0;\r
+               TCHAR *pDomain = NULL;\r
+               DWORD domainSize = 0;\r
+\r
+               PSECURITY_DESCRIPTOR pSd;\r
+               PACL pOrigDacl;\r
+               ACL_SIZE_INFORMATION aclInfo;\r
+\r
+               PACL pNewDacl;\r
+               DWORD newDaclSize;\r
+\r
+               DWORD aceIndex;\r
+               DWORD newAceIndex = 0;\r
+\r
+               BOOL ret;\r
+               int err = 0;\r
+\r
+               ret = GetUserName(userName, &userNameSize);\r
+               if (ret == FALSE) {\r
+                       err = YAMY_ERROR_ON_GET_USERNAME;\r
+                       goto exit;\r
+               }\r
+\r
+               // get buffer size for pSid (and pDomain)\r
+               ret = LookupAccountName(NULL, userName, pSid, &sidSize, pDomain, &domainSize, &sidType);\r
+               if (ret != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {\r
+                       // above call should fail by ERROR_INSUFFICIENT_BUFFER\r
+                       err = YAMY_ERROR_ON_GET_LOGONUSERNAME;\r
+                       goto exit;\r
+               }\r
+\r
+               pSid = reinterpret_cast<PSID>(LocalAlloc(LPTR, sidSize));\r
+               pDomain = reinterpret_cast<TCHAR*>(LocalAlloc(LPTR, domainSize * sizeof(TCHAR)));\r
+               if (pSid == NULL || pDomain == NULL) {\r
+                       err = YAMY_ERROR_NO_MEMORY;\r
+                       goto exit;\r
+               }\r
+\r
+               // get SID (and Domain) for logoned user\r
+               ret = LookupAccountName(NULL, userName, pSid, &sidSize, pDomain, &domainSize, &sidType);\r
+               if (ret == FALSE) {\r
+                       // LookupAccountName() should success in this time\r
+                       err = YAMY_ERROR_ON_GET_LOGONUSERNAME;\r
+                       goto exit;\r
+               }\r
+\r
+               // get DACL for hdl\r
+               ret = GetSecurityInfo(hdl, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pOrigDacl, NULL, &pSd);\r
+               if (ret != ERROR_SUCCESS) {\r
+                       err = YAMY_ERROR_ON_GET_SECURITYINFO;\r
+                       goto exit;\r
+               }\r
+\r
+               // get size for original DACL\r
+               ret = GetAclInformation(pOrigDacl, &aclInfo, sizeof(aclInfo), AclSizeInformation);\r
+               if (ret == FALSE) {\r
+                       err = YAMY_ERROR_ON_GET_DACL;\r
+                       goto exit;\r
+               }\r
+\r
+               // compute size for new DACL\r
+               newDaclSize = aclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pSid) - sizeof(DWORD);\r
+\r
+               // allocate memory for new DACL\r
+               pNewDacl = reinterpret_cast<PACL>(LocalAlloc(LPTR, newDaclSize));\r
+               if (pNewDacl == NULL) {\r
+                       err = YAMY_ERROR_NO_MEMORY;\r
+                       goto exit;\r
+               }\r
+\r
+               // initialize new DACL\r
+               ret = InitializeAcl(pNewDacl, newDaclSize, ACL_REVISION);\r
+               if (ret == FALSE) {\r
+                       err = YAMY_ERROR_ON_INITIALIZE_ACL;\r
+                       goto exit;\r
+               }\r
+\r
+               // copy original DACL to new DACL\r
+               for (aceIndex = 0; aceIndex < aclInfo.AceCount; aceIndex++) {\r
+                       LPVOID pAce;\r
+\r
+                       ret = GetAce(pOrigDacl, aceIndex, &pAce);\r
+                       if (ret == FALSE) {\r
+                               err = YAMY_ERROR_ON_GET_ACE;\r
+                               goto exit;\r
+                       }\r
+\r
+                       if ((reinterpret_cast<ACCESS_ALLOWED_ACE*>(pAce))->Header.AceFlags & INHERITED_ACE) {\r
+                               break;\r
+                       }\r
+\r
+                       if (EqualSid(pSid, &(reinterpret_cast<ACCESS_ALLOWED_ACE*>(pAce))->SidStart) != FALSE) {\r
+                               continue;\r
+                       }\r
+\r
+                       ret = AddAce(pNewDacl, ACL_REVISION, MAXDWORD, pAce, (reinterpret_cast<PACE_HEADER>(pAce))->AceSize);\r
+                       if (ret == FALSE) {\r
+                               err = YAMY_ERROR_ON_ADD_ACE;\r
+                               goto exit;\r
+                       }\r
+\r
+                       newAceIndex++;\r
+               }\r
+\r
+               ret = AddAccessAllowedAce(pNewDacl, ACL_REVISION, GENERIC_ALL, pSid);\r
+               if (ret == FALSE) {\r
+                       err = YAMY_ERROR_ON_ADD_ALLOWED_ACE;\r
+                       goto exit;\r
+               }\r
+\r
+               // copy the rest of inherited ACEs\r
+               for (; aceIndex < aclInfo.AceCount; aceIndex++) {\r
+                       LPVOID pAce;\r
+\r
+                       ret = GetAce(pOrigDacl, aceIndex, &pAce);\r
+                       if (ret == FALSE) {\r
+                               err = YAMY_ERROR_ON_GET_ACE;\r
+                               goto exit;\r
+                       }\r
+\r
+                       ret = AddAce(pNewDacl, ACL_REVISION, MAXDWORD, pAce, (reinterpret_cast<PACE_HEADER>(pAce))->AceSize);\r
+                       if (ret == FALSE) {\r
+                               err = YAMY_ERROR_ON_ADD_ACE;\r
+                               goto exit;\r
+                       }\r
+               }\r
+\r
+               ret = SetSecurityInfo(hdl, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, pNewDacl, NULL);\r
+               if (ret != ERROR_SUCCESS) {\r
+                       err = YAMY_ERROR_ON_SET_SECURITYINFO;\r
+               }\r
+\r
+exit:\r
+               LocalFree(pSd);\r
+               LocalFree(pSid);\r
+               LocalFree(pDomain);\r
+               LocalFree(pNewDacl);\r
+\r
+               return err;\r
+       }\r
+\r
 public:\r
        ///\r
        Mayu(HANDLE i_mutex)\r
@@ -746,10 +1005,21 @@ public:
                        m_log(WM_APP_msgStreamNotify),\r
                        m_setting(NULL),\r
                        m_isSettingDialogOpened(false),\r
+                       m_sessionState(0),\r
                        m_engine(m_log) {\r
+               Registry reg(MAYU_REGISTRY_ROOT);\r
+               reg.read(_T("escapeNLSKeys"), &m_escapeNlsKeys, 0);\r
 #ifdef USE_MAILSLOT\r
                m_hNotifyMailslot = CreateMailslot(NOTIFY_MAILSLOT_NAME, 0, MAILSLOT_WAIT_FOREVER, (SECURITY_ATTRIBUTES *)NULL);\r
                ASSERT(m_hNotifyMailslot != INVALID_HANDLE_VALUE);\r
+               int err;\r
+               if (checkWindowsVersion(6, 0) != FALSE) { // enableToWriteByUser() is available only Vista or later\r
+                       err = enableToWriteByUser(m_hNotifyMailslot);\r
+                       if (err) {\r
+                               errorDialogWithCode(IDS_cannotPermitStandardUser, err);\r
+                       }\r
+               }\r
+\r
                m_hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
                ASSERT(m_hNotifyEvent);\r
                m_olNotify.Offset = 0;\r
@@ -843,7 +1113,7 @@ public:
                tstring tip = loadString(IDS_mayu);\r
                tcslcpy(m_ni.szTip, tip.c_str(), NUMBER_OF(m_ni.szTip));\r
                if (m_canUseTasktrayBaloon) {\r
-                       m_ni.cbSize = sizeof(m_ni);\r
+                       m_ni.cbSize = NOTIFYICONDATA_V3_SIZE;\r
                        m_ni.uFlags |= NIF_INFO;\r
                } else\r
                        m_ni.cbSize = NOTIFYICONDATA_V1_SIZE;\r
@@ -856,35 +1126,54 @@ public:
                // set initial lock state\r
                notifyLockState();\r
 \r
-               BOOL result;\r
-\r
+#ifdef _WIN64\r
                ZeroMemory(&m_pi,sizeof(m_pi));\r
                ZeroMemory(&m_si,sizeof(m_si));\r
                m_si.cb=sizeof(m_si);\r
-#ifdef _WIN64\r
-               result = CreateProcess(_T("yamyd32"), _T("yamyd32"), NULL, NULL, FALSE,\r
+\r
+               // create mutex to block yamyd\r
+               m_hMutexYamyd = CreateMutex((SECURITY_ATTRIBUTES *)NULL, TRUE, MUTEX_YAMYD_BLOCKER);\r
+\r
+               tstring yamydPath;\r
+               _TCHAR exePath[GANA_MAX_PATH];\r
+               _TCHAR exeDrive[GANA_MAX_PATH];\r
+               _TCHAR exeDir[GANA_MAX_PATH];\r
+\r
+               GetModuleFileName(NULL, exePath, GANA_MAX_PATH);\r
+               _tsplitpath_s(exePath, exeDrive, GANA_MAX_PATH, exeDir, GANA_MAX_PATH, NULL, 0, NULL, 0);\r
+               yamydPath = exeDrive;\r
+               yamydPath += exeDir;\r
+               yamydPath += _T("yamyd32");\r
+\r
+               BOOL result = CreateProcess(yamydPath.c_str(), NULL, NULL, NULL, FALSE,\r
                                                           NORMAL_PRIORITY_CLASS, 0, NULL, &m_si, &m_pi);\r
                if (result == FALSE) {\r
                        TCHAR buf[1024];\r
                        TCHAR text[1024];\r
                        TCHAR title[1024];\r
 \r
+                       m_pi.hProcess = NULL;\r
                        LoadString(GetModuleHandle(NULL), IDS_cannotInvoke,\r
                                           text, sizeof(text)/sizeof(text[0]));\r
                        LoadString(GetModuleHandle(NULL), IDS_mayu,\r
                                           title, sizeof(title)/sizeof(title[0]));\r
                        _stprintf_s(buf, sizeof(buf)/sizeof(buf[0]),\r
                                                text, _T("yamyd32"), GetLastError());\r
-                       MessageBox((HWND)NULL, buf, title, MB_OK | MB_ICONSTOP);\r
+                       MessageBox((HWND)NULL, buf, title, MB_OK | MB_ICONSTOP);\r
                } else {\r
                        CloseHandle(m_pi.hThread);\r
-                       CloseHandle(m_pi.hProcess);\r
                }\r
 #endif // _WIN64\r
        }\r
 \r
        ///\r
        ~Mayu() {\r
+#ifdef USE_MAILSLOT\r
+               CancelIo(m_hNotifyMailslot);\r
+               SleepEx(0, TRUE);\r
+               CloseHandle(m_hNotifyMailslot);\r
+               CloseHandle(m_hNotifyEvent);\r
+#endif // USE_MAILSLOT\r
                ReleaseMutex(m_mutex);\r
                WaitForSingleObject(m_mutex, INFINITE);\r
                // first, detach log from edit control to avoid deadlock\r
@@ -896,7 +1185,19 @@ public:
                // stop notify from mayu.dll\r
                g_hookData->m_hwndTaskTray = NULL;\r
                CHECK_FALSE( uninstallMessageHook() );\r
-               PostMessage(HWND_BROADCAST, WM_NULL, 0, 0);\r
+\r
+#ifdef _WIN64\r
+               ReleaseMutex(m_hMutexYamyd);\r
+               if (m_pi.hProcess) {\r
+                       WaitForSingleObject(m_pi.hProcess, 5000);\r
+                       CloseHandle(m_pi.hProcess);\r
+               }\r
+               CloseHandle(m_hMutexYamyd);\r
+#endif // _WIN64\r
+               if (!(m_sessionState & SESSION_END_QUERIED)) {\r
+                       DWORD_PTR result;\r
+                       SendMessageTimeout(HWND_BROADCAST, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, 3000, &result);\r
+               }\r
 \r
                // destroy windows\r
                CHECK_TRUE( DestroyWindow(m_hwndVersion) );\r
@@ -917,11 +1218,6 @@ public:
 \r
                // remove setting;\r
                delete m_setting;\r
-\r
-#ifdef USE_MAILSLOT\r
-               CloseHandle(m_hNotifyEvent);\r
-               CloseHandle(m_hNotifyMailslot);\r
-#endif // USE_MAILSLOT\r
        }\r
 \r
        /// message loop\r