CProcessManager.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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. State CProcessManager::IsRunning()
  60. {
  61. return _state;
  62. }
  63. const PROCESS_INFORMATION& CProcessManager::GetSubProcessInfo()
  64. {
  65. return _subProcInfo;
  66. }
  67. const PROCESS_INFORMATION& CProcessManager::GetClashProcessInfo()
  68. {
  69. return _clashProcInfo;
  70. }
  71. HWND CProcessManager::GetConsoleWindow()
  72. {
  73. return _hWndConsole;
  74. }
  75. void CProcessManager::StartTest() {
  76. try
  77. {
  78. THROW_IF_WIN32_BOOL_FALSE(AllocConsole());
  79. freopen("CONOUT$", "w", stdout);
  80. wchar_t objName[objNameSize + 1] = OBJPREFIX;
  81. GUID guid = {};
  82. THROW_IF_FAILED(CoCreateGuid(&guid));
  83. THROW_HR_IF(E_OUTOFMEMORY, StringFromGUID2(guid, objName + prefixSize, guidSize) != guidSize);
  84. size_t size = (std::max)((_exePath.native().size() + 1) + (_clashCmd.size() + 1) * sizeof(wchar_t), sizeof(ProcessInfo));
  85. objName[objNameSize - 1] = L'F';
  86. std::wstring name(objName);
  87. printf("%ws\n", name.c_str());
  88. wil::unique_handle hFileMapping(CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, static_cast<DWORD>(size), objName));
  89. THROW_LAST_ERROR_IF_NULL(hFileMapping);
  90. auto error = GetLastError();
  91. if (error == ERROR_ALREADY_EXISTS) THROW_WIN32(error);
  92. wil::unique_mapview_ptr<wchar_t> buffer(reinterpret_cast<wchar_t*>(MapViewOfFile(hFileMapping.get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0)));
  93. THROW_LAST_ERROR_IF_NULL(buffer);
  94. objName[objNameSize - 1] = L'E';
  95. wil::unique_handle hEvent(CreateEventW(nullptr, FALSE, FALSE, objName));
  96. THROW_LAST_ERROR_IF_NULL(hEvent);
  97. error = GetLastError();
  98. if (error == ERROR_ALREADY_EXISTS) THROW_WIN32(error);
  99. auto exePathPtr = buffer.get();
  100. size_t i = _exePath.native().copy(exePathPtr, std::filesystem::path::string_type::npos);
  101. exePathPtr[i] = 0;
  102. auto cmdPtr = buffer.get() + i + 1;
  103. i = _clashCmd.copy(cmdPtr, std::wstring::npos);
  104. cmdPtr[i] = 0;
  105. auto selfPath = GetModuleFsPath(mInstance);
  106. auto guidStr = objName + prefixSize;
  107. std::wstring cmd(LR"(")");
  108. cmd.append(selfPath);
  109. cmd.append(LR"(" --pm=)");
  110. cmd.append(guidStr, guidSize - 1);
  111. HANDLE handles[] = { hEvent.get()};
  112. auto ret = WaitForMultipleObjects(static_cast<DWORD>(std::size(handles)), handles, FALSE, INFINITE);
  113. error = ERROR_TIMEOUT;
  114. switch (ret)
  115. {
  116. case WAIT_OBJECT_0: // Event signaled, clash process started suspended
  117. {
  118. auto info = reinterpret_cast<ProcessInfo*>(buffer.get());
  119. _clashProcInfo.dwProcessId = info->processId;
  120. _clashProcInfo.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _clashProcInfo.dwProcessId);
  121. THROW_LAST_ERROR_IF_NULL(_clashProcInfo.hProcess);
  122. _clashProcInfo.dwThreadId = info->threadId;
  123. _clashProcInfo.hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, _clashProcInfo.dwThreadId);
  124. THROW_LAST_ERROR_IF_NULL(_clashProcInfo.hThread);
  125. _hWndConsole = info->hWndConsole;
  126. }
  127. break;
  128. case WAIT_OBJECT_0 + 1: // Sub process exited before event signaled
  129. {
  130. //DWORD exitCode;
  131. //THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(_subProcInfo.hProcess, &exitCode));
  132. //HRESULT hr = static_cast<HRESULT>(exitCode); // Treat exit code as hresult
  133. //if (SUCCEEDED(hr)) hr = E_UNEXPECTED;
  134. //THROW_HR(hr);
  135. }
  136. return;
  137. case WAIT_FAILED:
  138. error = GetLastError();
  139. [[fallthrough]];
  140. case WAIT_TIMEOUT:
  141. THROW_WIN32(error);
  142. return;
  143. }
  144. _hEvent = std::move(hEvent);
  145. }
  146. catch (...)
  147. {
  148. LOG_CAUGHT_EXCEPTION();
  149. }
  150. }
  151. bool CProcessManager::Start()
  152. {
  153. if (_state != State::Stop)
  154. return false;
  155. try
  156. {
  157. _hJob.reset(CreateJobObjectW(nullptr, nullptr));
  158. THROW_LAST_ERROR_IF_NULL(_hJob);
  159. JOBOBJECT_BASIC_LIMIT_INFORMATION bli;
  160. bli.LimitFlags = JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
  161. JOBOBJECT_EXTENDED_LIMIT_INFORMATION eli;
  162. eli.BasicLimitInformation = bli;
  163. THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(_hJob.get(), JobObjectExtendedLimitInformation, &eli, sizeof(eli)));
  164. wchar_t objName[objNameSize + 1] = OBJPREFIX;
  165. GUID guid = {};
  166. THROW_IF_FAILED(CoCreateGuid(&guid));
  167. THROW_HR_IF(E_OUTOFMEMORY, StringFromGUID2(guid, objName + prefixSize, guidSize) != guidSize);
  168. size_t size = (std::max)((_exePath.native().size() + 1) + (_clashCmd.size() + 1) * sizeof(wchar_t), sizeof(ProcessInfo));
  169. objName[objNameSize - 1] = L'F';
  170. wil::unique_handle hFileMapping(CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, static_cast<DWORD>(size), objName));
  171. THROW_LAST_ERROR_IF_NULL(hFileMapping);
  172. auto error = GetLastError();
  173. if (error == ERROR_ALREADY_EXISTS) THROW_WIN32(error);
  174. wil::unique_mapview_ptr<wchar_t> buffer(reinterpret_cast<wchar_t*>(MapViewOfFile(hFileMapping.get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0)));
  175. THROW_LAST_ERROR_IF_NULL(buffer);
  176. objName[objNameSize - 1] = L'E';
  177. wil::unique_handle hEvent(CreateEventW(nullptr, FALSE, FALSE, objName));
  178. THROW_LAST_ERROR_IF_NULL(hEvent);
  179. error = GetLastError();
  180. if (error == ERROR_ALREADY_EXISTS) THROW_WIN32(error);
  181. auto exePathPtr = buffer.get();
  182. size_t i = _exePath.native().copy(exePathPtr, std::filesystem::path::string_type::npos);
  183. exePathPtr[i] = 0;
  184. auto cmdPtr = buffer.get() + i + 1;
  185. i = _clashCmd.copy(cmdPtr, std::wstring::npos);
  186. cmdPtr[i] = 0;
  187. auto selfPath = GetModuleFsPath(mInstance);
  188. auto guidStr = objName + prefixSize;
  189. std::wstring cmd(LR"(")");
  190. cmd.append(selfPath);
  191. cmd.append(LR"(" --pm=)");
  192. cmd.append(guidStr, guidSize - 1);
  193. {
  194. // Disable Windows Error Reporting for subprocess
  195. auto lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
  196. auto restoreErrorMode = wil::scope_exit([=]() { // Restore error mode after CreateProcessW
  197. SetErrorMode(lastErrorMode);
  198. });
  199. wil::unique_cotaskmem_string appId;
  200. THROW_IF_FAILED(GetCurrentProcessExplicitAppUserModelID(&appId));
  201. /*wil::unique_cotaskmem_string appId;
  202. THROW_IF_FAILED(GetCurrentProcessExplicitAppUserModelID(&appId));*/
  203. /*STARTUPINFOW si = {
  204. .cb = sizeof(si),
  205. .lpTitle = appId.get(),
  206. .dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_PREVENTPINNING | STARTF_TITLEISAPPID | STARTF_USESHOWWINDOW,
  207. .wShowWindow = SW_HIDE
  208. };*/
  209. STARTUPINFOW si;
  210. ZeroMemory(&si, sizeof(si));
  211. si.cb = sizeof(si);
  212. si.lpTitle = appId.get();
  213. si.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_PREVENTPINNING | STARTF_TITLEISAPPID | STARTF_USESHOWWINDOW;
  214. si.wShowWindow = SW_HIDE;
  215. THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(selfPath.c_str(), cmd.data(), nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &_subProcInfo));
  216. }
  217. // Ensure process in job before start
  218. THROW_IF_WIN32_BOOL_FALSE(AssignProcessToJobObject(_hJob.get(), _subProcInfo.hProcess));
  219. THROW_LAST_ERROR_IF(ResumeThread(_subProcInfo.hThread) == static_cast<DWORD>(-1));
  220. HANDLE handles[] = { hEvent.get(), _subProcInfo.hProcess };
  221. auto ret = WaitForMultipleObjects(static_cast<DWORD>(std::size(handles)), handles, FALSE, INFINITE);
  222. error = ERROR_TIMEOUT;
  223. switch (ret)
  224. {
  225. case WAIT_OBJECT_0: // Event signaled, clash process started suspended
  226. {
  227. auto info = reinterpret_cast<ProcessInfo*>(buffer.get());
  228. _clashProcInfo.dwProcessId = info->processId;
  229. _clashProcInfo.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _clashProcInfo.dwProcessId);
  230. THROW_LAST_ERROR_IF_NULL(_clashProcInfo.hProcess);
  231. _clashProcInfo.dwThreadId = info->threadId;
  232. _clashProcInfo.hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, _clashProcInfo.dwThreadId);
  233. THROW_LAST_ERROR_IF_NULL(_clashProcInfo.hThread);
  234. _hWndConsole = info->hWndConsole;
  235. }
  236. break;
  237. case WAIT_OBJECT_0 + 1: // Sub process exited before event signaled
  238. {
  239. DWORD exitCode;
  240. THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(_subProcInfo.hProcess, &exitCode));
  241. HRESULT hr = static_cast<HRESULT>(exitCode); // Treat exit code as hresult
  242. if (SUCCEEDED(hr)) hr = E_UNEXPECTED;
  243. THROW_HR(hr);
  244. }
  245. return false;
  246. case WAIT_FAILED:
  247. error = GetLastError();
  248. [[fallthrough]];
  249. case WAIT_TIMEOUT:
  250. THROW_WIN32(error);
  251. return false;
  252. }
  253. _hEvent = std::move(hEvent);
  254. ResumeThread(_clashProcInfo.hThread);
  255. }
  256. catch (...)
  257. {
  258. if (_hJob)
  259. {
  260. _hJob.reset();
  261. }
  262. LOG_CAUGHT_EXCEPTION();
  263. return false;
  264. }
  265. _state = State::Running;
  266. return true;
  267. }
  268. void CProcessManager::Stop()
  269. {
  270. if (_state != State::Stop)
  271. {
  272. _state = State::Stop;
  273. _hJob.reset();
  274. _subProcInfo.reset();
  275. _clashProcInfo.reset();
  276. _hWndConsole = nullptr;
  277. _hEvent.reset();
  278. }
  279. }
  280. void CProcessManager::SendStopSignal()
  281. {
  282. if (_state == State::Running)
  283. {
  284. _state = State::WaitStop;
  285. SetEvent(_hEvent.get());
  286. }
  287. }
  288. int CProcessManager::SubProcess(std::wstring_view guid)
  289. {
  290. try
  291. {
  292. wchar_t objName[objNameSize + 1] = OBJPREFIX;
  293. size_t i = guid.copy(objName + prefixSize, guidSize - 1);
  294. objName[prefixSize + i] = L'F';
  295. wil::unique_handle hFileMapping(OpenFileMappingW(FILE_MAP_WRITE | FILE_MAP_READ, FALSE, objName));
  296. THROW_LAST_ERROR_IF_NULL(hFileMapping);
  297. wil::unique_mapview_ptr<wchar_t> buffer(reinterpret_cast<wchar_t*>(MapViewOfFile(hFileMapping.get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0)));
  298. THROW_LAST_ERROR_IF_NULL(buffer);
  299. objName[prefixSize + i] = L'E';
  300. wil::unique_handle hEvent(OpenEventW(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, objName));
  301. THROW_LAST_ERROR_IF_NULL(hEvent);
  302. THROW_IF_WIN32_BOOL_FALSE(AllocConsole());
  303. HWND hWndConsole = ::GetConsoleWindow();
  304. THROW_LAST_ERROR_IF_NULL(hWndConsole);
  305. ShowWindow(hWndConsole, SW_HIDE);
  306. auto exePath = buffer.get();
  307. auto clashCmd = buffer.get() + wcslen(exePath) + 1;
  308. STARTUPINFOW si;
  309. ZeroMemory(&si, sizeof(si));
  310. si.cb = sizeof(si);
  311. wil::unique_process_information procInfo;
  312. THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(exePath, clashCmd, nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &procInfo));
  313. auto info = reinterpret_cast<ProcessInfo*>(buffer.get());
  314. /**info = {
  315. .processId = procInfo.dwProcessId,
  316. .threadId = procInfo.dwThreadId,
  317. .hWndConsole = hWndConsole
  318. };*/
  319. info->processId = procInfo.dwProcessId;
  320. info->threadId = procInfo.dwThreadId;
  321. info->hWndConsole = hWndConsole;
  322. SetConsoleCtrlHandler(nullptr, TRUE); // Ignores Ctrl+C
  323. THROW_IF_WIN32_BOOL_FALSE(SetEvent(hEvent.get()));
  324. while (true)
  325. {
  326. HANDLE handles[] = { hEvent.get(), procInfo.hProcess };
  327. auto ret = WaitForMultipleObjects(static_cast<DWORD>(std::size(handles)), handles, FALSE, INFINITE);
  328. if (ret == WAIT_OBJECT_0) // Event signaled
  329. {
  330. GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
  331. }
  332. else if (ret == WAIT_OBJECT_0 + 1) // Clash process exited
  333. {
  334. break;
  335. }
  336. else if (ret == WAIT_FAILED)
  337. {
  338. THROW_LAST_ERROR();
  339. }
  340. else if (ret == WAIT_TIMEOUT)
  341. {
  342. THROW_WIN32(ERROR_TIMEOUT);
  343. }
  344. }
  345. const std::wstring_view msg = (
  346. L"\n"
  347. L"[Process completed]\n");
  348. THROW_IF_WIN32_BOOL_FALSE(WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg.data(), static_cast<DWORD>(msg.size()), nullptr, nullptr));
  349. static_cast<void>(_getch());
  350. }
  351. CATCH_RETURN();
  352. return S_OK;
  353. }