#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
#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
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
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
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
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
# 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
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
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
}\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
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
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
}\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
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
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
// 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
// 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
\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