CProcessManager.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. #include "stdafx.h"
  2. #include "CProcessManager.h"
  3. CProcessManager* SSingleton<CProcessManager>::ms_Singleton = NULL;
  4. CProcessManager::CProcessManager()
  5. {
  6. }
  7. CProcessManager::~CProcessManager()
  8. {
  9. }
  10. void CProcessManager::_UpdateClashCmd()
  11. {
  12. _clashCmd.assign(LR"(")");
  13. _clashCmd.append(_exePath.filename());
  14. _clashCmd.append(LR"(")");
  15. if (!_homeDir.empty())
  16. {
  17. _clashCmd.append(LR"( -d ")");
  18. _clashCmd.append(_homeDir);
  19. _clashCmd.append(LR"(")");
  20. }
  21. if (!_configFile.empty())
  22. {
  23. _clashCmd.append(LR"( -f ")");
  24. _clashCmd.append(_configFile);
  25. _clashCmd.append(LR"(")");
  26. }
  27. //if (!_uiDir.empty())
  28. //{
  29. // _clashCmd.append(LR"( -ext-ui ")");
  30. // _clashCmd.append(_uiDir);
  31. // _clashCmd.append(LR"(")");
  32. //}
  33. //if (!_ctlAddr.empty())
  34. //{
  35. // _clashCmd.append(LR"( -ext-ctl ")");
  36. // _clashCmd.append(_ctlAddr);
  37. // _clashCmd.append(LR"(")");
  38. //}
  39. //// Override secret even if empty
  40. //_clashCmd.append(LR"( -secret ")");
  41. //_clashCmd.append(_ctlSecret);
  42. //_clashCmd.append(LR"(")");
  43. }
  44. void CProcessManager::SetInsTanCe(HINSTANCE m) {
  45. mInstance = m;
  46. }
  47. void CProcessManager::SetArgs(std::filesystem::path exePath, std::filesystem::path homeDir, std::filesystem::path cconfigFile)
  48. {
  49. _exePath = exePath;
  50. _homeDir = homeDir;
  51. _configFile = cconfigFile;
  52. _UpdateClashCmd();
  53. }
  54. void CProcessManager::SetConfigFile(std::filesystem::path configFile)
  55. {
  56. _configFile = configFile;
  57. _UpdateClashCmd();
  58. }
  59. bool CProcessManager::Start()
  60. {
  61. if (_state != State::Stop)
  62. return false;
  63. try
  64. {
  65. _hJob.reset(CreateJobObjectW(nullptr, nullptr));
  66. THROW_LAST_ERROR_IF_NULL(_hJob);
  67. JOBOBJECT_BASIC_LIMIT_INFORMATION bli;
  68. bli.LimitFlags = JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
  69. JOBOBJECT_EXTENDED_LIMIT_INFORMATION eli;
  70. eli.BasicLimitInformation = bli;
  71. THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(_hJob.get(), JobObjectExtendedLimitInformation, &eli, sizeof(eli)));
  72. wchar_t objName[objNameSize + 1] = OBJPREFIX;
  73. GUID guid = {};
  74. THROW_IF_FAILED(CoCreateGuid(&guid));
  75. THROW_HR_IF(E_OUTOFMEMORY, StringFromGUID2(guid, objName + prefixSize, guidSize) != guidSize);
  76. size_t size = (std::max)((_exePath.native().size() + 1) + (_clashCmd.size() + 1) * sizeof(wchar_t), sizeof(ProcessInfo));
  77. objName[objNameSize - 1] = L'F';
  78. wil::unique_handle hFileMapping(CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, static_cast<DWORD>(size), objName));
  79. THROW_LAST_ERROR_IF_NULL(hFileMapping);
  80. auto error = GetLastError();
  81. if (error == ERROR_ALREADY_EXISTS) THROW_WIN32(error);
  82. wil::unique_mapview_ptr<wchar_t> buffer(reinterpret_cast<wchar_t*>(MapViewOfFile(hFileMapping.get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0)));
  83. THROW_LAST_ERROR_IF_NULL(buffer);
  84. objName[objNameSize - 1] = L'E';
  85. wil::unique_handle hEvent(CreateEventW(nullptr, FALSE, FALSE, objName));
  86. THROW_LAST_ERROR_IF_NULL(hEvent);
  87. error = GetLastError();
  88. if (error == ERROR_ALREADY_EXISTS) THROW_WIN32(error);
  89. auto exePathPtr = buffer.get();
  90. size_t i = _exePath.native().copy(exePathPtr, std::filesystem::path::string_type::npos);
  91. exePathPtr[i] = 0;
  92. auto cmdPtr = buffer.get() + i + 1;
  93. i = _clashCmd.copy(cmdPtr, std::wstring::npos);
  94. cmdPtr[i] = 0;
  95. auto selfPath = GetModuleFsPath(mInstance);
  96. auto guidStr = objName + prefixSize;
  97. std::wstring cmd(LR"(")");
  98. cmd.append(selfPath);
  99. cmd.append(LR"(" --pm=)");
  100. cmd.append(guidStr, guidSize - 1);
  101. {
  102. // Disable Windows Error Reporting for subprocess
  103. auto lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
  104. auto restoreErrorMode = wil::scope_exit([=]() { // Restore error mode after CreateProcessW
  105. SetErrorMode(lastErrorMode);
  106. });
  107. wil::unique_cotaskmem_string appId;
  108. THROW_IF_FAILED(GetCurrentProcessExplicitAppUserModelID(&appId));
  109. /*wil::unique_cotaskmem_string appId;
  110. THROW_IF_FAILED(GetCurrentProcessExplicitAppUserModelID(&appId));*/
  111. /*STARTUPINFOW si = {
  112. .cb = sizeof(si),
  113. .lpTitle = appId.get(),
  114. .dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_PREVENTPINNING | STARTF_TITLEISAPPID | STARTF_USESHOWWINDOW,
  115. .wShowWindow = SW_HIDE
  116. };*/
  117. STARTUPINFOW si;
  118. ZeroMemory(&si, sizeof(si));
  119. si.cb = sizeof(si);
  120. si.lpTitle = appId.get();
  121. si.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_PREVENTPINNING | STARTF_TITLEISAPPID | STARTF_USESHOWWINDOW;
  122. si.wShowWindow = SW_HIDE;
  123. THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(selfPath.c_str(), cmd.data(), nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &_subProcInfo));
  124. }
  125. // Ensure process in job before start
  126. THROW_IF_WIN32_BOOL_FALSE(AssignProcessToJobObject(_hJob.get(), _subProcInfo.hProcess));
  127. THROW_LAST_ERROR_IF(ResumeThread(_subProcInfo.hThread) == static_cast<DWORD>(-1));
  128. HANDLE handles[] = { hEvent.get(), _subProcInfo.hProcess };
  129. auto ret = WaitForMultipleObjects(static_cast<DWORD>(std::size(handles)), handles, FALSE, INFINITE);
  130. error = ERROR_TIMEOUT;
  131. switch (ret)
  132. {
  133. case WAIT_OBJECT_0: // Event signaled, clash process started suspended
  134. {
  135. auto info = reinterpret_cast<ProcessInfo*>(buffer.get());
  136. _clashProcInfo.dwProcessId = info->processId;
  137. _clashProcInfo.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _clashProcInfo.dwProcessId);
  138. THROW_LAST_ERROR_IF_NULL(_clashProcInfo.hProcess);
  139. _clashProcInfo.dwThreadId = info->threadId;
  140. _clashProcInfo.hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, _clashProcInfo.dwThreadId);
  141. THROW_LAST_ERROR_IF_NULL(_clashProcInfo.hThread);
  142. _hWndConsole = info->hWndConsole;
  143. }
  144. break;
  145. case WAIT_OBJECT_0 + 1: // Sub process exited before event signaled
  146. {
  147. DWORD exitCode;
  148. THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(_subProcInfo.hProcess, &exitCode));
  149. HRESULT hr = static_cast<HRESULT>(exitCode); // Treat exit code as hresult
  150. if (SUCCEEDED(hr)) hr = E_UNEXPECTED;
  151. THROW_HR(hr);
  152. }
  153. return false;
  154. case WAIT_FAILED:
  155. error = GetLastError();
  156. [[fallthrough]];
  157. case WAIT_TIMEOUT:
  158. THROW_WIN32(error);
  159. return false;
  160. }
  161. _hEvent = std::move(hEvent);
  162. ResumeThread(_clashProcInfo.hThread);
  163. }
  164. catch (...)
  165. {
  166. _hJob.reset();
  167. LOG_CAUGHT_EXCEPTION();
  168. return false;
  169. }
  170. _state = State::Running;
  171. return true;
  172. }
  173. void CProcessManager::Stop()
  174. {
  175. if (_state != State::Stop)
  176. {
  177. _state = State::Stop;
  178. _hJob.reset();
  179. _subProcInfo.reset();
  180. _clashProcInfo.reset();
  181. _hWndConsole = nullptr;
  182. _hEvent.reset();
  183. }
  184. }
  185. void CProcessManager::SendStopSignal()
  186. {
  187. if (_state == State::Running)
  188. {
  189. _state = State::WaitStop;
  190. SetEvent(_hEvent.get());
  191. }
  192. }
  193. int CProcessManager::SubProcess(std::wstring_view guid)
  194. {
  195. try
  196. {
  197. wchar_t objName[objNameSize + 1] = OBJPREFIX;
  198. size_t i = guid.copy(objName + prefixSize, guidSize - 1);
  199. objName[prefixSize + i] = L'F';
  200. wil::unique_handle hFileMapping(OpenFileMappingW(FILE_MAP_WRITE | FILE_MAP_READ, FALSE, objName));
  201. THROW_LAST_ERROR_IF_NULL(hFileMapping);
  202. wil::unique_mapview_ptr<wchar_t> buffer(reinterpret_cast<wchar_t*>(MapViewOfFile(hFileMapping.get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0)));
  203. THROW_LAST_ERROR_IF_NULL(buffer);
  204. objName[prefixSize + i] = L'E';
  205. wil::unique_handle hEvent(OpenEventW(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, objName));
  206. THROW_LAST_ERROR_IF_NULL(hEvent);
  207. THROW_IF_WIN32_BOOL_FALSE(AllocConsole());
  208. HWND hWndConsole = ::GetConsoleWindow();
  209. THROW_LAST_ERROR_IF_NULL(hWndConsole);
  210. ShowWindow(hWndConsole, SW_HIDE);
  211. auto exePath = buffer.get();
  212. auto clashCmd = buffer.get() + wcslen(exePath) + 1;
  213. STARTUPINFOW si;
  214. si.cb = sizeof(si);
  215. wil::unique_process_information procInfo;
  216. THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(exePath, clashCmd, nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &procInfo));
  217. auto info = reinterpret_cast<ProcessInfo*>(buffer.get());
  218. /**info = {
  219. .processId = procInfo.dwProcessId,
  220. .threadId = procInfo.dwThreadId,
  221. .hWndConsole = hWndConsole
  222. };*/
  223. info->processId = procInfo.dwProcessId;
  224. info->threadId = procInfo.dwThreadId;
  225. info->hWndConsole = hWndConsole;
  226. SetConsoleCtrlHandler(nullptr, TRUE); // Ignores Ctrl+C
  227. THROW_IF_WIN32_BOOL_FALSE(SetEvent(hEvent.get()));
  228. while (true)
  229. {
  230. HANDLE handles[] = { hEvent.get(), procInfo.hProcess };
  231. auto ret = WaitForMultipleObjects(static_cast<DWORD>(std::size(handles)), handles, FALSE, INFINITE);
  232. if (ret == WAIT_OBJECT_0) // Event signaled
  233. {
  234. GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
  235. }
  236. else if (ret == WAIT_OBJECT_0 + 1) // Clash process exited
  237. {
  238. break;
  239. }
  240. else if (ret == WAIT_FAILED)
  241. {
  242. THROW_LAST_ERROR();
  243. }
  244. else if (ret == WAIT_TIMEOUT)
  245. {
  246. THROW_WIN32(ERROR_TIMEOUT);
  247. }
  248. }
  249. const std::wstring_view msg = (
  250. L"\n"
  251. L"[Process completed]\n");
  252. THROW_IF_WIN32_BOOL_FALSE(WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg.data(), static_cast<DWORD>(msg.size()), nullptr, nullptr));
  253. static_cast<void>(_getch());
  254. }
  255. CATCH_RETURN();
  256. return S_OK;
  257. }