From 1eaa054b33738dcce2b694051c15204544000739 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 6 Jun 2021 22:43:40 -0600 Subject: [PATCH] improve devtools resource management and style fixes --- GUI/mainwindow.cpp | 33 +++--- extensions/devtools.cpp | 133 +++++++++++------------- extensions/devtools.h | 2 +- extensions/devtoolsdeepltranslate.cpp | 2 +- extensions/devtoolspapagotranslate.cpp | 2 +- extensions/devtoolssystrantranslate.cpp | 2 +- 6 files changed, 80 insertions(+), 94 deletions(-) diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index a8b9615..ff408b3 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -133,6 +133,21 @@ namespace } }; } + void AttachSavedProcesses() + { + std::unordered_set attachTargets; + if (autoAttach) + for (auto process : QString(QTextFile(GAME_SAVE_FILE, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts)) + attachTargets.insert(S(process)); + if (autoAttachSavedOnly) + for (auto process : QString(QTextFile(HOOK_SAVE_FILE, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts)) + attachTargets.insert(S(process.split(" , ")[0])); + + if (!attachTargets.empty()) + for (auto [processId, processName] : GetAllProcesses()) + if (processName && attachTargets.count(processName.value()) > 0 && alreadyAttached.count(processId) == 0) Host::InjectProcess(processId); + } + std::optional UserSelectedProcess() { QStringList savedProcesses = QString::fromUtf8(QTextFile(GAME_SAVE_FILE, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts); @@ -666,23 +681,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) else for (auto [processId, processName] : processes) if (processName.value_or(L"").find(L"\\" + arg.substr(2)) != std::string::npos) Host::InjectProcess(processId); - std::thread([] - { - for (; ; Sleep(10000)) - { - std::unordered_set attachTargets; - if (autoAttach) - for (auto process : QString(QTextFile(GAME_SAVE_FILE, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts)) - attachTargets.insert(S(process)); - if (autoAttachSavedOnly) - for (auto process : QString(QTextFile(HOOK_SAVE_FILE, QIODevice::ReadOnly).readAll()).split("\n", QString::SkipEmptyParts)) - attachTargets.insert(S(process.split(" , ")[0])); - - if (!attachTargets.empty()) - for (auto [processId, processName] : GetAllProcesses()) - if (processName && attachTargets.count(processName.value()) > 0 && alreadyAttached.count(processId) == 0) Host::InjectProcess(processId); - } - }).detach(); + std::thread([] { for (; ; Sleep(10000)) AttachSavedProcesses(); }).detach(); } MainWindow::~MainWindow() diff --git a/extensions/devtools.cpp b/extensions/devtools.cpp index 63960c6..e2955ac 100644 --- a/extensions/devtools.cpp +++ b/extensions/devtools.cpp @@ -20,17 +20,53 @@ extern Settings settings; namespace { - auto statusLabel = new QLabel("Stopped"); - PROCESS_INFORMATION processInfo = {}; - std::atomic idCounter = 0; - std::mutex devToolsMutex; + QLabel* statusLabel; + AutoHandle<> process; QWebSocket webSocket; - std::unordered_map>> mapQueue; + std::atomic idCounter = 0; + Synchronized>>> mapQueue; void StatusChanged(QString status) { QMetaObject::invokeMethod(statusLabel, std::bind(&QLabel::setText, statusLabel, status)); } + void Start(std::wstring chromePath, bool headless) + { + if (process) DevTools::Close(); + + auto args = FormatString( + L"%s --proxy-server=direct:// --disable-extensions --disable-gpu --user-data-dir=%s\\devtoolscache --remote-debugging-port=9222", + chromePath, + std::filesystem::current_path().wstring() + ); + if (headless) args += L" --headless"; + DWORD exitCode = 0; + STARTUPINFOW DUMMY = { sizeof(DUMMY) }; + PROCESS_INFORMATION processInfo = {}; + if (!CreateProcessW(NULL, args.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &DUMMY, &processInfo)) return StatusChanged("StartupFailed"); + CloseHandle(processInfo.hThread); + process = processInfo.hProcess; + + if (HttpRequest httpRequest{ + L"Mozilla/5.0 Textractor", + L"127.0.0.1", + L"POST", + L"/json/list", + "", + NULL, + 9222, + NULL, + WINHTTP_FLAG_ESCAPE_DISABLE + }) + if (auto list = Copy(JSON::Parse(httpRequest.response).Array())) if (auto it = std::find_if( + list->begin(), + list->end(), + [](const JSON::Value& object) { return object[L"type"].String() && *object[L"type"].String() == L"page" && object[L"webSocketDebuggerUrl"].String(); } + ); it != list->end()) return webSocket.open(S(*(*it)[L"webSocketDebuggerUrl"].String())); + + StatusChanged("ConnectingFailed"); + } + auto _ = ([] { QObject::connect(&webSocket, &QWebSocket::stateChanged, @@ -38,11 +74,11 @@ namespace QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](QString message) { auto result = JSON::Parse(S(message)); - std::scoped_lock lock(devToolsMutex); - if (auto id = result[L"id"].Number()) if (auto request = mapQueue.find((int)*id); request != mapQueue.end()) + auto mapQueue = ::mapQueue.Acquire(); + if (auto id = result[L"id"].Number()) if (auto request = mapQueue->find((int)*id); request != mapQueue->end()) { request->second.set(result); - mapQueue.erase(request); + mapQueue->erase(request); } }); }(), 0); @@ -50,7 +86,7 @@ namespace namespace DevTools { - void Start() + void Initialize() { QString chromePath = settings.value(CHROME_LOCATION).toString(); wchar_t programFiles[MAX_PATH + 100] = {}; @@ -79,47 +115,7 @@ namespace DevTools auto startButton = new QPushButton(START_DEVTOOLS), stopButton = new QPushButton(STOP_DEVTOOLS); headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool()); QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); }); - QObject::connect(startButton, &QPushButton::clicked, [chromePathEdit, headlessCheck] - { - DWORD exitCode = 0; - auto args = FormatString( - L"%s --proxy-server=direct:// --disable-extensions --disable-gpu --user-data-dir=%s\\devtoolscache --remote-debugging-port=9222", - S(chromePathEdit->text()), - std::filesystem::current_path().wstring() - ); - if (headlessCheck->isChecked()) args += L" --headless"; - STARTUPINFOW DUMMY = { sizeof(DUMMY) }; - if ((GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE) || - CreateProcessW(NULL, args.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &DUMMY, &processInfo) - ) - { - if (HttpRequest httpRequest{ - L"Mozilla/5.0 Textractor", - L"127.0.0.1", - L"POST", - L"/json/list", - "", - NULL, - 9222, - NULL, - WINHTTP_FLAG_ESCAPE_DISABLE - }) - { - if (auto list = Copy(JSON::Parse(httpRequest.response).Array())) if (auto it = std::find_if( - list->begin(), - list->end(), - [](const JSON::Value& object) { return object[L"type"].String() && *object[L"type"].String() == L"page" && object[L"webSocketDebuggerUrl"].String(); } - ); it != list->end()) - { - std::scoped_lock lock(devToolsMutex); - webSocket.open(S(*(*it)[L"webSocketDebuggerUrl"].String())); - return; - } - } - StatusChanged("Failed Connection"); - } - else StatusChanged("Failed Startup"); - }); + QObject::connect(startButton, &QPushButton::clicked, [chromePathEdit, headlessCheck] { Start(S(chromePathEdit->text()), headlessCheck->isChecked()); }); QObject::connect(stopButton, &QPushButton::clicked, &Close); auto buttons = new QHBoxLayout(); buttons->addWidget(startButton); @@ -130,6 +126,7 @@ namespace DevTools QObject::connect(autoStartCheck, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); }); display->addRow(AUTO_START, autoStartCheck); display->addRow(buttons); + statusLabel = new QLabel("Stopped"); statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); display->addRow(DEVTOOLS_STATUS, statusLabel); if (autoStartCheck->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection); @@ -137,29 +134,24 @@ namespace DevTools void Close() { - std::scoped_lock lock(devToolsMutex); - for (const auto& [_, task] : mapQueue) task.set_exception(std::runtime_error("closed")); webSocket.close(); - mapQueue.clear(); - DWORD exitCode = 0; - if (GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE) + for (const auto& [_, task] : mapQueue.Acquire().contents) task.set_exception(std::runtime_error("closed")); + mapQueue->clear(); + + if (process) { - TerminateProcess(processInfo.hProcess, 0); - WaitForSingleObject(processInfo.hProcess, 2000); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - for (int retry = 0; ++retry < 20; Sleep(100)) - { - try { std::filesystem::remove_all(L"devtoolscache"); break; } - catch (std::filesystem::filesystem_error) { continue; } + TerminateProcess(process, 0); + WaitForSingleObject(process, 1000); + for (int retry = 0; ++retry < 20; Sleep(100)) + try { std::filesystem::remove_all(L"devtoolscache"); break; } + catch (std::filesystem::filesystem_error) { continue; } } + process = NULL; StatusChanged("Stopped"); } bool Connected() { - std::scoped_lock lock(devToolsMutex); return webSocket.state() == QAbstractSocket::ConnectedState; } @@ -167,14 +159,9 @@ namespace DevTools { concurrency::task_completion_event> response; int id = idCounter += 1; - auto message = FormatString(LR"({"id":%d,"method":"%S","params":%s})", id, method, params); - { - std::scoped_lock lock(devToolsMutex); - if (webSocket.state() != QAbstractSocket::ConnectedState) return {}; - mapQueue.try_emplace(id, response); - webSocket.sendTextMessage(S(message)); - webSocket.flush(); - } + if (!Connected()) return {}; + mapQueue->try_emplace(id, response); + QMetaObject::invokeMethod(&webSocket, std::bind(&QWebSocket::sendTextMessage, webSocket, S(FormatString(LR"({"id":%d,"method":"%S","params":%s})", id, method, params)))); try { if (auto result = create_task(response).get()[L"result"]) return result; } catch (...) {} return {}; } diff --git a/extensions/devtools.h b/extensions/devtools.h index 919bd7d..83a004b 100644 --- a/extensions/devtools.h +++ b/extensions/devtools.h @@ -3,7 +3,7 @@ namespace DevTools { - void Start(); + void Initialize(); void Close(); bool Connected(); JSON::Value SendRequest(const char* method, const std::wstring& params = L"{}"); diff --git a/extensions/devtoolsdeepltranslate.cpp b/extensions/devtoolsdeepltranslate.cpp index 8c19b71..7293e8a 100644 --- a/extensions/devtoolsdeepltranslate.cpp +++ b/extensions/devtoolsdeepltranslate.cpp @@ -46,7 +46,7 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved { case DLL_PROCESS_ATTACH: { - DevTools::Start(); + DevTools::Initialize(); } break; case DLL_PROCESS_DETACH: diff --git a/extensions/devtoolspapagotranslate.cpp b/extensions/devtoolspapagotranslate.cpp index fcb43e2..cce6473 100644 --- a/extensions/devtoolspapagotranslate.cpp +++ b/extensions/devtoolspapagotranslate.cpp @@ -37,7 +37,7 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved { case DLL_PROCESS_ATTACH: { - DevTools::Start(); + DevTools::Initialize(); } break; case DLL_PROCESS_DETACH: diff --git a/extensions/devtoolssystrantranslate.cpp b/extensions/devtoolssystrantranslate.cpp index 6f158c4..d1fcacd 100644 --- a/extensions/devtoolssystrantranslate.cpp +++ b/extensions/devtoolssystrantranslate.cpp @@ -35,7 +35,7 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved { case DLL_PROCESS_ATTACH: { - DevTools::Start(); + DevTools::Initialize(); } break; case DLL_PROCESS_DETACH: