From 0015a12cf8916d8000c6d14356b4c17c62f4a588 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Wed, 11 Sep 2019 23:36:13 -0400
Subject: rewritten spawn() to use std::wstring instead of manual buffers error
dialogs now use TaskDialog with more user-friendly text
---
src/spawn.h | 19 -------------------
1 file changed, 19 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/spawn.h b/src/spawn.h
index c2d99bdb..9a2dbfbd 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -26,25 +26,6 @@ along with Mod Organizer. If not, see .
#include
#include
-
-/**
- * @brief a dirty little trick so we can issue a clean restart from startBinary
- * @note unused
- */
-/*class ExitProxy : public QObject {
- Q_OBJECT
-public:
- static ExitProxy *instance();
- void emitExit();
-signals:
- void exit();
-private:
- ExitProxy() {}
-private:
- static ExitProxy *s_Instance;
-};*/
-
-
/**
* @brief spawn a binary with Mod Organizer injected
*
--
cgit v1.3.1
From 971cecf343777894cec5144da11d16ef97d0db31 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 12 Sep 2019 01:09:40 -0400
Subject: split spawnBinaryProcess() into spawn, no changes
---
src/mainwindow.cpp | 2 +-
src/organizercore.cpp | 286 ++--------------------------------------------
src/spawn.cpp | 310 +++++++++++++++++++++++++++++++++++++++++++++++++-
src/spawn.h | 18 +++
4 files changed, 338 insertions(+), 278 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 054262e9..6737e330 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -6434,7 +6434,7 @@ void MainWindow::on_bossButton_clicked()
return;
}
- HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"),
+ HANDLE loot = spawn::startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"),
parameters.join(" "),
qApp->applicationDirPath() + "/loot",
true,
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 91e16716..9403b47d 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -135,34 +135,6 @@ static DWORD getProcessParentID(DWORD pid)
return res;
}
-static void startSteam(QWidget *widget)
-{
- QSettings steamSettings("HKEY_CURRENT_USER\\Software\\Valve\\Steam",
- QSettings::NativeFormat);
- QString exe = steamSettings.value("SteamExe", "").toString();
- if (!exe.isEmpty()) {
- exe = QString("\"%1\"").arg(exe);
- // See if username and password supplied. If so, pass them into steam.
- QStringList args;
- QString username;
- QString password;
- if (Settings::instance().steam().login(username, password)) {
- args << "-login";
- args << username;
- if (password != "") {
- args << password;
- }
- }
- if (!QProcess::startDetached(exe, args)) {
- reportError(QObject::tr("Failed to start \"%1\"").arg(exe));
- } else {
- QMessageBox::information(
- widget, QObject::tr("Waiting"),
- QObject::tr("Please press OK once you're logged into steam."));
- }
- }
-}
-
template
QStringList toStringList(InputIterator current, InputIterator end)
{
@@ -173,86 +145,6 @@ QStringList toStringList(InputIterator current, InputIterator end)
return result;
}
-bool checkService()
-{
- SC_HANDLE serviceManagerHandle = NULL;
- SC_HANDLE serviceHandle = NULL;
- LPSERVICE_STATUS_PROCESS serviceStatus = NULL;
- LPQUERY_SERVICE_CONFIG serviceConfig = NULL;
- bool serviceRunning = true;
-
- DWORD bytesNeeded;
-
- try {
- serviceManagerHandle = OpenSCManager(NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
- if (!serviceManagerHandle) {
- log::warn("failed to open service manager (query status) (error {})", GetLastError());
- throw 1;
- }
-
- serviceHandle = OpenService(serviceManagerHandle, L"EventLog", SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
- if (!serviceHandle) {
- log::warn("failed to open EventLog service (query status) (error {})", GetLastError());
- throw 2;
- }
-
- if (QueryServiceConfig(serviceHandle, NULL, 0, &bytesNeeded)
- || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
- log::warn("failed to get size of service config (error {})", GetLastError());
- throw 3;
- }
-
- DWORD serviceConfigSize = bytesNeeded;
- serviceConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, serviceConfigSize);
- if (!QueryServiceConfig(serviceHandle, serviceConfig, serviceConfigSize, &bytesNeeded)) {
- log::warn("failed to query service config (error {})", GetLastError());
- throw 4;
- }
-
- if (serviceConfig->dwStartType == SERVICE_DISABLED) {
- log::error("Windows Event Log service is disabled!");
- serviceRunning = false;
- }
-
- if (QueryServiceStatusEx(serviceHandle, SC_STATUS_PROCESS_INFO, NULL, 0, &bytesNeeded)
- || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
- log::warn("failed to get size of service status (error {})", GetLastError());
- throw 5;
- }
-
- DWORD serviceStatusSize = bytesNeeded;
- serviceStatus = (LPSERVICE_STATUS_PROCESS)LocalAlloc(LMEM_FIXED, serviceStatusSize);
- if (!QueryServiceStatusEx(serviceHandle, SC_STATUS_PROCESS_INFO, (LPBYTE)serviceStatus, serviceStatusSize, &bytesNeeded)) {
- log::warn("failed to query service status (error {})", GetLastError());
- throw 6;
- }
-
- if (serviceStatus->dwCurrentState != SERVICE_RUNNING) {
- log::error("Windows Event Log service is not running");
- serviceRunning = false;
- }
- }
- catch (int e) {
- UNUSED_VAR(e);
- serviceRunning = false;
- }
-
- if (serviceStatus) {
- LocalFree(serviceStatus);
- }
- if (serviceConfig) {
- LocalFree(serviceConfig);
- }
- if (serviceHandle) {
- CloseServiceHandle(serviceHandle);
- }
- if (serviceManagerHandle) {
- CloseServiceHandle(serviceManagerHandle);
- }
-
- return serviceRunning;
-}
-
OrganizerCore::OrganizerCore(Settings &settings)
: m_UserInterface(nullptr)
@@ -361,67 +253,6 @@ void OrganizerCore::storeSettings()
}
}
-bool OrganizerCore::testForSteam(bool *found, bool *access)
-{
- HANDLE hProcessSnap;
- HANDLE hProcess;
- PROCESSENTRY32 pe32;
- DWORD lastError;
-
- if (found == nullptr || access == nullptr) {
- return false;
- }
-
- // Take a snapshot of all processes in the system.
- hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if (hProcessSnap == INVALID_HANDLE_VALUE) {
- lastError = GetLastError();
- log::error("unable to get snapshot of processes (error {})", lastError);
- return false;
- }
-
- // Retrieve information about the first process,
- // and exit if unsuccessful
- pe32.dwSize = sizeof(PROCESSENTRY32);
- if (!Process32First(hProcessSnap, &pe32)) {
- lastError = GetLastError();
- log::error("unable to get first process (error {})", lastError);
- CloseHandle(hProcessSnap);
- return false;
- }
-
- *found = false;
- *access = true;
-
- // Now walk the snapshot of processes, and
- // display information about each process in turn
- do {
- if ((_tcsicmp(pe32.szExeFile, L"Steam.exe") == 0) ||
- (_tcsicmp(pe32.szExeFile, L"SteamService.exe") == 0)) {
-
- *found = true;
-
- // Try to open the process to determine if MO has the proper access
- hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
- FALSE, pe32.th32ProcessID);
- if (hProcess == NULL) {
- lastError = GetLastError();
- if (lastError == ERROR_ACCESS_DENIED) {
- *access = false;
- }
- } else {
- CloseHandle(hProcess);
- }
- break;
- }
-
-} while(Process32Next(hProcessSnap, &pe32));
-
-CloseHandle(hProcessSnap);
-return true;
-
-}
-
void OrganizerCore::updateExecutablesList()
{
if (m_PluginContainer == nullptr) {
@@ -1453,93 +1284,18 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
{
prepareStart();
- if (!binary.exists()) {
- reportError(
- tr("Executable not found: %1").arg(qUtf8Printable(binary.absoluteFilePath())));
- return INVALID_HANDLE_VALUE;
- }
-
- if (!steamAppID.isEmpty()) {
- ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(steamAppID).c_str());
- } else {
- ::SetEnvironmentVariableW(L"SteamAPPId",
- ToWString(m_Settings.steam().appID()).c_str());
- }
-
QWidget *window = qApp->activeWindow();
if ((window != nullptr) && (!window->isVisible())) {
window = nullptr;
}
- // This could possibly be extracted somewhere else but it's probably for when
- // we have more than one provider of game registration.
- if ((QFileInfo(
- managedGame()->gameDirectory().absoluteFilePath("steam_api.dll"))
- .exists()
- || QFileInfo(managedGame()->gameDirectory().absoluteFilePath(
- "steam_api64.dll"))
- .exists())
- && (m_Settings.game().loadMechanismType() == LoadMechanism::LOAD_MODORGANIZER)) {
-
- bool steamFound = true;
- bool steamAccess = true;
- if (!testForSteam(&steamFound, &steamAccess)) {
- log::error("unable to determine state of Steam");
- }
-
- if (!steamFound) {
- QDialogButtonBox::StandardButton result;
- result = QuestionBoxMemory::query(window, "steamQuery", binary.fileName(),
- tr("Start Steam?"),
- tr("Steam is required to be running already to correctly start the game. "
- "Should MO try to start steam now?"),
- QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
- if (result == QDialogButtonBox::Yes) {
- startSteam(window);
-
- // double-check that Steam is started and MO has access
- steamFound = true;
- steamAccess = true;
- if (!testForSteam(&steamFound, &steamAccess)) {
- log::error("unable to determine state of Steam");
- } else if (!steamFound) {
- log::error("could not find Steam");
- }
- } else if (result == QDialogButtonBox::Cancel) {
- return INVALID_HANDLE_VALUE;
- }
- }
+ if (!spawn::checkBinary(binary)) {
+ return INVALID_HANDLE_VALUE;
+ }
- if (!steamAccess) {
- QDialogButtonBox::StandardButton result;
- result = QuestionBoxMemory::query(window, "steamAdminQuery", binary.fileName(),
- tr("Steam: Access Denied"),
- tr("MO was denied access to the Steam process. This normally indicates that "
- "Steam is being run as administrator while MO is not. This can cause issues "
- "launching the game. It is recommended to not run Steam as administrator unless "
- "absolutely necessary.\n\n"
- "Restart MO as administrator?"),
- QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
- if (result == QDialogButtonBox::Yes) {
- WCHAR cwd[MAX_PATH];
- if (!GetCurrentDirectory(MAX_PATH, cwd)) {
- log::error("unable to get current directory (error {})", GetLastError());
- cwd[0] = L'\0';
- }
- if (!Helper::adminLaunch(
- qApp->applicationDirPath().toStdWString(),
- qApp->applicationFilePath().toStdWString(),
- std::wstring(cwd))) {
- log::error("unable to relaunch MO as admin");
- return INVALID_HANDLE_VALUE;
- }
- qApp->exit(0);
- return INVALID_HANDLE_VALUE;
- } else if (result == QDialogButtonBox::Cancel) {
- return INVALID_HANDLE_VALUE;
- }
- }
+ if (!spawn::checkSteam(window, managedGame()->gameDirectory(), binary, steamAppID, m_Settings)) {
+ return INVALID_HANDLE_VALUE;
}
while (m_DirectoryUpdate) {
@@ -1566,32 +1322,12 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
return INVALID_HANDLE_VALUE;
}
- // Check if the Windows Event Logging service is running. For some reason, this seems to be
- // critical to the successful running of usvfs.
- if (!checkService()) {
- if (QuestionBoxMemory::query(window, QString("eventLogService"), binary.fileName(),
- tr("Windows Event Log Error"),
- tr("The Windows Event Log service is disabled and/or not running. This prevents"
- " USVFS from running properly. Your mods may not be working in the executable"
- " that you are launching. Note that you may have to restart MO and/or your PC"
- " after the service is fixed.\n\nContinue launching %1?").arg(binary.fileName()),
- QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) {
- return INVALID_HANDLE_VALUE;
- }
+ if (!spawn::checkEnvironment(window, binary)) {
+ return INVALID_HANDLE_VALUE;
}
- for (auto exec : settings().executablesBlacklist().split(";")) {
- if (exec.compare(binary.fileName(), Qt::CaseInsensitive) == 0) {
- if (QuestionBoxMemory::query(window, QString("blacklistedExecutable"), binary.fileName(),
- tr("Blacklisted Executable"),
- tr("The executable you are attempted to launch is blacklisted in the virtual file"
- " system. This will likely prevent the executable, and any executables that are"
- " launched by this one, from seeing any mods. This could extend to INI files, save"
- " games and any other virtualized files.\n\nContinue launching %1?").arg(binary.fileName()),
- QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) {
- return INVALID_HANDLE_VALUE;
- }
- }
+ if (!spawn::checkBlacklist(window, m_Settings, binary)) {
+ return INVALID_HANDLE_VALUE;
}
QString modsPath = settings().paths().mods();
@@ -1628,11 +1364,11 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
log::debug("Spawning proxyed process <{}>", cmdline);
- return startBinary(QFileInfo(QCoreApplication::applicationFilePath()),
+ return spawn::startBinary(QFileInfo(QCoreApplication::applicationFilePath()),
cmdline, QCoreApplication::applicationDirPath(), true);
} else {
log::debug("Spawning direct process <{}, {}, {}>", binPath, arguments, cwdPath);
- return startBinary(binary, arguments, currentDirectory, true);
+ return spawn::startBinary(binary, arguments, currentDirectory, true);
}
} else {
log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath());
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 614bc92f..4376e5bf 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -24,6 +24,7 @@ along with Mod Organizer. If not, see .
#include "env.h"
#include "envwindows.h"
#include "envsecurity.h"
+#include "settings.h"
#include
#include
#include
@@ -41,6 +42,10 @@ along with Mod Organizer. If not, see .
using namespace MOBase;
using namespace MOShared;
+namespace spawn
+{
+
+// details
namespace
{
@@ -320,8 +325,6 @@ bool confirmRestartAsAdmin(const SpawnParameters& sp)
return (r == QMessageBox::Yes);
}
-} // namespace
-
void startBinaryAdmin(const SpawnParameters& sp)
{
if (!confirmRestartAsAdmin(sp)) {
@@ -344,6 +347,307 @@ void startBinaryAdmin(const SpawnParameters& sp)
}
}
+} // namespace
+
+
+bool checkBinary(const QFileInfo& binary)
+{
+ if (!binary.exists()) {
+ reportError(
+ QObject::tr("Executable not found: %1")
+ .arg(qUtf8Printable(binary.absoluteFilePath())));
+
+ return false;
+ }
+
+ return true;
+}
+
+bool testForSteam(bool *found, bool *access)
+{
+ HANDLE hProcessSnap;
+ HANDLE hProcess;
+ PROCESSENTRY32 pe32;
+ DWORD lastError;
+
+ if (found == nullptr || access == nullptr) {
+ return false;
+ }
+
+ // Take a snapshot of all processes in the system.
+ hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hProcessSnap == INVALID_HANDLE_VALUE) {
+ lastError = GetLastError();
+ log::error("unable to get snapshot of processes (error {})", lastError);
+ return false;
+ }
+
+ // Retrieve information about the first process,
+ // and exit if unsuccessful
+ pe32.dwSize = sizeof(PROCESSENTRY32);
+ if (!Process32First(hProcessSnap, &pe32)) {
+ lastError = GetLastError();
+ log::error("unable to get first process (error {})", lastError);
+ CloseHandle(hProcessSnap);
+ return false;
+ }
+
+ *found = false;
+ *access = true;
+
+ // Now walk the snapshot of processes, and
+ // display information about each process in turn
+ do {
+ if ((_tcsicmp(pe32.szExeFile, L"Steam.exe") == 0) ||
+ (_tcsicmp(pe32.szExeFile, L"SteamService.exe") == 0)) {
+
+ *found = true;
+
+ // Try to open the process to determine if MO has the proper access
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+ FALSE, pe32.th32ProcessID);
+ if (hProcess == NULL) {
+ lastError = GetLastError();
+ if (lastError == ERROR_ACCESS_DENIED) {
+ *access = false;
+ }
+ } else {
+ CloseHandle(hProcess);
+ }
+ break;
+ }
+
+ } while(Process32Next(hProcessSnap, &pe32));
+
+ CloseHandle(hProcessSnap);
+ return true;
+}
+
+void startSteam(QWidget *widget)
+{
+ QSettings steamSettings("HKEY_CURRENT_USER\\Software\\Valve\\Steam",
+ QSettings::NativeFormat);
+ QString exe = steamSettings.value("SteamExe", "").toString();
+ if (!exe.isEmpty()) {
+ exe = QString("\"%1\"").arg(exe);
+ // See if username and password supplied. If so, pass them into steam.
+ QStringList args;
+ QString username;
+ QString password;
+ if (Settings::instance().steam().login(username, password)) {
+ args << "-login";
+ args << username;
+ if (password != "") {
+ args << password;
+ }
+ }
+ if (!QProcess::startDetached(exe, args)) {
+ reportError(QObject::tr("Failed to start \"%1\"").arg(exe));
+ } else {
+ QMessageBox::information(
+ widget, QObject::tr("Waiting"),
+ QObject::tr("Please press OK once you're logged into steam."));
+ }
+ }
+}
+
+bool checkSteam(
+ QWidget* parent, const QDir& gameDirectory,
+ const QFileInfo &binary, const QString &steamAppID, const Settings& settings)
+{
+ if (!steamAppID.isEmpty()) {
+ ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(steamAppID).c_str());
+ } else {
+ ::SetEnvironmentVariableW(L"SteamAPPId",
+ ToWString(settings.steam().appID()).c_str());
+ }
+
+ if ((QFileInfo(gameDirectory.absoluteFilePath("steam_api.dll")).exists() ||
+ QFileInfo(gameDirectory.absoluteFilePath("steam_api64.dll")).exists())
+ && (settings.game().loadMechanismType() == LoadMechanism::LOAD_MODORGANIZER)) {
+
+ bool steamFound = true;
+ bool steamAccess = true;
+ if (!testForSteam(&steamFound, &steamAccess)) {
+ log::error("unable to determine state of Steam");
+ }
+
+ if (!steamFound) {
+ QDialogButtonBox::StandardButton result;
+ result = QuestionBoxMemory::query(parent, "steamQuery", binary.fileName(),
+ QObject::tr("Start Steam?"),
+ QObject::tr("Steam is required to be running already to correctly start the game. "
+ "Should MO try to start steam now?"),
+ QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
+ if (result == QDialogButtonBox::Yes) {
+ startSteam(parent);
+
+ // double-check that Steam is started and MO has access
+ steamFound = true;
+ steamAccess = true;
+ if (!testForSteam(&steamFound, &steamAccess)) {
+ log::error("unable to determine state of Steam");
+ } else if (!steamFound) {
+ log::error("could not find Steam");
+ }
+
+ } else if (result == QDialogButtonBox::Cancel) {
+ return false;
+ }
+ }
+
+ if (!steamAccess) {
+ QDialogButtonBox::StandardButton result;
+ result = QuestionBoxMemory::query(parent, "steamAdminQuery", binary.fileName(),
+ QObject::tr("Steam: Access Denied"),
+ QObject::tr("MO was denied access to the Steam process. This normally indicates that "
+ "Steam is being run as administrator while MO is not. This can cause issues "
+ "launching the game. It is recommended to not run Steam as administrator unless "
+ "absolutely necessary.\n\n"
+ "Restart MO as administrator?"),
+ QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
+ if (result == QDialogButtonBox::Yes) {
+ WCHAR cwd[MAX_PATH];
+ if (!GetCurrentDirectory(MAX_PATH, cwd)) {
+ log::error("unable to get current directory (error {})", GetLastError());
+ cwd[0] = L'\0';
+ }
+ if (!Helper::adminLaunch(
+ qApp->applicationDirPath().toStdWString(),
+ qApp->applicationFilePath().toStdWString(),
+ std::wstring(cwd))) {
+ log::error("unable to relaunch MO as admin");
+ return false;
+ }
+ qApp->exit(0);
+ return false;
+ } else if (result == QDialogButtonBox::Cancel) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool checkService()
+{
+ SC_HANDLE serviceManagerHandle = NULL;
+ SC_HANDLE serviceHandle = NULL;
+ LPSERVICE_STATUS_PROCESS serviceStatus = NULL;
+ LPQUERY_SERVICE_CONFIG serviceConfig = NULL;
+ bool serviceRunning = true;
+
+ DWORD bytesNeeded;
+
+ try {
+ serviceManagerHandle = OpenSCManager(NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
+ if (!serviceManagerHandle) {
+ log::warn("failed to open service manager (query status) (error {})", GetLastError());
+ throw 1;
+ }
+
+ serviceHandle = OpenService(serviceManagerHandle, L"EventLog", SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
+ if (!serviceHandle) {
+ log::warn("failed to open EventLog service (query status) (error {})", GetLastError());
+ throw 2;
+ }
+
+ if (QueryServiceConfig(serviceHandle, NULL, 0, &bytesNeeded)
+ || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
+ log::warn("failed to get size of service config (error {})", GetLastError());
+ throw 3;
+ }
+
+ DWORD serviceConfigSize = bytesNeeded;
+ serviceConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, serviceConfigSize);
+ if (!QueryServiceConfig(serviceHandle, serviceConfig, serviceConfigSize, &bytesNeeded)) {
+ log::warn("failed to query service config (error {})", GetLastError());
+ throw 4;
+ }
+
+ if (serviceConfig->dwStartType == SERVICE_DISABLED) {
+ log::error("Windows Event Log service is disabled!");
+ serviceRunning = false;
+ }
+
+ if (QueryServiceStatusEx(serviceHandle, SC_STATUS_PROCESS_INFO, NULL, 0, &bytesNeeded)
+ || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
+ log::warn("failed to get size of service status (error {})", GetLastError());
+ throw 5;
+ }
+
+ DWORD serviceStatusSize = bytesNeeded;
+ serviceStatus = (LPSERVICE_STATUS_PROCESS)LocalAlloc(LMEM_FIXED, serviceStatusSize);
+ if (!QueryServiceStatusEx(serviceHandle, SC_STATUS_PROCESS_INFO, (LPBYTE)serviceStatus, serviceStatusSize, &bytesNeeded)) {
+ log::warn("failed to query service status (error {})", GetLastError());
+ throw 6;
+ }
+
+ if (serviceStatus->dwCurrentState != SERVICE_RUNNING) {
+ log::error("Windows Event Log service is not running");
+ serviceRunning = false;
+ }
+ }
+ catch (int) {
+ serviceRunning = false;
+ }
+
+ if (serviceStatus) {
+ LocalFree(serviceStatus);
+ }
+ if (serviceConfig) {
+ LocalFree(serviceConfig);
+ }
+ if (serviceHandle) {
+ CloseServiceHandle(serviceHandle);
+ }
+ if (serviceManagerHandle) {
+ CloseServiceHandle(serviceManagerHandle);
+ }
+
+ return serviceRunning;
+}
+
+bool checkEnvironment(QWidget* parent, const QFileInfo& binary)
+{
+ // Check if the Windows Event Logging service is running. For some reason, this seems to be
+ // critical to the successful running of usvfs.
+ if (!checkService()) {
+ if (QuestionBoxMemory::query(parent, QString("eventLogService"), binary.fileName(),
+ QObject::tr("Windows Event Log Error"),
+ QObject::tr("The Windows Event Log service is disabled and/or not running. This prevents"
+ " USVFS from running properly. Your mods may not be working in the executable"
+ " that you are launching. Note that you may have to restart MO and/or your PC"
+ " after the service is fixed.\n\nContinue launching %1?").arg(binary.fileName()),
+ QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool checkBlacklist(QWidget* parent, const Settings& settings, const QFileInfo& binary)
+{
+ for (auto exec : settings.executablesBlacklist().split(";")) {
+ if (exec.compare(binary.fileName(), Qt::CaseInsensitive) == 0) {
+ if (QuestionBoxMemory::query(parent, QString("blacklistedExecutable"), binary.fileName(),
+ QObject::tr("Blacklisted Executable"),
+ QObject::tr("The executable you are attempted to launch is blacklisted in the virtual file"
+ " system. This will likely prevent the executable, and any executables that are"
+ " launched by this one, from seeing any mods. This could extend to INI files, save"
+ " games and any other virtualized files.\n\nContinue launching %1?").arg(binary.fileName()),
+ QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
HANDLE startBinary(
const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory,
bool hooked, HANDLE stdOut, HANDLE stdErr)
@@ -382,3 +686,5 @@ HANDLE startBinary(
}
}
}
+
+} // namespace
\ No newline at end of file
diff --git a/src/spawn.h b/src/spawn.h
index 9a2dbfbd..9a5afb4a 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -26,6 +26,22 @@ along with Mod Organizer. If not, see .
#include
#include
+class Settings;
+
+namespace spawn
+{
+
+bool checkBinary(const QFileInfo& binary);
+
+bool checkSteam(
+ QWidget* parent, const QDir& gameDirectory,
+ const QFileInfo &binary, const QString &steamAppID, const Settings& settings);
+
+bool checkEnvironment(QWidget* parent, const QFileInfo& binary);
+
+bool checkBlacklist(
+ QWidget* parent, const Settings& settings, const QFileInfo& binary);
+
/**
* @brief spawn a binary with Mod Organizer injected
*
@@ -45,5 +61,7 @@ HANDLE startBinary(const QFileInfo &binary, const QString &arguments,
const QDir ¤tDirectory, bool hooked,
HANDLE stdOut = INVALID_HANDLE_VALUE, HANDLE stdErr = INVALID_HANDLE_VALUE);
+} // namespace
+
#endif // SPAWN_H
--
cgit v1.3.1
From bf3d7527801bcba16ba01735efd3d5acc052f485 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 12 Sep 2019 01:48:48 -0400
Subject: made SpawnParameters public, changed to Qt types removed 'suspended',
not used
---
src/mainwindow.cpp | 12 ++++---
src/organizercore.cpp | 24 ++++++++++----
src/spawn.cpp | 92 ++++++++++++++++++---------------------------------
src/spawn.h | 46 +++++++++++++++-----------
4 files changed, 83 insertions(+), 91 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 6737e330..7774d87d 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -6434,11 +6434,13 @@ void MainWindow::on_bossButton_clicked()
return;
}
- HANDLE loot = spawn::startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"),
- parameters.join(" "),
- qApp->applicationDirPath() + "/loot",
- true,
- stdOutWrite);
+ spawn::SpawnParameters sp;
+ sp.binary = QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe");
+ sp.arguments = parameters.join(" ");
+ sp.currentDirectory.setPath(qApp->applicationDirPath() + "/loot");
+ sp.stdOut = stdOutWrite;
+
+ HANDLE loot = spawn::startBinary(this, sp);
// we don't use the write end
::CloseHandle(stdOutWrite);
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 9403b47d..dd4edbce 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -1282,6 +1282,13 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
const QString &customOverwrite,
const QList &forcedLibraries)
{
+ spawn::SpawnParameters sp;
+ sp.binary = binary;
+ sp.arguments = arguments;
+ sp.currentDirectory = currentDirectory;
+ sp.hooked = true;
+
+
prepareStart();
QWidget *window = qApp->activeWindow();
@@ -1290,11 +1297,11 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
}
- if (!spawn::checkBinary(binary)) {
+ if (!spawn::checkBinary(window, sp)) {
return INVALID_HANDLE_VALUE;
}
- if (!spawn::checkSteam(window, managedGame()->gameDirectory(), binary, steamAppID, m_Settings)) {
+ if (!spawn::checkSteam(window, sp, managedGame()->gameDirectory(), steamAppID, m_Settings)) {
return INVALID_HANDLE_VALUE;
}
@@ -1322,11 +1329,11 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
return INVALID_HANDLE_VALUE;
}
- if (!spawn::checkEnvironment(window, binary)) {
+ if (!spawn::checkEnvironment(window, sp)) {
return INVALID_HANDLE_VALUE;
}
- if (!spawn::checkBlacklist(window, m_Settings, binary)) {
+ if (!spawn::checkBlacklist(window, sp, m_Settings)) {
return INVALID_HANDLE_VALUE;
}
@@ -1364,11 +1371,14 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
log::debug("Spawning proxyed process <{}>", cmdline);
- return spawn::startBinary(QFileInfo(QCoreApplication::applicationFilePath()),
- cmdline, QCoreApplication::applicationDirPath(), true);
+ sp.binary = QFileInfo(QCoreApplication::applicationFilePath());
+ sp.arguments = cmdline;
+ sp.currentDirectory.setPath(QCoreApplication::applicationDirPath());
+
+ return spawn::startBinary(window, sp);
} else {
log::debug("Spawning direct process <{}, {}, {}>", binPath, arguments, cwdPath);
- return spawn::startBinary(binary, arguments, currentDirectory, true);
+ return spawn::startBinary(window, sp);
}
} else {
log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath());
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 4376e5bf..a56df23a 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -49,17 +49,6 @@ namespace spawn
namespace
{
-struct SpawnParameters
-{
- std::wstring binary;
- std::wstring arguments;
- std::wstring currentDirectory;
- bool suspended = false;
- bool hooked = false;
- HANDLE stdOut = INVALID_HANDLE_VALUE;
- HANDLE stdErr = INVALID_HANDLE_VALUE;
-};
-
std::wstring pathEnv()
{
@@ -103,12 +92,11 @@ DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHand
si.dwFlags |= STARTF_USESTDHANDLES;
}
- std::wstring commandLine;
+ const auto bin = QDir::toNativeSeparators(sp.binary.absoluteFilePath()).toStdWString();
+ std::wstring commandLine = L"\"" + bin + L"\"";
if (sp.arguments[0] != L'\0') {
- commandLine = L"\"" + sp.binary + L"\" " + sp.arguments;
- } else {
- commandLine = L"\"" + sp.binary + L"\"";
+ commandLine += L" " + sp.arguments.toStdWString();
}
QString moPath = QCoreApplication::applicationDirPath();
@@ -119,16 +107,18 @@ DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHand
PROCESS_INFORMATION pi;
BOOL success = FALSE;
+ const auto cwd = QDir::toNativeSeparators(sp.currentDirectory.absolutePath()).toStdWString();
+
if (sp.hooked) {
success = ::CreateProcessHooked(
nullptr, const_cast(commandLine.c_str()), nullptr, nullptr,
inheritHandles, CREATE_BREAKAWAY_FROM_JOB, nullptr,
- sp.currentDirectory.c_str(), &si, &pi);
+ cwd.c_str(), &si, &pi);
} else {
success = ::CreateProcess(
nullptr, const_cast(commandLine.c_str()), nullptr, nullptr,
inheritHandles, CREATE_BREAKAWAY_FROM_JOB, nullptr,
- sp.currentDirectory.c_str(), &si, &pi);
+ cwd.c_str(), &si, &pi);
}
const auto e = GetLastError();
@@ -164,11 +154,10 @@ std::wstring makeRightsDetails(const env::FileSecurity& fs)
std::wstring makeDetails(const SpawnParameters& sp, DWORD code)
{
- const QFileInfo bin(QString::fromStdWString(sp.binary));
std::wstring owner, rights;
- if (bin.isFile()) {
- const auto fs = env::getFileSecurity(bin.absoluteFilePath());
+ if (sp.binary.isFile()) {
+ const auto fs = env::getFileSecurity(sp.binary.absoluteFilePath());
if (fs.error.isEmpty()) {
owner = fs.owner.toStdWString();
@@ -182,8 +171,8 @@ std::wstring makeDetails(const SpawnParameters& sp, DWORD code)
rights = L"(file not found)";
}
- const bool cwdExists = (sp.currentDirectory.empty() ?
- true : QFileInfo(QString::fromStdWString(sp.currentDirectory)).isDir());
+ const bool cwdExists = (sp.currentDirectory.isEmpty() ?
+ true : sp.currentDirectory.exists());
const auto appDir = QCoreApplication::applicationDirPath();
const auto sep = QDir::separator();
@@ -214,21 +203,20 @@ std::wstring makeDetails(const SpawnParameters& sp, DWORD code)
L" . rights: {rights}\n"
L" . arguments: '{args}'\n"
L" . cwd: '{cwd}'{cwdexists}\n"
- L" . stdout: {stdout}, stderr: {stderr}, suspended: {susp}, hooked: {hooked}\n"
+ L" . stdout: {stdout}, stderr: {stderr}, hooked: {hooked}\n"
L" . usvfs x86:{x86_dll} x64:{x64_dll} proxy_x86:{x86_proxy} proxy_x64:{x64_proxy}\n"
L" . MO elevated: {elevated}",
fmt::arg(L"code", code),
fmt::arg(L"codename", errorCodeName(code)),
- fmt::arg(L"bin", sp.binary),
+ fmt::arg(L"bin", QDir::toNativeSeparators(sp.binary.absoluteFilePath()).toStdWString()),
fmt::arg(L"owner", owner),
fmt::arg(L"rights", rights),
fmt::arg(L"error", formatSystemMessage(code)),
- fmt::arg(L"args", sp.arguments),
- fmt::arg(L"cwd", sp.currentDirectory),
+ fmt::arg(L"args", sp.arguments.toStdWString()),
+ fmt::arg(L"cwd", QDir::toNativeSeparators(sp.currentDirectory.absolutePath()).toStdWString()),
fmt::arg(L"cwdexists", (cwdExists ? L"" : L" (not found)")),
fmt::arg(L"stdout", (sp.stdOut == INVALID_HANDLE_VALUE ? L"no" : L"yes")),
fmt::arg(L"stderr", (sp.stdErr == INVALID_HANDLE_VALUE ? L"no" : L"yes")),
- fmt::arg(L"susp", (sp.suspended ? L"yes" : L"no")),
fmt::arg(L"hooked", (sp.hooked ? L"yes" : L"no")),
fmt::arg(L"x86_dll", usvfs_x86_dll),
fmt::arg(L"x64_dll", usvfs_x64_dll),
@@ -242,12 +230,10 @@ void spawnFailed(const SpawnParameters& sp, DWORD code)
const auto details = QString::fromStdWString(makeDetails(sp, code));
log::error("{}", details);
- const auto binary = QFileInfo(QString::fromStdWString(sp.binary));
-
const auto title = QObject::tr("Cannot launch program");
const auto mainText = QObject::tr("Cannot start %1")
- .arg(binary.fileName());
+ .arg(sp.binary.fileName());
QString content;
@@ -286,12 +272,10 @@ bool confirmRestartAsAdmin(const SpawnParameters& sp)
log::error("{}", details);
- const auto binary = QFileInfo(QString::fromStdWString(sp.binary));
-
const auto title = QObject::tr("Elevation required");
const auto mainText = QObject::tr("Cannot start %1")
- .arg(binary.fileName());
+ .arg(sp.binary.fileName());
const auto content = QObject::tr(
"This program is requesting to run as administrator but Mod Organizer "
@@ -350,12 +334,12 @@ void startBinaryAdmin(const SpawnParameters& sp)
} // namespace
-bool checkBinary(const QFileInfo& binary)
+bool checkBinary(QWidget* parent, const SpawnParameters& sp)
{
- if (!binary.exists()) {
+ if (!sp.binary.exists()) {
reportError(
QObject::tr("Executable not found: %1")
- .arg(qUtf8Printable(binary.absoluteFilePath())));
+ .arg(sp.binary.absoluteFilePath()));
return false;
}
@@ -452,8 +436,8 @@ void startSteam(QWidget *widget)
}
bool checkSteam(
- QWidget* parent, const QDir& gameDirectory,
- const QFileInfo &binary, const QString &steamAppID, const Settings& settings)
+ QWidget* parent, const SpawnParameters& sp,
+ const QDir& gameDirectory, const QString &steamAppID, const Settings& settings)
{
if (!steamAppID.isEmpty()) {
::SetEnvironmentVariableW(L"SteamAPPId", ToWString(steamAppID).c_str());
@@ -474,7 +458,7 @@ bool checkSteam(
if (!steamFound) {
QDialogButtonBox::StandardButton result;
- result = QuestionBoxMemory::query(parent, "steamQuery", binary.fileName(),
+ result = QuestionBoxMemory::query(parent, "steamQuery", sp.binary.fileName(),
QObject::tr("Start Steam?"),
QObject::tr("Steam is required to be running already to correctly start the game. "
"Should MO try to start steam now?"),
@@ -498,7 +482,7 @@ bool checkSteam(
if (!steamAccess) {
QDialogButtonBox::StandardButton result;
- result = QuestionBoxMemory::query(parent, "steamAdminQuery", binary.fileName(),
+ result = QuestionBoxMemory::query(parent, "steamAdminQuery", sp.binary.fileName(),
QObject::tr("Steam: Access Denied"),
QObject::tr("MO was denied access to the Steam process. This normally indicates that "
"Steam is being run as administrator while MO is not. This can cause issues "
@@ -609,17 +593,17 @@ bool checkService()
return serviceRunning;
}
-bool checkEnvironment(QWidget* parent, const QFileInfo& binary)
+bool checkEnvironment(QWidget* parent, const SpawnParameters& sp)
{
// Check if the Windows Event Logging service is running. For some reason, this seems to be
// critical to the successful running of usvfs.
if (!checkService()) {
- if (QuestionBoxMemory::query(parent, QString("eventLogService"), binary.fileName(),
+ if (QuestionBoxMemory::query(parent, QString("eventLogService"), sp.binary.fileName(),
QObject::tr("Windows Event Log Error"),
QObject::tr("The Windows Event Log service is disabled and/or not running. This prevents"
" USVFS from running properly. Your mods may not be working in the executable"
" that you are launching. Note that you may have to restart MO and/or your PC"
- " after the service is fixed.\n\nContinue launching %1?").arg(binary.fileName()),
+ " after the service is fixed.\n\nContinue launching %1?").arg(sp.binary.fileName()),
QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) {
return false;
}
@@ -628,16 +612,16 @@ bool checkEnvironment(QWidget* parent, const QFileInfo& binary)
return true;
}
-bool checkBlacklist(QWidget* parent, const Settings& settings, const QFileInfo& binary)
+bool checkBlacklist(QWidget* parent, const SpawnParameters& sp, const Settings& settings)
{
for (auto exec : settings.executablesBlacklist().split(";")) {
- if (exec.compare(binary.fileName(), Qt::CaseInsensitive) == 0) {
- if (QuestionBoxMemory::query(parent, QString("blacklistedExecutable"), binary.fileName(),
+ if (exec.compare(sp.binary.fileName(), Qt::CaseInsensitive) == 0) {
+ if (QuestionBoxMemory::query(parent, QString("blacklistedExecutable"), sp.binary.fileName(),
QObject::tr("Blacklisted Executable"),
QObject::tr("The executable you are attempted to launch is blacklisted in the virtual file"
" system. This will likely prevent the executable, and any executables that are"
" launched by this one, from seeing any mods. This could extend to INI files, save"
- " games and any other virtualized files.\n\nContinue launching %1?").arg(binary.fileName()),
+ " games and any other virtualized files.\n\nContinue launching %1?").arg(sp.binary.fileName()),
QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) {
return false;
}
@@ -648,20 +632,8 @@ bool checkBlacklist(QWidget* parent, const Settings& settings, const QFileInfo&
}
-HANDLE startBinary(
- const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory,
- bool hooked, HANDLE stdOut, HANDLE stdErr)
+HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
{
- SpawnParameters sp;
-
- sp.binary = QDir::toNativeSeparators(binary.absoluteFilePath()).toStdWString();
- sp.arguments = arguments.toStdWString();
- sp.currentDirectory = QDir::toNativeSeparators(currentDirectory.absolutePath()).toStdWString();
- sp.suspended = true;
- sp.hooked = hooked;
- sp.stdOut = stdOut;
- sp.stdErr = stdErr;
-
HANDLE processHandle, threadHandle;
const auto e = spawn(sp, processHandle, threadHandle);
diff --git a/src/spawn.h b/src/spawn.h
index 9a5afb4a..9398b6cc 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -31,20 +31,7 @@ class Settings;
namespace spawn
{
-bool checkBinary(const QFileInfo& binary);
-
-bool checkSteam(
- QWidget* parent, const QDir& gameDirectory,
- const QFileInfo &binary, const QString &steamAppID, const Settings& settings);
-
-bool checkEnvironment(QWidget* parent, const QFileInfo& binary);
-
-bool checkBlacklist(
- QWidget* parent, const Settings& settings, const QFileInfo& binary);
-
-/**
- * @brief spawn a binary with Mod Organizer injected
- *
+/*
* @param binary the binary to spawn
* @param arguments arguments to pass to the binary
* @param profileName name of the active profile
@@ -53,13 +40,34 @@ bool checkBlacklist(
* @param hooked if set, the binary is started with mo injected
* @param stdout if not equal to INVALID_HANDLE_VALUE, this is used as stdout for the process
* @param stderr if not equal to INVALID_HANDLE_VALUE, this is used as stderr for the process
+*/
+struct SpawnParameters
+{
+ QFileInfo binary;
+ QString arguments;
+ QDir currentDirectory;
+ bool hooked = false;
+ HANDLE stdOut = INVALID_HANDLE_VALUE;
+ HANDLE stdErr = INVALID_HANDLE_VALUE;
+};
+
+
+bool checkBinary(QWidget* parent, const SpawnParameters& sp);
+
+bool checkSteam(
+ QWidget* parent, const SpawnParameters& sp,
+ const QDir& gameDirectory, const QString &steamAppID, const Settings& settings);
+
+bool checkEnvironment(QWidget* parent, const SpawnParameters& sp);
+
+bool checkBlacklist(
+ QWidget* parent, const SpawnParameters& sp, const Settings& settings);
+
+/**
+ * @brief spawn a binary with Mod Organizer injected
* @return the process handle
- * @todo is the profile name even used any more?
- * @todo is the hooked parameter used?
**/
-HANDLE startBinary(const QFileInfo &binary, const QString &arguments,
- const QDir ¤tDirectory, bool hooked,
- HANDLE stdOut = INVALID_HANDLE_VALUE, HANDLE stdErr = INVALID_HANDLE_VALUE);
+HANDLE startBinary(QWidget* parent, const SpawnParameters& sp);
} // namespace
--
cgit v1.3.1
From c603681115b6071f241f6931685d36a92b6403f8 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 10:07:52 -0400
Subject: moved helper stuff to spawn so it can reuse error handling removed
unused helper::init() removed logging when deleting a credential that doesn't
exist, happens all the time
---
src/envsecurity.cpp | 2 +-
src/helper.cpp | 107 +---------------------------
src/helper.h | 42 +----------
src/settingsdialogworkarounds.cpp | 2 +-
src/settingsutilities.cpp | 18 ++---
src/spawn.cpp | 145 +++++++++++++++++++++++++++++++++++---
src/spawn.h | 25 +++++++
7 files changed, 175 insertions(+), 166 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/envsecurity.cpp b/src/envsecurity.cpp
index 6e3fadbe..3b4cdcaa 100644
--- a/src/envsecurity.cpp
+++ b/src/envsecurity.cpp
@@ -275,7 +275,7 @@ std::vector getSecurityProductsFromWMI()
}
if (prop.vt != VT_UI4 && prop.vt != VT_I4) {
- log::error("productState is a {}, is not a VT_UI4", prop.vt);
+ log::error("productState is a {}, not a VT_UI4", prop.vt);
return;
}
diff --git a/src/helper.cpp b/src/helper.cpp
index 59a2d3d1..24446cb8 100644
--- a/src/helper.cpp
+++ b/src/helper.cpp
@@ -17,109 +17,4 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see .
*/
-#include "helper.h"
-#include "utility.h"
-#include
-#include
-
-#define WIN32_LEAN_AND_MEAN
-#include
-
-#include
-#include
-
-using MOBase::reportError;
-
-
-namespace Helper {
-
-
-static bool helperExec(LPCWSTR moDirectory, LPCWSTR commandLine, BOOL async)
-{
- wchar_t fileName[MAX_PATH];
- _snwprintf(fileName, MAX_PATH, L"%ls\\helper.exe", moDirectory);
-
- SHELLEXECUTEINFOW execInfo = {0};
-
- execInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
- execInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
- execInfo.hwnd = nullptr;
- execInfo.lpVerb = L"runas";
- execInfo.lpFile = fileName;
- execInfo.lpParameters = commandLine;
- execInfo.lpDirectory = moDirectory;
- execInfo.nShow = SW_SHOW;
-
- ::ShellExecuteExW(&execInfo);
-
- if (execInfo.hProcess == 0) {
- reportError(QObject::tr("helper failed"));
- return false;
- }
-
- if (async) {
- return true;
- }
-
- if (::WaitForSingleObject(execInfo.hProcess, INFINITE) != WAIT_OBJECT_0) {
- reportError(QObject::tr("helper failed"));
- return false;
- }
-
- DWORD exitCode;
- GetExitCodeProcess(execInfo.hProcess, &exitCode);
- return exitCode == NOERROR;
-}
-
-
-bool init(const std::wstring &moPath, const std::wstring &dataPath)
-{
- DWORD userNameLen = UNLEN + 1;
- wchar_t userName[UNLEN + 1];
-
- if (!GetUserName(userName, &userNameLen)) {
- reportError(QObject::tr("failed to determine account name"));
- return false;
- }
- wchar_t *commandLine = new wchar_t[32768];
-
- _snwprintf(commandLine, 32768, L"init \"%ls\" \"%ls\"",
- dataPath.c_str(), userName);
-
- bool res = helperExec(moPath.c_str(), commandLine, FALSE);
- delete [] commandLine;
-
- return res;
-}
-
-
-bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath)
-{
- wchar_t *commandLine = new wchar_t[32768];
- _snwprintf(commandLine, 32768, L"backdateBSA \"%ls\"",
- dataPath.c_str());
-
- bool res = helperExec(moPath.c_str(), commandLine, FALSE);
- delete [] commandLine;
-
- return res;
-}
-
-
-bool adminLaunch(const std::wstring &moPath, const std::wstring &moFile, const std::wstring &workingDir)
-{
- wchar_t *commandLine = new wchar_t[32768];
- _snwprintf(commandLine, 32768, L"adminLaunch %d \"%ls\" \"%ls\"",
- ::GetCurrentProcessId(),
- moFile.c_str(),
- workingDir.c_str()
- );
-
- bool res = helperExec(moPath.c_str(), commandLine, TRUE);
- delete [] commandLine;
-
- return res;
-}
-
-
-} // namespace
+// moved to spawn.cpp
diff --git a/src/helper.h b/src/helper.h
index f6667a84..335b8647 100644
--- a/src/helper.h
+++ b/src/helper.h
@@ -20,45 +20,7 @@ along with Mod Organizer. If not, see .
#ifndef HELPER_H
#define HELPER_H
-
-#include
-
-
-/**
- * @brief Convenience functions to work with the external helper program.
- *
- * The mo_helper program is used to make changes on the system that require administrative
- * rights, so that ModOrganizer itself can run without special privileges
- **/
-namespace Helper {
-
-/**
- * @brief initialise the specified directory for use with mod organizer.
- *
- * This will create all required sub-directories and give the user running ModOrganizer
- * write-access
- *
- * @param moPath absolute path to the ModOrganizer base directory
- * @return true on success
- **/
-bool init(const std::wstring &moPath, const std::wstring &dataPath);
-
-/**
- * @brief sets the last modified time for all .bsa-files in the target directory well into the past
- * @param moPath absolute path to the modOrganizer base directory
- * @param dataPath the path taht contains the .bsa-files, usually the data directory of the game
- **/
-bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath);
-
-/**
- * @brief waits for the current process to exit and restarts it as an administrator
- * @param moPath absolute path to the modOrganizer base directory
- * @param moFile file name of modOrganizer
- * @param workingDir current working directory
- **/
-bool adminLaunch(const std::wstring &moPath, const std::wstring &moFile, const std::wstring &workingDir);
-
-}
-
+// all helper code moved to spawn.h and spawn.cpp
+#include "spawn.h"
#endif // HELPER_H
diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp
index 4d811e40..f89b021c 100644
--- a/src/settingsdialogworkarounds.cpp
+++ b/src/settingsdialogworkarounds.cpp
@@ -83,7 +83,7 @@ void WorkaroundsSettingsTab::on_bsaDateBtn_clicked()
const auto* game = qApp->property("managed_game").value();
QDir dir = game->dataDirectory();
- Helper::backdateBSAs(qApp->applicationDirPath().toStdWString(),
+ helper::backdateBSAs(qApp->applicationDirPath().toStdWString(),
dir.absolutePath().toStdWString());
}
diff --git a/src/settingsutilities.cpp b/src/settingsutilities.cpp
index 6c99a602..db7c1818 100644
--- a/src/settingsutilities.cpp
+++ b/src/settingsutilities.cpp
@@ -224,16 +224,18 @@ bool deleteWindowsCredential(const QString& key)
if (!CredDeleteW(credName.toStdWString().c_str(), CRED_TYPE_GENERIC, 0)) {
const auto e = GetLastError();
+
+ // not an error if the key already doesn't exist, and don't log it because
+ // it happens all the time when the settings dialog is closed since it
+ // doesn't check first
if (e == ERROR_NOT_FOUND) {
- // not an error if the key already doesn't exist
- log::debug("can't delete windows credential {}, doesn't exist", credName);
return true;
- } else {
- log::error(
- "failed to delete windows credential {}, {}",
- credName, formatSystemMessage(e));
- return false;
}
+
+ log::error(
+ "failed to delete windows credential {}, {}",
+ credName, formatSystemMessage(e));
+ return false;
}
log::debug("deleted windows credential {}", credName);
@@ -269,7 +271,7 @@ bool addWindowsCredential(const QString& key, const QString& data)
return false;
}
- log::debug("added windows credential {}", credName);
+ log::debug("set windows credential {}", credName);
return true;
}
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 94737871..6c524681 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -64,7 +64,7 @@ std::wstring makeRightsDetails(const env::FileSecurity& fs)
return s;
}
-std::wstring makeDetails(const SpawnParameters& sp, DWORD code)
+QString makeDetails(const SpawnParameters& sp, DWORD code, const QString& more={})
{
std::wstring owner, rights;
@@ -109,7 +109,7 @@ std::wstring makeDetails(const SpawnParameters& sp, DWORD code)
}
std::wstring f =
- L"Error {code} {codename}: {error}\n"
+ L"Error {code} {codename}{more}: {error}\n"
L" . binary: '{bin}'\n"
L" . owner: {owner}\n"
L" . rights: {rights}\n"
@@ -122,9 +122,12 @@ std::wstring makeDetails(const SpawnParameters& sp, DWORD code)
f += L"\n . usvfs x86:{x86_dll} x64:{x64_dll} proxy_x86:{x86_proxy} proxy_x64:{x64_proxy}";
}
- return fmt::format(f,
+ const std::wstring wmore = (more.isEmpty() ? L"" : (", " + more).toStdWString());
+
+ const auto s = fmt::format(f,
fmt::arg(L"code", code),
fmt::arg(L"codename", errorCodeName(code)),
+ fmt::arg(L"more", wmore),
fmt::arg(L"bin", QDir::toNativeSeparators(sp.binary.absoluteFilePath()).toStdWString()),
fmt::arg(L"owner", owner),
fmt::arg(L"rights", rights),
@@ -140,6 +143,8 @@ std::wstring makeDetails(const SpawnParameters& sp, DWORD code)
fmt::arg(L"x86_proxy", usvfs_x86_proxy),
fmt::arg(L"x64_proxy", usvfs_x64_proxy),
fmt::arg(L"elevated", elevated));
+
+ return QString::fromStdWString(s);
}
QString makeContent(const SpawnParameters& sp, DWORD code)
@@ -198,7 +203,7 @@ QMessageBox::StandardButton startSteamFailed(
return MOBase::TaskDialog(parent, QObject::tr("Cannot start Steam"))
.main(QObject::tr("Cannot start Steam"))
.content(makeContent(sp, e))
- .details(QString::fromStdWString(details))
+ .details(details)
.button({
QObject::tr("Continue without starting Steam"),
QObject::tr("The program may fail to launch."),
@@ -211,7 +216,7 @@ QMessageBox::StandardButton startSteamFailed(
void spawnFailed(const SpawnParameters& sp, DWORD code)
{
- const auto details = QString::fromStdWString(makeDetails(sp, code));
+ const auto details = makeDetails(sp, code);
log::error("{}", details);
const auto title = QObject::tr("Cannot launch program");
@@ -231,10 +236,38 @@ void spawnFailed(const SpawnParameters& sp, DWORD code)
.exec();
}
+void helperFailed(
+ DWORD code, const QString& why, const std::wstring& binary,
+ const std::wstring& cwd, const std::wstring& args)
+{
+ SpawnParameters sp;
+ sp.binary = QString::fromStdWString(binary);
+ sp.currentDirectory.setPath(QString::fromStdWString(cwd));
+ sp.arguments = QString::fromStdWString(args);
+
+ const auto details = makeDetails(sp, code, "in " + why);
+ log::error("{}", details);
+
+ const auto title = QObject::tr("Cannot launch helper");
+
+ const auto mainText = QObject::tr("Cannot start %1")
+ .arg(sp.binary.fileName());
+
+ QWidget *window = qApp->activeWindow();
+ if ((window != nullptr) && (!window->isVisible())) {
+ window = nullptr;
+ }
+
+ MOBase::TaskDialog(window, title)
+ .main(mainText)
+ .content(makeContent(sp, code))
+ .details(details)
+ .exec();
+}
+
bool confirmRestartAsAdmin(const SpawnParameters& sp)
{
- const auto details = QString::fromStdWString(
- makeDetails(sp, ERROR_ELEVATION_REQUIRED));
+ const auto details = makeDetails(sp, ERROR_ELEVATION_REQUIRED);
log::error("{}", details);
@@ -370,18 +403,18 @@ bool restartAsAdmin()
cwd[0] = L'\0';
}
- if (!Helper::adminLaunch(
+ if (!helper::adminLaunch(
qApp->applicationDirPath().toStdWString(),
qApp->applicationFilePath().toStdWString(),
std::wstring(cwd)))
{
- // todo
log::error("admin launch failed");
return false;
}
log::debug("exiting MO");
qApp->exit(0);
+
return true;
}
@@ -728,4 +761,96 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
}
}
-} // namespace
\ No newline at end of file
+} // namespace
+
+
+
+namespace helper
+{
+
+bool helperExec(
+ const std::wstring& moDirectory, const std::wstring& commandLine, BOOL async)
+{
+ const std::wstring fileName = moDirectory + L"\\helper.exe";
+
+ env::HandlePtr process;
+
+ {
+ SHELLEXECUTEINFOW execInfo = {};
+
+ ULONG flags = SEE_MASK_FLAG_NO_UI ;
+ if (!async)
+ flags |= SEE_MASK_NOCLOSEPROCESS;
+
+ execInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
+ execInfo.fMask = flags;
+ execInfo.hwnd = 0;
+ execInfo.lpVerb = L"runas";
+ execInfo.lpFile = fileName.c_str();
+ execInfo.lpParameters = commandLine.c_str();
+ execInfo.lpDirectory = moDirectory.c_str();
+ execInfo.nShow = SW_SHOW;
+
+ if (!::ShellExecuteExW(&execInfo) && execInfo.hProcess == 0) {
+ const auto e = GetLastError();
+
+ spawn::dialogs::helperFailed(
+ e, "ShellExecuteExW()", fileName, moDirectory, commandLine);
+
+ return false;
+ }
+
+ if (async) {
+ return true;
+ }
+
+ process.reset(execInfo.hProcess);
+ }
+
+ const auto r = ::WaitForSingleObject(process.get(), INFINITE);
+
+ if (r != WAIT_OBJECT_0) {
+ // for WAIT_ABANDONED, the documentation doesn't mention that GetLastError()
+ // returns something meaningful, but code ERROR_ABANDONED_WAIT_0 exists, so
+ // use that instead
+ const auto code = (r == WAIT_ABANDONED ?
+ ERROR_ABANDONED_WAIT_0 : GetLastError());
+
+ spawn::dialogs::helperFailed(
+ code, "WaitForSingleObject()", fileName, moDirectory, commandLine);
+
+ return false;
+ }
+
+ DWORD exitCode = 0;
+ if (!GetExitCodeProcess(process.get(), &exitCode)) {
+ const auto e = GetLastError();
+
+ spawn::dialogs::helperFailed(
+ e, "GetExitCodeProcess()", fileName, moDirectory, commandLine);
+
+ return false;
+ }
+
+ return (exitCode == 0);
+}
+
+bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath)
+{
+ const std::wstring commandLine = fmt::format(
+ L"backdateBSA \"{}\"", dataPath);
+
+ return helperExec(moPath, commandLine, FALSE);
+}
+
+
+bool adminLaunch(const std::wstring &moPath, const std::wstring &moFile, const std::wstring &workingDir)
+{
+ const std::wstring commandLine = fmt::format(
+ L"adminLaunch {} \"{}\" \"{}\"",
+ ::GetCurrentProcessId(), moFile, workingDir);
+
+ return helperExec(moPath, commandLine, true);
+}
+
+} // namespace
diff --git a/src/spawn.h b/src/spawn.h
index 9398b6cc..da626329 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -71,5 +71,30 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp);
} // namespace
+
+// convenience functions to work with the external helper program, which is used
+// to make changes on the system that require administrative rights, so that
+// ModOrganizer itself can run without special privileges
+//
+namespace helper
+{
+
+/**
+* @brief sets the last modified time for all .bsa-files in the target directory well into the past
+* @param moPath absolute path to the modOrganizer base directory
+* @param dataPath the path taht contains the .bsa-files, usually the data directory of the game
+**/
+bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath);
+
+/**
+* @brief waits for the current process to exit and restarts it as an administrator
+* @param moPath absolute path to the modOrganizer base directory
+* @param moFile file name of modOrganizer
+* @param workingDir current working directory
+**/
+bool adminLaunch(const std::wstring &moPath, const std::wstring &moFile, const std::wstring &workingDir);
+
+} // namespace
+
#endif // SPAWN_H
--
cgit v1.3.1
From f92e2c376d36132a9676b30f0b08543f27a13064 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 16:27:41 -0400
Subject: TaskDialog for restarting as admin for steam added a parent widget
parameter to a bunch of places fixed paths still getting changed even if
folders can't be created made private the member variables that were
temporarily public during rework
---
src/settingsdialog.cpp | 44 ++++++++++------
src/settingsdialog.h | 20 +++----
src/settingsdialoggeneral.cpp | 7 ++-
src/settingsdialognexus.cpp | 14 ++---
src/settingsdialogpaths.cpp | 6 ++-
src/settingsdialogworkarounds.cpp | 6 ++-
src/spawn.cpp | 106 ++++++++++++++++++++------------------
src/spawn.h | 7 ++-
8 files changed, 121 insertions(+), 89 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
index 1d3d4a39..8fb25b1c 100644
--- a/src/settingsdialog.cpp
+++ b/src/settingsdialog.cpp
@@ -33,8 +33,8 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& setti
: TutorableDialog("SettingsDialog", parent)
, ui(new Ui::SettingsDialog)
, m_settings(settings)
- , m_PluginContainer(pluginContainer)
- , m_keyChanged(false)
+ , m_pluginContainer(pluginContainer)
+ , m_restartNeeded(false)
{
ui->setupUi(this);
@@ -47,6 +47,25 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& setti
m_tabs.push_back(std::unique_ptr(new WorkaroundsSettingsTab(settings, *this)));
}
+PluginContainer* SettingsDialog::pluginContainer()
+{
+ return m_pluginContainer;
+}
+
+QWidget* SettingsDialog::parentWidgetForDialogs()
+{
+ if (isVisible()) {
+ return this;
+ } else {
+ return parentWidget();
+ }
+}
+
+void SettingsDialog::setRestartNeeded()
+{
+ m_restartNeeded = true;
+}
+
int SettingsDialog::exec()
{
GeometrySaver gs(m_settings, this);
@@ -68,13 +87,8 @@ int SettingsDialog::exec()
}
}
- bool restartNeeded = false;
- if (getApiKeyChanged()) {
- restartNeeded = true;
- }
-
- if (restartNeeded) {
- if (QMessageBox::question(nullptr,
+ if (m_restartNeeded) {
+ if (QMessageBox::question(parentWidgetForDialogs(),
tr("Restart Mod Organizer?"),
tr("In order to finish configuration changes, MO must be restarted.\n"
"Restart it now?"),
@@ -111,7 +125,7 @@ void SettingsDialog::accept()
QDir::fromNativeSeparators(
Settings::instance().paths().mods(true))) &&
(QMessageBox::question(
- nullptr, tr("Confirm"),
+ parentWidgetForDialogs(), tr("Confirm"),
tr("Changing the mod directory affects all your profiles! "
"Mods not present (or named differently) in the new location "
"will be disabled in all profiles. "
@@ -124,11 +138,6 @@ void SettingsDialog::accept()
TutorableDialog::accept();
}
-bool SettingsDialog::getApiKeyChanged()
-{
- return m_keyChanged;
-}
-
SettingsTab::SettingsTab(Settings& s, SettingsDialog& d)
: ui(d.ui), m_settings(s), m_dialog(d)
@@ -146,3 +155,8 @@ SettingsDialog& SettingsTab::dialog()
{
return m_dialog;
}
+
+QWidget* SettingsTab::parentWidget()
+{
+ return m_dialog.parentWidgetForDialogs();
+}
diff --git a/src/settingsdialog.h b/src/settingsdialog.h
index 6a99cb8d..e89da665 100644
--- a/src/settingsdialog.h
+++ b/src/settingsdialog.h
@@ -42,6 +42,7 @@ protected:
Settings& settings();
SettingsDialog& dialog();
+ QWidget* parentWidget();
private:
Settings& m_settings;
@@ -56,11 +57,12 @@ private:
**/
class SettingsDialog : public MOBase::TutorableDialog
{
- Q_OBJECT
+ Q_OBJECT;
+ friend class SettingsTab;
public:
explicit SettingsDialog(
- PluginContainer *pluginContainer, Settings& settings, QWidget *parent = 0);
+ PluginContainer* pluginContainer, Settings& settings, QWidget* parent = 0);
~SettingsDialog();
@@ -70,23 +72,21 @@ public:
*/
QString getColoredButtonStyleSheet() const;
- // temp
- Ui::SettingsDialog *ui;
- bool m_keyChanged;
- PluginContainer *m_PluginContainer;
+ PluginContainer* pluginContainer();
+ QWidget* parentWidgetForDialogs();
+ void setRestartNeeded();
int exec() override;
public slots:
virtual void accept();
-public:
- bool getApiKeyChanged();
-
private:
Settings& m_settings;
std::vector> m_tabs;
-
+ Ui::SettingsDialog* ui;
+ bool m_restartNeeded;
+ PluginContainer* m_pluginContainer;
};
#endif // SETTINGSDIALOG_H
diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp
index 3f7ece38..8ecdcbb9 100644
--- a/src/settingsdialoggeneral.cpp
+++ b/src/settingsdialoggeneral.cpp
@@ -238,8 +238,11 @@ void GeneralSettingsTab::on_resetColorsBtn_clicked()
void GeneralSettingsTab::on_resetDialogsButton_clicked()
{
- if (QMessageBox::question(&dialog(), QObject::tr("Confirm?"),
- QObject::tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"),
+ if (QMessageBox::question(
+ parentWidget(), QObject::tr("Confirm?"),
+ QObject::tr(
+ "This will reset all the choices you made to dialogs and make them all "
+ "visible again. Continue?"),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
resetDialogs();
}
diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp
index 0b08f13f..826075c0 100644
--- a/src/settingsdialognexus.cpp
+++ b/src/settingsdialognexus.cpp
@@ -226,7 +226,7 @@ void NexusSettingsTab::on_nexusDisconnect_clicked()
void NexusSettingsTab::on_clearCacheButton_clicked()
{
QDir(Settings::instance().paths().cache()).removeRecursively();
- NexusInterface::instance(dialog().m_PluginContainer)->clearCache();
+ NexusInterface::instance(dialog().pluginContainer())->clearCache();
}
void NexusSettingsTab::on_associateButton_clicked()
@@ -238,7 +238,7 @@ void NexusSettingsTab::validateKey(const QString& key)
{
if (!m_nexusValidator) {
m_nexusValidator.reset(new NexusKeyValidator(
- *NexusInterface::instance(dialog().m_PluginContainer)->getAccessManager()));
+ *NexusInterface::instance(dialog().pluginContainer())->getAccessManager()));
m_nexusValidator->stateChanged = [&](auto&& s, auto&& e){
onValidatorStateChanged(s, e);
@@ -294,7 +294,7 @@ void NexusSettingsTab::onValidatorStateChanged(
void NexusSettingsTab::onValidatorFinished(const APIUserAccount& user)
{
- NexusInterface::instance(dialog().m_PluginContainer)->setUserAccount(user);
+ NexusInterface::instance(dialog().pluginContainer())->setUserAccount(user);
if (!user.apiKey().isEmpty()) {
if (setKey(user.apiKey())) {
@@ -311,7 +311,7 @@ void NexusSettingsTab::addNexusLog(const QString& s)
bool NexusSettingsTab::setKey(const QString& key)
{
- dialog().m_keyChanged = true;
+ dialog().setRestartNeeded();
const bool ret = settings().nexus().setApiKey(key);
updateNexusState();
return ret;
@@ -319,10 +319,10 @@ bool NexusSettingsTab::setKey(const QString& key)
bool NexusSettingsTab::clearKey()
{
- dialog().m_keyChanged = true;
+ dialog().setRestartNeeded();
const auto ret = settings().nexus().clearApiKey();
- NexusInterface::instance(dialog().m_PluginContainer)->getAccessManager()->clearApiKey();
+ NexusInterface::instance(dialog().pluginContainer())->getAccessManager()->clearApiKey();
updateNexusState();
return ret;
@@ -371,7 +371,7 @@ void NexusSettingsTab::updateNexusButtons()
void NexusSettingsTab::updateNexusData()
{
- const auto user = NexusInterface::instance(dialog().m_PluginContainer)
+ const auto user = NexusInterface::instance(dialog().pluginContainer())
->getAPIUserAccount();
if (user.isValid()) {
diff --git a/src/settingsdialogpaths.cpp b/src/settingsdialogpaths.cpp
index aeb4dd5d..c6fd40a7 100644
--- a/src/settingsdialogpaths.cpp
+++ b/src/settingsdialogpaths.cpp
@@ -68,10 +68,12 @@ void PathsSettingsTab::update()
if (!QDir(realPath).exists()) {
if (!QDir().mkpath(realPath)) {
- QMessageBox::warning(qApp->activeWindow(), QObject::tr("Error"),
+ QMessageBox::warning(parentWidget(), QObject::tr("Error"),
QObject::tr("Failed to create \"%1\", you may not have the "
- "necessary permission. path remains unchanged.")
+ "necessary permissions. Path remains unchanged.")
.arg(realPath));
+
+ continue;
}
}
diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp
index ccbfcbfe..5e70e5a6 100644
--- a/src/settingsdialogworkarounds.cpp
+++ b/src/settingsdialogworkarounds.cpp
@@ -83,7 +83,9 @@ void WorkaroundsSettingsTab::on_bsaDateBtn_clicked()
const auto* game = qApp->property("managed_game").value();
QDir dir = game->dataDirectory();
- helper::backdateBSAs(qApp->applicationDirPath().toStdWString(),
+ helper::backdateBSAs(
+ parentWidget(),
+ qApp->applicationDirPath().toStdWString(),
dir.absolutePath().toStdWString());
}
@@ -95,7 +97,7 @@ void WorkaroundsSettingsTab::on_resetGeometryBtn_clicked()
"Restart now?");
const auto res = QMessageBox::question(
- nullptr, caption, text, QMessageBox::Yes | QMessageBox::Cancel);
+ parentWidget(), caption, text, QMessageBox::Yes | QMessageBox::Cancel);
if (res == QMessageBox::Yes) {
settings().geometry().requestReset();
diff --git a/src/spawn.cpp b/src/spawn.cpp
index e18e6bb3..a0cf9fb6 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -215,7 +215,7 @@ QMessageBox::StandardButton startSteamFailed(
.exec();
}
-void spawnFailed(const SpawnParameters& sp, DWORD code)
+void spawnFailed(QWidget* parent, const SpawnParameters& sp, DWORD code)
{
const auto details = makeDetails(sp, code);
log::error("{}", details);
@@ -225,12 +225,7 @@ void spawnFailed(const SpawnParameters& sp, DWORD code)
const auto mainText = QObject::tr("Cannot start %1")
.arg(sp.binary.fileName());
- QWidget *window = qApp->activeWindow();
- if ((window != nullptr) && (!window->isVisible())) {
- window = nullptr;
- }
-
- MOBase::TaskDialog(window, title)
+ MOBase::TaskDialog(parent, title)
.main(mainText)
.content(makeContent(sp, code))
.details(details)
@@ -239,7 +234,7 @@ void spawnFailed(const SpawnParameters& sp, DWORD code)
}
void helperFailed(
- DWORD code, const QString& why, const std::wstring& binary,
+ QWidget* parent, DWORD code, const QString& why, const std::wstring& binary,
const std::wstring& cwd, const std::wstring& args)
{
SpawnParameters sp;
@@ -255,12 +250,7 @@ void helperFailed(
const auto mainText = QObject::tr("Cannot start %1")
.arg(sp.binary.fileName());
- QWidget *window = qApp->activeWindow();
- if ((window != nullptr) && (!window->isVisible())) {
- window = nullptr;
- }
-
- MOBase::TaskDialog(window, title)
+ MOBase::TaskDialog(parent, title)
.main(mainText)
.content(makeContent(sp, code))
.details(details)
@@ -268,7 +258,7 @@ void helperFailed(
.exec();
}
-bool confirmRestartAsAdmin(const SpawnParameters& sp)
+bool confirmRestartAsAdmin(QWidget* parent, const SpawnParameters& sp)
{
const auto details = makeDetails(sp, ERROR_ELEVATION_REQUIRED);
@@ -287,15 +277,9 @@ bool confirmRestartAsAdmin(const SpawnParameters& sp)
"You can restart Mod Organizer as administrator and try launching the "
"program again.");
-
- QWidget *window = qApp->activeWindow();
- if ((window != nullptr) && (!window->isVisible())) {
- window = nullptr;
- }
-
log::debug("asking user to restart MO as administrator");
- const auto r = MOBase::TaskDialog(window, title)
+ const auto r = MOBase::TaskDialog(parent, title)
.main(mainText)
.content(content)
.details(details)
@@ -313,7 +297,7 @@ bool confirmRestartAsAdmin(const SpawnParameters& sp)
}
QMessageBox::StandardButton confirmStartSteam(
- QWidget* window, const SpawnParameters& sp, const QString& details)
+ QWidget* parent, const SpawnParameters& sp, const QString& details)
{
const auto title = QObject::tr("Launch Steam");
const auto mainText = QObject::tr("This program requires Steam");
@@ -321,7 +305,7 @@ QMessageBox::StandardButton confirmStartSteam(
"Mod Organizer has detected that this program likely requires Steam to be "
"running to function properly.");
- return MOBase::TaskDialog(window, title)
+ return MOBase::TaskDialog(parent, title)
.main(mainText)
.content(content)
.details(details)
@@ -340,17 +324,35 @@ QMessageBox::StandardButton confirmStartSteam(
.exec();
}
-QuestionBoxMemory::Button confirmRestartAsAdminForSteam(QWidget* parent, const SpawnParameters& sp)
+QMessageBox::StandardButton confirmRestartAsAdminForSteam(
+ QWidget* parent, const SpawnParameters& sp)
{
- return QuestionBoxMemory::query(
- parent, "steamAdminQuery", sp.binary.fileName(),
- QObject::tr("Steam: Access Denied"),
- QObject::tr("MO was denied access to the Steam process. This normally indicates that "
- "Steam is being run as administrator while MO is not. This can cause issues "
- "launching the game. It is recommended to not run Steam as administrator unless "
- "absolutely necessary.\n\n"
- "Restart MO as administrator?"),
- QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
+ const auto title = QObject::tr("Elevation required");
+ const auto mainText = QObject::tr("Steam is running as administrator");
+ const auto content = QObject::tr(
+ "Running Steam as administrator is typically unnecessary and can cause "
+ "problems when Mod Organizer is not running as administrato\r\n\r\n"
+ "You can restart Mod Organizer as administrator and try launching the "
+ "program again.");
+
+ return MOBase::TaskDialog(parent, title)
+ .main(mainText)
+ .content(content)
+ .details("")
+ .icon(QMessageBox::Question)
+ .button({
+ QObject::tr("Restart Mod Organizer as administrator"),
+ QObject::tr("You must allow \"helper.exe\" to make changes to the system."),
+ QMessageBox::Yes})
+ .button({
+ QObject::tr("Continue"),
+ QObject::tr("The program might fail to run."),
+ QMessageBox::No})
+ .button({
+ QObject::tr("Cancel"),
+ QMessageBox::Cancel})
+ .remember("steamAdminQuery", sp.binary.fileName())
+ .exec();
}
bool eventLogNotRunning(
@@ -447,7 +449,7 @@ DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHand
return ERROR_SUCCESS;
}
-bool restartAsAdmin()
+bool restartAsAdmin(QWidget* parent)
{
WCHAR cwd[MAX_PATH] = {};
if (!GetCurrentDirectory(MAX_PATH, cwd)) {
@@ -455,6 +457,7 @@ bool restartAsAdmin()
}
if (!helper::adminLaunch(
+ parent,
qApp->applicationDirPath().toStdWString(),
qApp->applicationFilePath().toStdWString(),
std::wstring(cwd)))
@@ -469,21 +472,21 @@ bool restartAsAdmin()
return true;
}
-void startBinaryAdmin(const SpawnParameters& sp)
+void startBinaryAdmin(QWidget* parent, const SpawnParameters& sp)
{
- if (!dialogs::confirmRestartAsAdmin(sp)) {
+ if (!dialogs::confirmRestartAsAdmin(parent, sp)) {
log::debug("user declined");
return;
}
log::info("restarting MO as administrator");
- restartAsAdmin();
+ restartAsAdmin(parent);
}
bool checkBinary(QWidget* parent, const SpawnParameters& sp)
{
if (!sp.binary.exists()) {
- dialogs::spawnFailed(sp, ERROR_FILE_NOT_FOUND);
+ dialogs::spawnFailed(parent, sp, ERROR_FILE_NOT_FOUND);
return false;
}
@@ -660,7 +663,7 @@ bool checkSteam(
const auto c = dialogs::confirmRestartAsAdminForSteam(parent, sp);
if (c == QDialogButtonBox::Yes) {
- restartAsAdmin();
+ restartAsAdmin(parent);
return false;
} else if (c == QDialogButtonBox::No) {
log::debug("user declined to restart MO, continuing");
@@ -720,13 +723,13 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
case ERROR_ELEVATION_REQUIRED:
{
- startBinaryAdmin(sp);
+ startBinaryAdmin(parent, sp);
return INVALID_HANDLE_VALUE;
}
default:
{
- dialogs::spawnFailed(sp, e);
+ dialogs::spawnFailed(parent, sp, e);
return INVALID_HANDLE_VALUE;
}
}
@@ -740,6 +743,7 @@ namespace helper
{
bool helperExec(
+ QWidget* parent,
const std::wstring& moDirectory, const std::wstring& commandLine, BOOL async)
{
const std::wstring fileName = moDirectory + L"\\helper.exe";
@@ -766,7 +770,7 @@ bool helperExec(
const auto e = GetLastError();
spawn::dialogs::helperFailed(
- e, "ShellExecuteExW()", fileName, moDirectory, commandLine);
+ parent, e, "ShellExecuteExW()", fileName, moDirectory, commandLine);
return false;
}
@@ -788,7 +792,8 @@ bool helperExec(
ERROR_ABANDONED_WAIT_0 : GetLastError());
spawn::dialogs::helperFailed(
- code, "WaitForSingleObject()", fileName, moDirectory, commandLine);
+ parent, code, "WaitForSingleObject()",
+ fileName, moDirectory, commandLine);
return false;
}
@@ -798,7 +803,7 @@ bool helperExec(
const auto e = GetLastError();
spawn::dialogs::helperFailed(
- e, "GetExitCodeProcess()", fileName, moDirectory, commandLine);
+ parent, e, "GetExitCodeProcess()", fileName, moDirectory, commandLine);
return false;
}
@@ -806,21 +811,24 @@ bool helperExec(
return (exitCode == 0);
}
-bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath)
+bool backdateBSAs(
+ QWidget* parent, const std::wstring &moPath, const std::wstring &dataPath)
{
const std::wstring commandLine = fmt::format(
L"backdateBSA \"{}\"", dataPath);
- return helperExec(moPath, commandLine, FALSE);
+ return helperExec(parent, moPath, commandLine, FALSE);
}
-bool adminLaunch(const std::wstring &moPath, const std::wstring &moFile, const std::wstring &workingDir)
+bool adminLaunch(
+ QWidget* parent, const std::wstring &moPath,
+ const std::wstring &moFile, const std::wstring &workingDir)
{
const std::wstring commandLine = fmt::format(
L"adminLaunch {} \"{}\" \"{}\"",
::GetCurrentProcessId(), moFile, workingDir);
- return helperExec(moPath, commandLine, true);
+ return helperExec(parent, moPath, commandLine, true);
}
} // namespace
diff --git a/src/spawn.h b/src/spawn.h
index da626329..9e1e2539 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -84,7 +84,8 @@ namespace helper
* @param moPath absolute path to the modOrganizer base directory
* @param dataPath the path taht contains the .bsa-files, usually the data directory of the game
**/
-bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath);
+bool backdateBSAs(
+ QWidget* parent, const std::wstring &moPath, const std::wstring &dataPath);
/**
* @brief waits for the current process to exit and restarts it as an administrator
@@ -92,7 +93,9 @@ bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath);
* @param moFile file name of modOrganizer
* @param workingDir current working directory
**/
-bool adminLaunch(const std::wstring &moPath, const std::wstring &moFile, const std::wstring &workingDir);
+bool adminLaunch(
+ QWidget* parent, const std::wstring &moPath,
+ const std::wstring &moFile, const std::wstring &workingDir);
} // namespace
--
cgit v1.3.1
From 54871e7b56a326f81e3de17f53604150d770793b Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 17:14:09 -0400
Subject: added details to blacklist dialog
---
src/spawn.cpp | 15 ++++++++++-----
src/spawn.h | 2 +-
2 files changed, 11 insertions(+), 6 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/spawn.cpp b/src/spawn.cpp
index ab9d90a0..d2c8ddd5 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -384,7 +384,7 @@ bool eventLogNotRunning(
}
QMessageBox::StandardButton confirmBlacklisted(
- QWidget* parent, const SpawnParameters& sp)
+ QWidget* parent, const SpawnParameters& sp, Settings& settings)
{
const auto title = QObject::tr("Blacklisted program");
const auto mainText = QObject::tr("The program %1 is blacklisted")
@@ -394,10 +394,14 @@ QMessageBox::StandardButton confirmBlacklisted(
"filesystem. This will likely prevent it from seeing any mods, INI files "
"or any other virtualized files.");
+ const auto details =
+ "Executable: " + sp.binary.fileName() + "\n"
+ "Current blacklist: " + settings.executablesBlacklist();
+
auto r = MOBase::TaskDialog(parent, title)
.main(mainText)
.content(content)
- .details("")
+ .details(details)
.icon(QMessageBox::Question)
.remember("blacklistedExecutable", sp.binary.fileName())
.button({
@@ -413,7 +417,7 @@ QMessageBox::StandardButton confirmBlacklisted(
.exec();
if (r == QMessageBox::Retry) {
- if (!WorkaroundsSettingsTab::changeBlacklistNow(parent, Settings::instance())) {
+ if (!WorkaroundsSettingsTab::changeBlacklistNow(parent, settings)) {
r = QMessageBox::Cancel;
}
}
@@ -739,14 +743,15 @@ bool checkEnvironment(QWidget* parent, const SpawnParameters& sp)
return dialogs::eventLogNotRunning(parent, s, sp);
}
-bool checkBlacklist(QWidget* parent, const SpawnParameters& sp, const Settings& settings)
+bool checkBlacklist(
+ QWidget* parent, const SpawnParameters& sp, Settings& settings)
{
for (;;) {
if (!settings.isExecutableBlacklisted(sp.binary.fileName())) {
return true;
}
- const auto r = dialogs::confirmBlacklisted(parent, sp);
+ const auto r = dialogs::confirmBlacklisted(parent, sp, settings);
if (r != QMessageBox::Retry) {
return (r == QMessageBox::Yes);
diff --git a/src/spawn.h b/src/spawn.h
index 9e1e2539..31b44739 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -61,7 +61,7 @@ bool checkSteam(
bool checkEnvironment(QWidget* parent, const SpawnParameters& sp);
bool checkBlacklist(
- QWidget* parent, const SpawnParameters& sp, const Settings& settings);
+ QWidget* parent, const SpawnParameters& sp, Settings& settings);
/**
* @brief spawn a binary with Mod Organizer injected
--
cgit v1.3.1
From d91580cc2669e2ab018c2aaab472cd763e5441d8 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Fri, 18 Oct 2019 15:39:07 -0400
Subject: initial Spawner and SpawnedProcess added steam app id to spawn
parameters removed threadHandle, unused moved most of the stuff from
OrganizerCore::spawnBinaryProcess() to Spawner replaced m_UserInterface by
m_MainWindow
---
src/mainwindow.cpp | 4 +-
src/organizercore.cpp | 192 ++++++++++++++++--------------------------------
src/organizercore.h | 9 ++-
src/plugincontainer.cpp | 1 +
src/spawn.cpp | 131 +++++++++++++++++++++++++++++++--
src/spawn.h | 34 +++++++++
6 files changed, 230 insertions(+), 141 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 12ed40b3..b5b9ab0d 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -411,7 +411,7 @@ MainWindow::MainWindow(Settings &settings
m_Tutorial.expose("modList", m_OrganizerCore.modList());
m_Tutorial.expose("espList", m_OrganizerCore.pluginList());
- m_OrganizerCore.setUserInterface(this, this);
+ m_OrganizerCore.setUserInterface(this);
for (const QString &fileName : m_PluginContainer.pluginFileNames()) {
installTranslator(QFileInfo(fileName).baseName());
}
@@ -595,7 +595,7 @@ MainWindow::~MainWindow()
cleanup();
m_PluginContainer.setUserInterface(nullptr, nullptr);
- m_OrganizerCore.setUserInterface(nullptr, nullptr);
+ m_OrganizerCore.setUserInterface(nullptr);
m_IntegratedBrowser.close();
delete ui;
} catch (std::exception &e) {
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 5613e8ce..3c986004 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -1,5 +1,5 @@
#include "organizercore.h"
-
+#include "mainwindow.h"
#include "delayedfilewriter.h"
#include "guessedvalue.h"
#include "imodinterface.h"
@@ -127,7 +127,7 @@ QStringList toStringList(InputIterator current, InputIterator end)
OrganizerCore::OrganizerCore(Settings &settings)
- : m_UserInterface(nullptr)
+ : m_MainWindow(nullptr)
, m_PluginContainer(nullptr)
, m_GameName()
, m_CurrentProfile(nullptr)
@@ -249,44 +249,43 @@ void OrganizerCore::updateExecutablesList()
m_PluginContainer, m_Settings.interface().displayForeign(), managedGame());
}
-void OrganizerCore::setUserInterface(IUserInterface *userInterface,
- QWidget *widget)
+void OrganizerCore::setUserInterface(MainWindow* mainWindow)
{
storeSettings();
- m_UserInterface = userInterface;
+ m_MainWindow = mainWindow;
- if (widget != nullptr) {
- connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), widget,
+ if (m_MainWindow != nullptr) {
+ connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), m_MainWindow,
SLOT(modlistChanged(QModelIndex, int)));
- connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), widget,
+ connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), m_MainWindow,
SLOT(modlistChanged(QModelIndexList, int)));
- connect(&m_ModList, SIGNAL(showMessage(QString)), widget,
+ connect(&m_ModList, SIGNAL(showMessage(QString)), m_MainWindow,
SLOT(showMessage(QString)));
- connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), widget,
+ connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), m_MainWindow,
SLOT(modRenamed(QString, QString)));
- connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget,
+ connect(&m_ModList, SIGNAL(modUninstalled(QString)), m_MainWindow,
SLOT(modRemoved(QString)));
- connect(&m_ModList, SIGNAL(removeSelectedMods()), widget,
+ connect(&m_ModList, SIGNAL(removeSelectedMods()), m_MainWindow,
SLOT(removeMod_clicked()));
- connect(&m_ModList, SIGNAL(clearOverwrite()), widget,
+ connect(&m_ModList, SIGNAL(clearOverwrite()), m_MainWindow,
SLOT(clearOverwrite()));
- connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget,
+ connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), m_MainWindow,
SLOT(displayColumnSelection(QPoint)));
- connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget,
+ connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), m_MainWindow,
SLOT(fileMoved(QString, QString, QString)));
- connect(&m_ModList, SIGNAL(modorder_changed()), widget,
+ connect(&m_ModList, SIGNAL(modorder_changed()), m_MainWindow,
SLOT(modorder_changed()));
- connect(&m_PluginList, SIGNAL(writePluginsList()), widget,
+ connect(&m_PluginList, SIGNAL(writePluginsList()), m_MainWindow,
SLOT(esplist_changed()));
- connect(&m_PluginList, SIGNAL(esplist_changed()), widget,
+ connect(&m_PluginList, SIGNAL(esplist_changed()), m_MainWindow,
SLOT(esplist_changed()));
- connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget,
+ connect(&m_DownloadManager, SIGNAL(showMessage(QString)), m_MainWindow,
SLOT(showMessage(QString)));
}
- m_InstallationManager.setParentWidget(widget);
- m_Updater.setUserInterface(widget);
+ m_InstallationManager.setParentWidget(m_MainWindow);
+ m_Updater.setUserInterface(m_MainWindow);
checkForUpdates();
}
@@ -295,7 +294,7 @@ void OrganizerCore::checkForUpdates()
{
// this currently wouldn't work reliably if the ui isn't initialized yet to
// display the result
- if (m_UserInterface != nullptr) {
+ if (m_MainWindow != nullptr) {
m_Updater.testForUpdate(m_Settings);
}
}
@@ -755,13 +754,13 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName,
int modIndex = ModInfo::getIndex(modName);
if (modIndex != UINT_MAX) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
- if (hasIniTweaks && (m_UserInterface != nullptr)
+ if (hasIniTweaks && (m_MainWindow != nullptr)
&& (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"),
tr("This mod contains ini tweaks. Do you "
"want to configure them now?"),
QMessageBox::Yes | QMessageBox::No)
== QMessageBox::Yes)) {
- m_UserInterface->displayModInformation(
+ m_MainWindow->displayModInformation(
modInfo, modIndex, ModInfoTabIDs::IniFiles);
}
m_ModInstalled(modName);
@@ -822,13 +821,13 @@ void OrganizerCore::installDownload(int index)
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
modInfo->addInstalledFile(modID, fileID);
- if (hasIniTweaks && m_UserInterface != nullptr
+ if (hasIniTweaks && m_MainWindow != nullptr
&& (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"),
tr("This mod contains ini tweaks. Do you "
"want to configure them now?"),
QMessageBox::Yes | QMessageBox::No)
== QMessageBox::Yes)) {
- m_UserInterface->displayModInformation(
+ m_MainWindow->displayModInformation(
modInfo, modIndex, ModInfoTabIDs::IniFiles);
}
@@ -1250,11 +1249,11 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
std::unique_ptr dlg;
ILockedWaitingForProcess* uilock = nullptr;
- if (m_UserInterface != nullptr) {
- uilock = m_UserInterface->lock();
+ if (m_MainWindow != nullptr) {
+ uilock = m_MainWindow->lock();
}
else {
- // i.e. when running command line shortcuts there is no m_UserInterface
+ // i.e. when running command line shortcuts there is no user interface
dlg.reset(new LockedDialog);
dlg->show();
dlg->setEnabled(true);
@@ -1262,8 +1261,8 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
}
ON_BLOCK_EXIT([&]() {
- if (m_UserInterface != nullptr) {
- m_UserInterface->unlock();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->unlock();
} });
DWORD ignoreExitCode;
@@ -1275,35 +1274,21 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
}
-HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
- const QString &arguments,
- const QString &profileName,
- const QDir ¤tDirectory,
- const QString &steamAppID,
- const QString &customOverwrite,
- const QList &forcedLibraries)
+HANDLE OrganizerCore::spawnBinaryProcess(
+ const QFileInfo &binary, const QString &arguments, const QString &profileName,
+ const QDir ¤tDirectory, const QString &steamAppID,
+ const QString &customOverwrite,
+ const QList &forcedLibraries)
{
spawn::SpawnParameters sp;
sp.binary = binary;
sp.arguments = arguments;
sp.currentDirectory = currentDirectory;
+ sp.steamAppID = steamAppID;
sp.hooked = true;
prepareStart();
- QWidget *window = qApp->activeWindow();
- if ((window != nullptr) && (!window->isVisible())) {
- window = nullptr;
- }
-
- if (!spawn::checkBinary(window, sp)) {
- return INVALID_HANDLE_VALUE;
- }
-
- if (!spawn::checkSteam(window, sp, managedGame()->gameDirectory(), steamAppID, m_Settings)) {
- return INVALID_HANDLE_VALUE;
- }
-
while (m_DirectoryUpdate) {
::Sleep(100);
QCoreApplication::processEvents();
@@ -1315,72 +1300,25 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
}
// TODO: should also pass arguments
- if (m_AboutToRun(binary.absoluteFilePath())) {
- try {
- m_USVFS.updateMapping(fileMapping(profileName, customOverwrite));
- m_USVFS.updateForcedLibraries(forcedLibraries);
-
- } catch (const UsvfsConnectorException &e) {
- log::debug(e.what());
- return INVALID_HANDLE_VALUE;
- } catch (const std::exception &e) {
- QMessageBox::warning(window, tr("Error"), e.what());
- return INVALID_HANDLE_VALUE;
- }
-
- if (!spawn::checkEnvironment(window, sp)) {
- return INVALID_HANDLE_VALUE;
- }
-
- if (!spawn::checkBlacklist(window, sp, m_Settings)) {
- return INVALID_HANDLE_VALUE;
- }
-
- QString modsPath = settings().paths().mods();
-
- // Check if this a request with either an executable or a working directory under our mods folder
- // then will start the process in a virtualized "environment" with the appropriate paths fixed:
- // (i.e. mods\FNIS\path\exe => game\data\path\exe)
- QString cwdPath = currentDirectory.absolutePath();
- bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive);
- QString binPath = binary.absoluteFilePath();
- bool virtualizedBin = binPath.startsWith(modsPath, Qt::CaseInsensitive);
- if (virtualizedCwd || virtualizedBin) {
- if (virtualizedCwd) {
- int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1);
- QString adjustedCwd = cwdPath.mid(cwdOffset, -1);
- cwdPath = m_GamePlugin->dataDirectory().absolutePath();
- if (cwdOffset >= 0)
- cwdPath += adjustedCwd;
-
- }
-
- if (virtualizedBin) {
- int binOffset = binPath.indexOf('/', modsPath.length() + 1);
- QString adjustedBin = binPath.mid(binOffset, -1);
- binPath = m_GamePlugin->dataDirectory().absolutePath();
- if (binOffset >= 0)
- binPath += adjustedBin;
- }
-
- QString cmdline
- = QString("launch \"%1\" \"%2\" %3")
- .arg(QDir::toNativeSeparators(cwdPath),
- QDir::toNativeSeparators(binPath), arguments);
+ if (!m_AboutToRun(binary.absoluteFilePath())) {
+ log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath());
+ return INVALID_HANDLE_VALUE;
+ }
- sp.binary = QFileInfo(QCoreApplication::applicationFilePath());
- sp.arguments = cmdline;
- sp.currentDirectory.setPath(QCoreApplication::applicationDirPath());
+ try {
+ m_USVFS.updateMapping(fileMapping(profileName, customOverwrite));
+ m_USVFS.updateForcedLibraries(forcedLibraries);
- return spawn::startBinary(window, sp);
- } else {
- log::debug("Spawning direct process <{}, {}, {}>", binPath, arguments, cwdPath);
- return spawn::startBinary(window, sp);
- }
- } else {
- log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath());
+ } catch (const UsvfsConnectorException &e) {
+ log::debug(e.what());
+ return INVALID_HANDLE_VALUE;
+ } catch (const std::exception &e) {
+ QMessageBox::warning(m_MainWindow, tr("Error"), e.what());
return INVALID_HANDLE_VALUE;
}
+
+ auto process = spawn::Spawner().spawn(m_MainWindow, m_GamePlugin, sp, m_Settings);
+ return process.releaseHandle();
}
HANDLE OrganizerCore::runShortcut(const MOShortcut& shortcut)
@@ -1498,13 +1436,13 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
return true;
ILockedWaitingForProcess* uilock = nullptr;
- if (m_UserInterface != nullptr) {
- uilock = m_UserInterface->lock();
+ if (m_MainWindow != nullptr) {
+ uilock = m_MainWindow->lock();
}
ON_BLOCK_EXIT([&] () {
- if (m_UserInterface != nullptr) {
- m_UserInterface->unlock();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->unlock();
} });
return waitForProcessCompletion(handle, exitCode, uilock);
}
@@ -1762,8 +1700,8 @@ void OrganizerCore::refreshBSAList()
m_ActiveArchives = m_DefaultArchives;
}
- if (m_UserInterface != nullptr) {
- m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives);
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->updateBSAList(m_DefaultArchives, m_ActiveArchives);
}
m_ArchivesInit = true;
@@ -1879,8 +1817,8 @@ void OrganizerCore::updateModsInDirectoryStructure(QMaparchivesWriter().writeImmediately(false);
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->archivesWriter().writeImmediately(false);
}
std::vector archives = enabledArchives();
@@ -2064,8 +2002,8 @@ void OrganizerCore::modStatusChanged(unsigned int index)
= m_DirectoryStructure->getOriginByName(ToWString(modInfo->name()));
origin.enable(false);
}
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->archivesWriter().write();
}
}
modInfo->clearCaches();
@@ -2114,8 +2052,8 @@ void OrganizerCore::modStatusChanged(QList index) {
origin.enable(false);
}
}
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->archivesWriter().write();
}
}
@@ -2290,8 +2228,8 @@ bool OrganizerCore::saveCurrentLists()
try {
savePluginList();
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->archivesWriter().write();
}
} catch (const std::exception &e) {
reportError(tr("failed to save load order: %1").arg(e.what()));
diff --git a/src/organizercore.h b/src/organizercore.h
index 0d0a092c..04c96ba6 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -3,7 +3,7 @@
#include "selfupdater.h"
-#include "iuserinterface.h" //should be class IUserInterface;
+#include "ilockedwaitingforprocess.h"
#include "settings.h"
#include "modlist.h"
#include "modinfo.h"
@@ -26,6 +26,8 @@
class ModListSortProxy;
class PluginListSortProxy;
class Profile;
+class MainWindow;
+
namespace MOBase {
template class GuessedValue;
class IModInterface;
@@ -101,7 +103,7 @@ public:
~OrganizerCore();
- void setUserInterface(IUserInterface *userInterface, QWidget *widget);
+ void setUserInterface(MainWindow* mainWindow);
void connectPlugins(PluginContainer *container);
void disconnectPlugins();
@@ -328,8 +330,7 @@ private:
static const unsigned int PROBLEM_MO1SCRIPTEXTENDERWORKAROUND = 1;
private:
-
- IUserInterface *m_UserInterface;
+ MainWindow* m_MainWindow;
PluginContainer *m_PluginContainer;
QString m_GameName;
MOBase::IPluginGame *m_GamePlugin;
diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp
index c0706ba8..767d3eb8 100644
--- a/src/plugincontainer.cpp
+++ b/src/plugincontainer.cpp
@@ -3,6 +3,7 @@
#include "organizerproxy.h"
#include "report.h"
#include
+#include
#include
#include
#include
diff --git a/src/spawn.cpp b/src/spawn.cpp
index a34230b2..3c7d64ce 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -27,6 +27,7 @@ along with Mod Organizer. If not, see .
#include "envmodule.h"
#include "settings.h"
#include "settingsdialogworkarounds.h"
+#include
#include
#include
#include
@@ -440,7 +441,7 @@ QMessageBox::StandardButton confirmBlacklisted(
namespace spawn
{
-DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHandle)
+DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle)
{
BOOL inheritHandles = FALSE;
@@ -494,7 +495,7 @@ DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHand
}
processHandle = pi.hProcess;
- threadHandle = pi.hThread;
+ ::CloseHandle(pi.hThread);
return ERROR_SUCCESS;
}
@@ -618,8 +619,7 @@ bool startSteam(QWidget* parent)
(password.isEmpty() ? "no" : "yes"));
HANDLE ph = INVALID_HANDLE_VALUE;
- HANDLE th = INVALID_HANDLE_VALUE;
- const auto e = spawn(sp, ph, th);
+ const auto e = spawn(sp, ph);
if (e != ERROR_SUCCESS) {
// make sure username and passwords are not shown
@@ -772,17 +772,59 @@ bool checkBlacklist(
}
+void adjustForVirtualized(
+ const IPluginGame* game, SpawnParameters& sp, const Settings& settings)
+{
+ const QString modsPath = settings.paths().mods();
+
+ // Check if this a request with either an executable or a working directory
+ // under our mods folder then will start the process in a virtualized
+ // "environment" with the appropriate paths fixed:
+ // (i.e. mods\FNIS\path\exe => game\data\path\exe)
+ QString cwdPath = sp.currentDirectory.absolutePath();
+ bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive);
+ QString binPath = sp.binary.absoluteFilePath();
+ bool virtualizedBin = binPath.startsWith(modsPath, Qt::CaseInsensitive);
+ if (virtualizedCwd || virtualizedBin) {
+ if (virtualizedCwd) {
+ int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1);
+ QString adjustedCwd = cwdPath.mid(cwdOffset, -1);
+ cwdPath = game->dataDirectory().absolutePath();
+ if (cwdOffset >= 0)
+ cwdPath += adjustedCwd;
+
+ }
+
+ if (virtualizedBin) {
+ int binOffset = binPath.indexOf('/', modsPath.length() + 1);
+ QString adjustedBin = binPath.mid(binOffset, -1);
+ binPath = game->dataDirectory().absolutePath();
+ if (binOffset >= 0)
+ binPath += adjustedBin;
+ }
+
+ QString cmdline
+ = QString("launch \"%1\" \"%2\" %3")
+ .arg(QDir::toNativeSeparators(cwdPath),
+ QDir::toNativeSeparators(binPath), sp.arguments);
+
+ sp.binary = QFileInfo(QCoreApplication::applicationFilePath());
+ sp.arguments = cmdline;
+ sp.currentDirectory.setPath(QCoreApplication::applicationDirPath());
+ }
+}
+
+
HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
{
- HANDLE processHandle, threadHandle;
- const auto e = spawn(sp, processHandle, threadHandle);
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ const auto e = spawn::spawn(sp, handle);
switch (e)
{
case ERROR_SUCCESS:
{
- ::CloseHandle(threadHandle);
- return processHandle;
+ return handle;
}
case ERROR_ELEVATION_REQUIRED:
@@ -799,6 +841,79 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
}
}
+
+
+SpawnedProcess::SpawnedProcess(HANDLE handle, SpawnParameters sp)
+ : m_handle(handle), m_parameters(std::move(sp))
+{
+}
+
+SpawnedProcess::SpawnedProcess(SpawnedProcess&& other)
+ : m_handle(other.m_handle), m_parameters(std::move(other.m_parameters))
+{
+ other.m_handle = INVALID_HANDLE_VALUE;
+}
+
+SpawnedProcess& SpawnedProcess::operator=(SpawnedProcess&& other)
+{
+ if (this != &other) {
+ destroy();
+
+ m_handle = other.m_handle;
+ other.m_handle = INVALID_HANDLE_VALUE;
+
+ m_parameters = std::move(other.m_parameters);
+ }
+
+ return *this;
+}
+
+SpawnedProcess::~SpawnedProcess()
+{
+ destroy();
+}
+
+HANDLE SpawnedProcess::releaseHandle()
+{
+ const auto h = m_handle;
+ m_handle = INVALID_HANDLE_VALUE;
+ return h;
+}
+
+void SpawnedProcess::destroy()
+{
+ if (m_handle != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(m_handle);
+ m_handle = INVALID_HANDLE_VALUE;
+ }
+}
+
+
+SpawnedProcess Spawner::spawn(
+ QWidget* parent, const IPluginGame* game,
+ SpawnParameters sp, Settings& settings)
+{
+ if (!checkBinary(parent, sp)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!checkSteam(parent, sp, game->gameDirectory(), sp.steamAppID, settings)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!spawn::checkEnvironment(parent, sp)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!spawn::checkBlacklist(parent, sp, settings)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ adjustForVirtualized(game, sp, settings);
+
+ return {startBinary(parent, sp), sp};
+}
+
} // namespace
diff --git a/src/spawn.h b/src/spawn.h
index 31b44739..d2853cd5 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -27,6 +27,7 @@ along with Mod Organizer. If not, see .
#include
class Settings;
+namespace MOBase { class IPluginGame; }
namespace spawn
{
@@ -46,6 +47,7 @@ struct SpawnParameters
QFileInfo binary;
QString arguments;
QDir currentDirectory;
+ QString steamAppID;
bool hooked = false;
HANDLE stdOut = INVALID_HANDLE_VALUE;
HANDLE stdErr = INVALID_HANDLE_VALUE;
@@ -69,6 +71,38 @@ bool checkBlacklist(
**/
HANDLE startBinary(QWidget* parent, const SpawnParameters& sp);
+
+class SpawnedProcess
+{
+public:
+ SpawnedProcess(HANDLE handle, SpawnParameters sp);
+
+ SpawnedProcess(const SpawnedProcess&) = delete;
+ SpawnedProcess& operator=(const SpawnedProcess&) = delete;
+ SpawnedProcess(SpawnedProcess&& other);
+ SpawnedProcess& operator=(SpawnedProcess&& other);
+ ~SpawnedProcess();
+
+ HANDLE releaseHandle();
+
+private:
+ HANDLE m_handle;
+ SpawnParameters m_parameters;
+
+ void destroy();
+};
+
+
+class Spawner
+{
+public:
+ SpawnedProcess spawn(
+ QWidget* parent, const MOBase::IPluginGame* game,
+ SpawnParameters sp, Settings& settings);
+
+private:
+};
+
} // namespace
--
cgit v1.3.1
From 4e8dcc5157706e1478396179f5dc11305532b159 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 24 Oct 2019 04:27:22 -0400
Subject: moved findJavaInstallation() and getFileExecutionContext() to spawn
fixed env::get() returning garbage after value
---
src/editexecutablesdialog.cpp | 3 +-
src/env.cpp | 31 +++--
src/mainwindow.cpp | 68 +++++------
src/organizercore.cpp | 264 +++++++++++++++---------------------------
src/organizercore.h | 12 --
src/spawn.cpp | 183 ++++++++++++++++++++++++++++-
src/spawn.h | 19 +++
7 files changed, 351 insertions(+), 229 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp
index 8535b7a7..32b31357 100644
--- a/src/editexecutablesdialog.cpp
+++ b/src/editexecutablesdialog.cpp
@@ -24,6 +24,7 @@ along with Mod Organizer. If not, see .
#include "modlist.h"
#include "forcedloaddialog.h"
#include "organizercore.h"
+#include "spawn.h"
#include
#include
@@ -800,7 +801,7 @@ QFileInfo EditExecutablesDialog::browseBinary(const QString& initial)
void EditExecutablesDialog::setJarBinary(const QFileInfo& binary)
{
- auto java = OrganizerCore::findJavaInstallation(binary.absoluteFilePath());
+ auto java = spawn::findJavaInstallation(binary.absoluteFilePath());
if (java.isEmpty()) {
QMessageBox::information(
diff --git a/src/env.cpp b/src/env.cpp
index 78b5dc96..507607d1 100644
--- a/src/env.cpp
+++ b/src/env.cpp
@@ -190,19 +190,36 @@ QString setPath(const QString& s)
QString get(const QString& name)
{
- std::wstring s(4000, L' ');
+ std::size_t bufferSize = 4000;
+ auto buffer = std::make_unique(bufferSize);
DWORD realSize = ::GetEnvironmentVariableW(
- name.toStdWString().c_str(), s.data(), static_cast(s.size()));
+ name.toStdWString().c_str(),
+ buffer.get(), static_cast(bufferSize));
- if (realSize > s.size()) {
- s.resize(realSize);
+ if (realSize > bufferSize) {
+ bufferSize = realSize;
+ buffer = std::make_unique(bufferSize);
- ::GetEnvironmentVariableW(
- name.toStdWString().c_str(), s.data(), static_cast(s.size()));
+ realSize = ::GetEnvironmentVariableW(
+ name.toStdWString().c_str(),
+ buffer.get(), static_cast(bufferSize));
}
- return QString::fromStdWString(s);
+ if (realSize == 0) {
+ const auto e = ::GetLastError();
+
+ // don't log if not found
+ if (e != ERROR_ENVVAR_NOT_FOUND) {
+ log::error(
+ "failed to get environment variable '{}', {}",
+ name, formatSystemMessage(e));
+ }
+
+ return {};
+ }
+
+ return QString::fromWCharArray(buffer.get(), realSize);
}
QString set(const QString& n, const QString& v)
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 2474fdc0..0181a335 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -5295,43 +5295,42 @@ void MainWindow::addAsExecutable()
return;
}
- using FileExecutionTypes = OrganizerCore::FileExecutionTypes;
+ const QFileInfo target(m_ContextItem->data(0, Qt::UserRole).toString());
+ const auto fec = spawn::getFileExecutionContext(this, target);
- QFileInfo targetInfo(m_ContextItem->data(0, Qt::UserRole).toString());
- QFileInfo binaryInfo;
- QString arguments;
- FileExecutionTypes type;
-
- if (!OrganizerCore::getFileExecutionContext(this, targetInfo, binaryInfo, arguments, type)) {
- return;
- }
-
- switch (type)
+ switch (fec.type)
{
- case FileExecutionTypes::Executable: {
- QString name = QInputDialog::getText(this, tr("Enter Name"),
- tr("Please enter a name for the executable"), QLineEdit::Normal,
- targetInfo.completeBaseName());
-
- if (!name.isEmpty()) {
- //Note: If this already exists, you'll lose custom settings
- m_OrganizerCore.executablesList()->setExecutable(Executable()
- .title(name)
- .binaryInfo(binaryInfo)
- .arguments(arguments)
- .workingDirectory(targetInfo.absolutePath()));
-
- refreshExecutablesList();
- }
-
- break;
+ case spawn::FileExecutionTypes::Executable:
+ {
+ const QString name = QInputDialog::getText(
+ this, tr("Enter Name"),
+ tr("Enter a name for the executable"),
+ QLineEdit::Normal,
+ target.completeBaseName());
+
+ if (!name.isEmpty()) {
+ //Note: If this already exists, you'll lose custom settings
+ m_OrganizerCore.executablesList()->setExecutable(Executable()
+ .title(name)
+ .binaryInfo(fec.binary)
+ .arguments(fec.arguments)
+ .workingDirectory(target.absolutePath()));
+
+ refreshExecutablesList();
}
- case FileExecutionTypes::Other: // fall-through
- default: {
- QMessageBox::information(this, tr("Not an executable"), tr("This is not a recognized executable."));
- break;
- }
+ break;
+ }
+
+ case spawn::FileExecutionTypes::Other: // fall-through
+ default:
+ {
+ QMessageBox::information(
+ this, tr("Not an executable"),
+ tr("This is not a recognized executable."));
+
+ break;
+ }
}
}
@@ -5458,7 +5457,8 @@ void MainWindow::openDataFile()
return;
}
- QFileInfo targetInfo(m_ContextItem->data(0, Qt::UserRole).toString());
+ const QString path = m_ContextItem->data(0, Qt::UserRole).toString();
+ const QFileInfo targetInfo(path);
m_OrganizerCore.runFile(this, targetInfo);
}
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 6bf2c290..78f9517c 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -976,76 +976,6 @@ QStringList OrganizerCore::modsSortedByProfilePriority() const
return res;
}
-QString OrganizerCore::findJavaInstallation(const QString& jarFile)
-{
- if (!jarFile.isEmpty()) {
- // try to find java automatically based on the given jar file
- std::wstring jarFileW = jarFile.toStdWString();
-
- WCHAR buffer[MAX_PATH];
- if (::FindExecutableW(jarFileW.c_str(), nullptr, buffer) > (HINSTANCE)32) {
- DWORD binaryType = 0UL;
- if (!::GetBinaryTypeW(buffer, &binaryType)) {
- log::debug(
- "failed to determine binary type of \"{}\": {}",
- QString::fromWCharArray(buffer), ::GetLastError());
- } else if (binaryType == SCS_32BIT_BINARY || binaryType == SCS_64BIT_BINARY) {
- return QString::fromWCharArray(buffer);
- }
- }
- }
-
- // second attempt: look to the registry
- QSettings reg("HKEY_LOCAL_MACHINE\\Software\\JavaSoft\\Java Runtime Environment", QSettings::NativeFormat);
- if (reg.contains("CurrentVersion")) {
- QString currentVersion = reg.value("CurrentVersion").toString();
- return reg.value(QString("%1/JavaHome").arg(currentVersion)).toString().append("\\bin\\javaw.exe");
- }
-
- // not found
- return {};
-}
-
-bool OrganizerCore::getFileExecutionContext(
- QWidget* parent, const QFileInfo &targetInfo,
- QFileInfo &binaryInfo, QString &arguments, FileExecutionTypes& type)
-{
- QString extension = targetInfo.suffix();
- if ((extension.compare("cmd", Qt::CaseInsensitive) == 0) ||
- (extension.compare("com", Qt::CaseInsensitive) == 0) ||
- (extension.compare("bat", Qt::CaseInsensitive) == 0)) {
- binaryInfo = QFileInfo("C:\\Windows\\System32\\cmd.exe");
- arguments = QString("/C \"%1\"").arg(QDir::toNativeSeparators(targetInfo.absoluteFilePath()));
- type = FileExecutionTypes::Executable;
- return true;
- } else if (extension.compare("exe", Qt::CaseInsensitive) == 0) {
- binaryInfo = targetInfo;
- type = FileExecutionTypes::Executable;
- return true;
- } else if (extension.compare("jar", Qt::CaseInsensitive) == 0) {
- auto java = findJavaInstallation(targetInfo.absoluteFilePath());
-
- if (java.isEmpty()) {
- java = QFileDialog::getOpenFileName(
- parent, QObject::tr("Select binary"),
- QString(), QObject::tr("Binary") + " (*.exe)");
- }
-
- if (java.isEmpty()) {
- return false;
- }
-
- binaryInfo = QFileInfo(java);
- arguments = QString("-jar \"%1\"").arg(QDir::toNativeSeparators(targetInfo.absoluteFilePath()));
- type = FileExecutionTypes::Executable;
-
- return true;
- } else {
- type = FileExecutionTypes::Other;
- return true;
- }
-}
-
bool OrganizerCore::previewFileWithAlternatives(
QWidget* parent, QString fileName, int selectedOrigin)
{
@@ -1177,35 +1107,23 @@ bool OrganizerCore::previewFile(
bool OrganizerCore::runFile(
QWidget* parent, const QFileInfo& targetInfo)
{
- QFileInfo binaryInfo;
- QString arguments;
- FileExecutionTypes type;
+ const auto fec = spawn::getFileExecutionContext(parent, targetInfo);
- if (!getFileExecutionContext(parent, targetInfo, binaryInfo, arguments, type)) {
- return false;
- }
-
- switch (type)
+ switch (fec.type)
{
- case FileExecutionTypes::Executable: {
- runExecutableFile(
- binaryInfo, arguments, currentProfile()->name(),
- targetInfo.absolutePath());
-
+ case spawn::FileExecutionTypes::Executable:
+ {
+ runExecutableFile(fec.binary, fec.arguments, targetInfo.absoluteDir());
return true;
}
- case FileExecutionTypes::Other: {
- ::ShellExecuteW(nullptr, L"open",
- ToWString(targetInfo.absoluteFilePath()).c_str(),
- nullptr, nullptr, SW_SHOWNORMAL);
-
- return true;
+ case spawn::FileExecutionTypes::Other: // fall-through
+ default:
+ {
+ const auto r = shell::Open(targetInfo.absoluteFilePath());
+ return r.success();
}
}
-
- // nop
- return false;
}
bool OrganizerCore::runExecutableFile(
@@ -1281,6 +1199,87 @@ bool OrganizerCore::runShortcut(const MOShortcut& shortcut)
return runExecutable(exe, false);
}
+HANDLE OrganizerCore::runExecutableOrExecutableFile(
+ const QString &executable, const QStringList &args, const QString &cwd,
+ const QString &profile, const QString &forcedCustomOverwrite,
+ bool ignoreCustomOverwrite)
+{
+ QString profileName = profile;
+ if (profile == "") {
+ if (m_CurrentProfile != nullptr) {
+ profileName = m_CurrentProfile->name();
+ } else {
+ throw MyException(tr("No profile set"));
+ }
+ }
+
+ QFileInfo binary;
+ QString arguments = args.join(" ");
+ QString currentDirectory = cwd;
+ QString steamAppID;
+ QString customOverwrite;
+ QList forcedLibraries;
+
+ if (executable.contains('\\') || executable.contains('/')) {
+ // file path
+
+ binary = QFileInfo(executable);
+ if (binary.isRelative()) {
+ // relative path, should be relative to game directory
+ binary = managedGame()->gameDirectory().absoluteFilePath(executable);
+ }
+
+ if (currentDirectory == "") {
+ currentDirectory = binary.absolutePath();
+ }
+
+ try {
+ const Executable& exe = m_ExecutablesList.getByBinary(binary);
+ steamAppID = exe.steamAppID();
+ customOverwrite = m_CurrentProfile->setting("custom_overwrites", exe.title()).toString();
+ if (m_CurrentProfile->forcedLibrariesEnabled(exe.title())) {
+ forcedLibraries = m_CurrentProfile->determineForcedLibraries(exe.title());
+ }
+ } catch (const std::runtime_error &) {
+ // nop
+ }
+ } else {
+ // only a file name, search executables list
+ try {
+ const Executable &exe = m_ExecutablesList.get(executable);
+ steamAppID = exe.steamAppID();
+ customOverwrite = m_CurrentProfile->setting("custom_overwrites", exe.title()).toString();
+ if (m_CurrentProfile->forcedLibrariesEnabled(exe.title())) {
+ forcedLibraries = m_CurrentProfile->determineForcedLibraries(exe.title());
+ }
+ if (arguments == "") {
+ arguments = exe.arguments();
+ }
+ binary = exe.binaryInfo();
+ if (currentDirectory == "") {
+ currentDirectory = exe.workingDirectory();
+ }
+ } catch (const std::runtime_error &) {
+ log::warn("\"{}\" not set up as executable", executable);
+ binary = QFileInfo(executable);
+ }
+ }
+
+ if (!forcedCustomOverwrite.isEmpty())
+ customOverwrite = forcedCustomOverwrite;
+
+ if (ignoreCustomOverwrite)
+ customOverwrite.clear();
+
+ return spawnAndWait(binary,
+ arguments,
+ profileName,
+ currentDirectory,
+ steamAppID,
+ customOverwrite,
+ forcedLibraries);
+}
+
HANDLE OrganizerCore::spawnAndWait(
const QFileInfo &binary, const QString &arguments, const QString &profileName,
const QDir ¤tDirectory, const QString &steamAppID,
@@ -1362,87 +1361,6 @@ HANDLE OrganizerCore::spawnAndWait(
return handle;
}
-HANDLE OrganizerCore::runExecutableOrExecutableFile(
- const QString &executable, const QStringList &args, const QString &cwd,
- const QString &profile, const QString &forcedCustomOverwrite,
- bool ignoreCustomOverwrite)
-{
- QString profileName = profile;
- if (profile == "") {
- if (m_CurrentProfile != nullptr) {
- profileName = m_CurrentProfile->name();
- } else {
- throw MyException(tr("No profile set"));
- }
- }
-
- QFileInfo binary;
- QString arguments = args.join(" ");
- QString currentDirectory = cwd;
- QString steamAppID;
- QString customOverwrite;
- QList forcedLibraries;
-
- if (executable.contains('\\') || executable.contains('/')) {
- // file path
-
- binary = QFileInfo(executable);
- if (binary.isRelative()) {
- // relative path, should be relative to game directory
- binary = managedGame()->gameDirectory().absoluteFilePath(executable);
- }
-
- if (currentDirectory == "") {
- currentDirectory = binary.absolutePath();
- }
-
- try {
- const Executable& exe = m_ExecutablesList.getByBinary(binary);
- steamAppID = exe.steamAppID();
- customOverwrite = m_CurrentProfile->setting("custom_overwrites", exe.title()).toString();
- if (m_CurrentProfile->forcedLibrariesEnabled(exe.title())) {
- forcedLibraries = m_CurrentProfile->determineForcedLibraries(exe.title());
- }
- } catch (const std::runtime_error &) {
- // nop
- }
- } else {
- // only a file name, search executables list
- try {
- const Executable &exe = m_ExecutablesList.get(executable);
- steamAppID = exe.steamAppID();
- customOverwrite = m_CurrentProfile->setting("custom_overwrites", exe.title()).toString();
- if (m_CurrentProfile->forcedLibrariesEnabled(exe.title())) {
- forcedLibraries = m_CurrentProfile->determineForcedLibraries(exe.title());
- }
- if (arguments == "") {
- arguments = exe.arguments();
- }
- binary = exe.binaryInfo();
- if (currentDirectory == "") {
- currentDirectory = exe.workingDirectory();
- }
- } catch (const std::runtime_error &) {
- log::warn("\"{}\" not set up as executable", executable);
- binary = QFileInfo(executable);
- }
- }
-
- if (!forcedCustomOverwrite.isEmpty())
- customOverwrite = forcedCustomOverwrite;
-
- if (ignoreCustomOverwrite)
- customOverwrite.clear();
-
- return spawnAndWait(binary,
- arguments,
- profileName,
- currentDirectory,
- steamAppID,
- customOverwrite,
- forcedLibraries);
-}
-
bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
{
if (!Settings::instance().interface().lockGUI())
diff --git a/src/organizercore.h b/src/organizercore.h
index fa05a20f..f802c8cb 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -91,12 +91,6 @@ private:
typedef boost::signals2::signal SignalModInstalled;
public:
- enum class FileExecutionTypes
- {
- Executable = 1,
- Other = 2
- };
-
static bool isNxmLink(const QString &link) { return link.startsWith("nxm://", Qt::CaseInsensitive); }
OrganizerCore(Settings &settings);
@@ -152,12 +146,6 @@ public:
void doAfterLogin(const std::function &function) { m_PostLoginTasks.append(function); }
void loggedInAction(QWidget* parent, std::function f);
- static QString findJavaInstallation(const QString& jarFile={});
-
- static bool getFileExecutionContext(
- QWidget* parent, const QFileInfo &targetInfo,
- QFileInfo &binaryInfo, QString &arguments, FileExecutionTypes& type);
-
bool previewFileWithAlternatives(QWidget* parent, QString filename, int selectedOrigin=-1);
bool previewFile(QWidget* parent, const QString& originName, const QString& path);
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 3c7d64ce..dd93bfaa 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -901,11 +901,11 @@ SpawnedProcess Spawner::spawn(
return {INVALID_HANDLE_VALUE, sp};
}
- if (!spawn::checkEnvironment(parent, sp)) {
+ if (!checkEnvironment(parent, sp)) {
return {INVALID_HANDLE_VALUE, sp};
}
- if (!spawn::checkBlacklist(parent, sp, settings)) {
+ if (!checkBlacklist(parent, sp, settings)) {
return {INVALID_HANDLE_VALUE, sp};
}
@@ -914,6 +914,185 @@ SpawnedProcess Spawner::spawn(
return {startBinary(parent, sp), sp};
}
+
+QString getExecutableForJarFile(const QString& jarFile)
+{
+ const std::wstring jarFileW = jarFile.toStdWString();
+
+ WCHAR buffer[MAX_PATH];
+
+ const auto hinst = ::FindExecutableW(jarFileW.c_str(), nullptr, buffer);
+ const auto r = static_cast(reinterpret_cast(hinst));
+
+ // anything <= 32 signals failure
+ if (r <= 32) {
+ log::warn(
+ "failed to find executable associated with file '{}', {}",
+ jarFile, shell::formatError(r));
+
+ return {};
+ }
+
+ DWORD binaryType = 0;
+
+ if (!::GetBinaryTypeW(buffer, &binaryType)) {
+ const auto e = ::GetLastError();
+
+ log::warn(
+ "failed to determine binary type of '{}', {}",
+ QString::fromWCharArray(buffer), formatSystemMessage(e));
+
+ return {};
+ }
+
+ if (binaryType != SCS_32BIT_BINARY && binaryType != SCS_64BIT_BINARY) {
+ log::warn(
+ "unexpected binary type {} for file '{}'",
+ binaryType, QString::fromWCharArray(buffer));
+
+ return {};
+ }
+
+ return QString::fromWCharArray(buffer);
+}
+
+QString getJavaHome()
+{
+ const QString key = "HKEY_LOCAL_MACHINE\\Software\\JavaSoft\\Java Runtime Environment";
+ const QString value = "CurrentVersion";
+
+ QSettings reg(key, QSettings::NativeFormat);
+
+ if (!reg.contains(value)) {
+ log::warn("key '{}\\{}' doesn't exist", key, value);
+ return {};
+ }
+
+ const QString currentVersion = reg.value("CurrentVersion").toString();
+ const QString javaHome = QString("%1/JavaHome").arg(currentVersion);
+
+ if (!reg.contains(javaHome)) {
+ log::warn(
+ "java version '{}' was found at '{}\\{}', but '{}\\{}' doesn't exist",
+ currentVersion, key, value, key, javaHome);
+
+ return {};
+ }
+
+ const auto path = reg.value(javaHome).toString();
+ return path + "\\bin\\javaw.exe";
+}
+
+QString findJavaInstallation(const QString& jarFile)
+{
+ // try to find java automatically based on the given jar file
+ if (!jarFile.isEmpty()) {
+ const auto s = getExecutableForJarFile(jarFile);
+ if (!s.isEmpty()) {
+ return s;
+ }
+ }
+
+ // second attempt: look to the registry
+ const auto s = getJavaHome();
+ if (!s.isEmpty()) {
+ return s;
+ }
+
+ // not found
+ return {};
+}
+
+bool isBatchFile(const QFileInfo& target)
+{
+ const auto batchExtensions = {"cmd", "bat"};
+
+ const QString extension = target.suffix();
+ for (auto&& e : batchExtensions) {
+ if (extension.compare(e, Qt::CaseInsensitive) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isExeFile(const QFileInfo& target)
+{
+ return (target.suffix().compare("exe", Qt::CaseInsensitive) == 0);
+}
+
+bool isJavaFile(const QFileInfo& target)
+{
+ return (target.suffix().compare("jar", Qt::CaseInsensitive) == 0);
+}
+
+QFileInfo getCmdPath()
+{
+ const auto p = env::get("COMSPEC2");
+ if (!p.isEmpty()) {
+ return p;
+ }
+
+ QString systemDirectory;
+
+ const std::size_t buffer_size = 1000;
+ wchar_t buffer[buffer_size + 1] = {};
+
+ const auto length = ::GetSystemDirectoryW(buffer, buffer_size);
+ if (length != 0) {
+ systemDirectory = QString::fromWCharArray(buffer, length);
+
+ if (!systemDirectory.endsWith("\\")) {
+ systemDirectory += "\\";
+ }
+ } else {
+ systemDirectory = "C:\\Windows\\System32\\";
+ }
+
+ return systemDirectory + "cmd.exe";
+}
+
+FileExecutionContext getFileExecutionContext(
+ QWidget* parent, const QFileInfo& target)
+{
+ if (isExeFile(target)) {
+ return {
+ target,
+ "",
+ FileExecutionTypes::Executable
+ };
+ }
+
+ if (isBatchFile(target)) {
+ return {
+ getCmdPath(),
+ QString("/C \"%1\"").arg(QDir::toNativeSeparators(target.absoluteFilePath())),
+ FileExecutionTypes::Executable
+ };
+ }
+
+ if (isJavaFile(target)) {
+ auto java = findJavaInstallation(target.absoluteFilePath());
+
+ if (java.isEmpty()) {
+ java = QFileDialog::getOpenFileName(
+ parent, QObject::tr("Select binary"),
+ QString(), QObject::tr("Binary") + " (*.exe)");
+ }
+
+ if (!java.isEmpty()) {
+ return {
+ QFileInfo(java),
+ QString("-jar \"%1\"").arg(QDir::toNativeSeparators(target.absoluteFilePath())),
+ FileExecutionTypes::Executable
+ };
+ }
+ }
+
+ return {{}, {}, FileExecutionTypes::Other};
+}
+
} // namespace
diff --git a/src/spawn.h b/src/spawn.h
index d2853cd5..866e1795 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -103,6 +103,25 @@ public:
private:
};
+
+enum class FileExecutionTypes
+{
+ Executable = 1,
+ Other
+};
+
+struct FileExecutionContext
+{
+ QFileInfo binary;
+ QString arguments;
+ FileExecutionTypes type;
+};
+
+QString findJavaInstallation(const QString& jarFile);
+
+FileExecutionContext getFileExecutionContext(
+ QWidget* parent, const QFileInfo& target);
+
} // namespace
--
cgit v1.3.1
From bff5a22f48b933fe9eba3a15497882ddf2a03990 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 24 Oct 2019 07:11:08 -0400
Subject: spawning an executable now only waits for that particular process
added waitForAllUSVFSProcesses() to OrganizerCore, used when closing MO
---
src/envmodule.cpp | 73 ++++++++++++++++++++++---
src/envmodule.h | 3 ++
src/mainwindow.cpp | 14 ++---
src/organizercore.cpp | 145 ++++++++++++++++++++++++++------------------------
src/organizercore.h | 11 +++-
src/spawn.cpp | 63 ++++++++++++++++++++++
src/spawn.h | 14 +++++
7 files changed, 233 insertions(+), 90 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/envmodule.cpp b/src/envmodule.cpp
index 3f1f8912..abbe02e5 100644
--- a/src/envmodule.cpp
+++ b/src/envmodule.cpp
@@ -408,7 +408,8 @@ std::vector getLoadedModules()
}
-std::vector getRunningProcesses()
+template
+void forEachRunningProcess(F&& f)
{
HandlePtr snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
@@ -416,7 +417,7 @@ std::vector getRunningProcesses()
{
const auto e = GetLastError();
log::error("CreateToolhelp32Snapshot() failed, {}", formatSystemMessage(e));
- return {};
+ return;
}
PROCESSENTRY32 entry = {};
@@ -427,16 +428,14 @@ std::vector getRunningProcesses()
if (!Process32First(snapshot.get(), &entry)) {
const auto e = GetLastError();
log::error("Process32First() failed, {}", formatSystemMessage(e));
- return {};
+ return;
}
- std::vector v;
-
for (;;)
{
- v.push_back(Process(
- entry.th32ProcessID,
- QString::fromStdWString(entry.szExeFile)));
+ if (!f(entry)) {
+ break;
+ }
// next process
if (!Process32Next(snapshot.get(), &entry))
@@ -450,8 +449,66 @@ std::vector getRunningProcesses()
break;
}
}
+}
+
+std::vector getRunningProcesses()
+{
+ std::vector v;
+
+ forEachRunningProcess([&](auto&& entry) {
+ v.push_back(Process(
+ entry.th32ProcessID,
+ QString::fromStdWString(entry.szExeFile)));
+
+ return true;
+ });
return v;
}
+QString getProcessName(HANDLE process)
+{
+ const QString badName = "unknown";
+
+ if (process == 0 || process == INVALID_HANDLE_VALUE) {
+ return badName;
+ }
+
+ const DWORD bufferSize = MAX_PATH;
+ wchar_t buffer[bufferSize + 1] = {};
+
+ const auto realSize = ::GetProcessImageFileNameW(process, buffer, bufferSize);
+
+ if (realSize == 0) {
+ const auto e = ::GetLastError();
+ log::error("GetProcessImageFileNameW() failed, {}", formatSystemMessage(e));
+ return badName;
+ }
+
+ auto s = QString::fromWCharArray(buffer, realSize);
+
+ const auto lastSlash = s.lastIndexOf("\\");
+ if (lastSlash != -1) {
+ s = s.mid(lastSlash + 1);
+ }
+
+ return s;
+}
+
+DWORD getProcessParentID(DWORD pid)
+{
+ DWORD ppid = 0;
+
+ forEachRunningProcess([&](auto&& entry) {
+ if (entry.th32ProcessID == pid) {
+ ppid = entry.th32ParentProcessID;
+ return false;
+ }
+
+ return true;
+ });
+
+ return ppid;
+}
+
} // namespace
diff --git a/src/envmodule.h b/src/envmodule.h
index deb7520f..212f6f7b 100644
--- a/src/envmodule.h
+++ b/src/envmodule.h
@@ -120,6 +120,9 @@ private:
std::vector getRunningProcesses();
std::vector getLoadedModules();
+QString getProcessName(HANDLE process);
+DWORD getProcessParentID(DWORD pid);
+
} // namespace env
#endif // ENV_MODULE_H
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 0181a335..bbb63333 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1311,16 +1311,10 @@ bool MainWindow::canExit()
}
}
- std::vector hiddenList;
- hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
- HANDLE injected_process_still_running = m_OrganizerCore.findAndOpenAUSVFSProcess(hiddenList, GetCurrentProcessId());
- if (injected_process_still_running != INVALID_HANDLE_VALUE)
- {
- m_exitAfterWait = true;
- m_OrganizerCore.waitForApplication(injected_process_still_running);
- if (!m_exitAfterWait) { // if operation cancelled
- return false;
- }
+ m_exitAfterWait = true;
+ m_OrganizerCore.waitForAllUSVFSProcessesWithLock();
+ if (!m_exitAfterWait) { // if operation cancelled
+ return false;
}
setCursor(Qt::WaitCursor);
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index d3f4a83c..2a228fda 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -34,6 +34,7 @@
#include "instancemanager.h"
#include
#include "previewdialog.h"
+#include "envmodule.h"
#include
#include
@@ -74,47 +75,6 @@ using namespace MOBase;
//static
CrashDumpsType OrganizerCore::m_globalCrashDumpsType = CrashDumpsType::None;
-static std::wstring getProcessName(HANDLE process)
-{
- wchar_t buffer[MAX_PATH];
- const wchar_t *fileName = L"unknown";
-
- if (process == nullptr) return fileName;
-
- if (::GetProcessImageFileNameW(process, buffer, MAX_PATH) != 0) {
- fileName = wcsrchr(buffer, L'\\');
- if (fileName == nullptr) {
- fileName = buffer;
- }
- else {
- fileName += 1;
- }
- }
-
- return fileName;
-}
-
-// Get parent PID for the given process, return 0 on failure
-static DWORD getProcessParentID(DWORD pid)
-{
- HANDLE th = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- PROCESSENTRY32 pe = { 0 };
- pe.dwSize = sizeof(PROCESSENTRY32);
-
- DWORD res = 0;
- if (Process32First(th, &pe))
- do {
- if (pe.th32ProcessID == pid) {
- res = pe.th32ParentProcessID;
- break;
- }
- } while (Process32Next(th, &pe));
-
- CloseHandle(th);
-
- return res;
-}
-
template
QStringList toStringList(InputIterator current, InputIterator end)
{
@@ -1348,35 +1308,46 @@ HANDLE OrganizerCore::spawnAndWait(
return handle;
}
-bool OrganizerCore::waitForProcessCompletionWithLock(
- HANDLE handle, LPDWORD exitCode)
+void OrganizerCore::withLock(std::function f)
{
- if (Settings::instance().interface().lockGUI()) {
- std::unique_ptr dlg;
- ILockedWaitingForProcess* uilock = nullptr;
+ std::unique_ptr dlg;
+ ILockedWaitingForProcess* uilock = nullptr;
+
+ if (m_MainWindow != nullptr) {
+ uilock = m_MainWindow->lock();
+ }
+ else {
+ // i.e. when running command line shortcuts there is no user interface
+ dlg.reset(new LockedDialog);
+ dlg->show();
+ dlg->setEnabled(true);
+ uilock = dlg.get();
+ }
+ ON_BLOCK_EXIT([&]() {
if (m_MainWindow != nullptr) {
- uilock = m_MainWindow->lock();
- }
- else {
- // i.e. when running command line shortcuts there is no user interface
- dlg.reset(new LockedDialog);
- dlg->show();
- dlg->setEnabled(true);
- uilock = dlg.get();
- }
+ m_MainWindow->unlock();
+ } });
- ON_BLOCK_EXIT([&]() {
- if (m_MainWindow != nullptr) {
- m_MainWindow->unlock();
- } });
+ f(uilock);
+}
+
+bool OrganizerCore::waitForProcessCompletionWithLock(
+ HANDLE handle, LPDWORD exitCode)
+{
+ if (!Settings::instance().interface().lockGUI()) {
+ return true;
+ }
+
+ bool r = false;
+ withLock([&](auto* uilock) {
DWORD ignoreExitCode;
- waitForProcessCompletion(handle, exitCode ? exitCode : &ignoreExitCode, uilock);
+ r = waitForProcessCompletion(handle, exitCode ? exitCode : &ignoreExitCode, uilock);
cycleDiagnostics();
- }
+ });
- return handle;
+ return r;
}
bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
@@ -1393,30 +1364,64 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
if (m_MainWindow != nullptr) {
m_MainWindow->unlock();
} });
+
return waitForProcessCompletion(handle, exitCode, uilock);
}
-bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock)
+bool OrganizerCore::waitForProcessCompletion(
+ HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock)
+{
+ const auto r = spawn::waitForProcess(handle, exitCode, uilock);
+
+ switch (r)
+ {
+ case spawn::WaitResults::Completed: // fall-through
+ case spawn::WaitResults::Unlocked:
+ return true;
+
+ case spawn::WaitResults::Error: // fall-through
+ default:
+ return false;
+ }
+}
+
+bool OrganizerCore::waitForAllUSVFSProcessesWithLock()
{
+ bool r = false;
+
+ withLock([&](auto* uilock) {
+ r = waitForAllUSVFSProcesses(uilock);
+ });
+
+ return r;
+}
+
+bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
+{
+ // Certain process names we wish to "hide" for aesthetic reason:
+ std::vector hiddenList;
+ hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
+
bool originalHandle = true;
bool newHandle = true;
bool uiunlocked = false;
+ HANDLE handle = findAndOpenAUSVFSProcess(hiddenList, GetCurrentProcessId());
+ DWORD* exitCode = nullptr;
DWORD currentPID = 0;
QString processName;
+
auto waitForChildUntil = GetTickCount64();
if (handle != INVALID_HANDLE_VALUE) {
currentPID = GetProcessId(handle);
- processName = QString::fromStdWString(getProcessName(handle));
+ processName = env::getProcessName(handle);
}
- // Certain process names we wish to "hide" for aesthetic reason:
bool waitingOnHidden = false;
- std::vector hiddenList;
- hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
for (QString hide : hiddenList)
if (processName.contains(hide, Qt::CaseInsensitive))
waitingOnHidden = true;
+
// The main reason for adding the hidden list is to hide the MO proxy we use to spawn virtualized processes.
// On the one hand we want to display the real executable without it feeling laggy, on the other we don't want
// to requery processes all the time if for some reason we are waiting on hidden processes and find no "unhidden"
@@ -1489,7 +1494,7 @@ bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode, IL
newHandle = handle != INVALID_HANDLE_VALUE;
if (newHandle) {
currentPID = GetProcessId(handle);
- processName = QString::fromStdWString(getProcessName(handle));
+ processName = env::getProcessName(handle);
for (QString hide : hiddenList)
if (processName.contains(hide, Qt::CaseInsensitive))
waitingOnHidden = true;
@@ -1541,13 +1546,13 @@ HANDLE OrganizerCore::findAndOpenAUSVFSProcess(const std::vector& hidde
continue;
}
- QString pname = QString::fromStdWString(getProcessName(handle));
+ QString pname = env::getProcessName(handle);
bool phidden = false;
for (auto hide : hiddenList)
if (pname.contains(hide, Qt::CaseInsensitive))
phidden = true;
- bool pprefered = preferedParentPid && getProcessParentID(pids[i]) == preferedParentPid;
+ bool pprefered = preferedParentPid && env::getProcessParentID(pids[i]) == preferedParentPid;
if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) {
if (best_match != INVALID_HANDLE_VALUE)
diff --git a/src/organizercore.h b/src/organizercore.h
index 3d3c7325..ffdb6830 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -167,6 +167,8 @@ public:
const QString &profile, const QString &forcedCustomOverwrite = "",
bool ignoreCustomOverwrite = false);
+ bool waitForApplication(HANDLE processHandle, LPDWORD exitCode = nullptr);
+ bool waitForAllUSVFSProcessesWithLock();
void loginSuccessfulUpdate(bool necessary);
void loginFailedUpdate(const QString &message);
@@ -221,8 +223,6 @@ public:
DownloadManager *downloadManager();
PluginList *pluginList();
ModList *modList();
- bool waitForApplication(HANDLE processHandle, LPDWORD exitCode = nullptr);
- HANDLE findAndOpenAUSVFSProcess(const std::vector& hiddenList, DWORD preferedParentPid);
bool onModInstalled(const std::function &func);
bool onAboutToRun(const std::function &func);
bool onFinishedRun(const std::function &func);
@@ -311,6 +311,13 @@ private:
bool waitForProcessCompletion(
HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock);
+ bool waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock);
+
+ void withLock(std::function f);
+
+ HANDLE findAndOpenAUSVFSProcess(
+ const std::vector& hiddenList, DWORD preferedParentPid);
+
private slots:
void directory_refreshed();
diff --git a/src/spawn.cpp b/src/spawn.cpp
index fe1e9e3e..f0b3b2c7 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -28,6 +28,7 @@ along with Mod Organizer. If not, see .
#include "settings.h"
#include "settingsdialogworkarounds.h"
#include
+#include
#include
#include
#include
@@ -1093,6 +1094,68 @@ FileExecutionContext getFileExecutionContext(
return {{}, {}, FileExecutionTypes::Other};
}
+WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
+{
+ if (handle == INVALID_HANDLE_VALUE) {
+ return WaitResults::Error;
+ }
+
+ const DWORD pid = ::GetProcessId(handle);
+ const QString processName = QString("%1 (%2)")
+ .arg(env::getProcessName(handle))
+ .arg(pid);
+
+ if (uilock)
+ uilock->setProcessName(processName);
+
+ constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1;
+ DWORD res = WAIT_TIMEOUT;
+
+ log::debug(
+ "waiting for process completion '{}' ({})",
+ processName, pid);
+
+ for (;;) {
+ // Wait for a an event on the handle, a key press, mouse click or timeout
+ const auto res = MsgWaitForMultipleObjects(
+ 1, &handle, FALSE, 50, QS_KEY | QS_MOUSEBUTTON);
+
+ if (res == WAIT_FAILED) {
+ // error
+ const auto e = ::GetLastError();
+
+ log::error(
+ "failed waiting for process completion '{}' ({}), {}",
+ processName, pid, formatSystemMessage(e));
+
+ return WaitResults::Error;
+ } else if (res == WAIT_OBJECT_0) {
+ // completed
+ log::debug("process '{}' ({}) completed", processName, pid);
+
+ if (exitCode) {
+ if (!::GetExitCodeProcess(handle, exitCode)) {
+ const auto e = ::GetLastError();
+ log::warn(
+ "failed to get exit code of process '{}' ({}): {}",
+ processName, pid, formatSystemMessage(e));
+ }
+ }
+
+ return WaitResults::Completed;
+ }
+
+ // keep processing events so the app doesn't appear dead
+ QCoreApplication::sendPostedEvents();
+ QCoreApplication::processEvents();
+
+ if (uilock && uilock->unlockForced()) {
+ log::debug("waiting for process '{}' ({}) aborted by UI", processName, pid);
+ return WaitResults::Unlocked;
+ }
+ }
+}
+
} // namespace
diff --git a/src/spawn.h b/src/spawn.h
index 866e1795..441cad2c 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -27,6 +27,8 @@ along with Mod Organizer. If not, see .
#include
class Settings;
+class ILockedWaitingForProcess;
+
namespace MOBase { class IPluginGame; }
namespace spawn
@@ -84,6 +86,7 @@ public:
~SpawnedProcess();
HANDLE releaseHandle();
+ void wait();
private:
HANDLE m_handle;
@@ -122,6 +125,17 @@ QString findJavaInstallation(const QString& jarFile);
FileExecutionContext getFileExecutionContext(
QWidget* parent, const QFileInfo& target);
+
+enum class WaitResults
+{
+ Completed = 1,
+ Error,
+ Unlocked
+};
+
+WaitResults waitForProcess(
+ HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock);
+
} // namespace
--
cgit v1.3.1
From 18b438cf27a552e69e984bfee63187b6471682ab Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Tue, 29 Oct 2019 07:28:12 -0400
Subject: split to getRunningUSVFSProcesses() simplified
waitForAllUSVFSProcesses() to always get the list of running processes after
one process completes
---
src/envmodule.cpp | 6 ++
src/envmodule.h | 2 +
src/organizercore.cpp | 224 ++++++++++++++-----------------------------------
src/spawn.cpp | 53 ++++++++----
src/spawn.h | 4 +
src/usvfsconnector.cpp | 47 +++++++++++
src/usvfsconnector.h | 2 +
7 files changed, 163 insertions(+), 175 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/envmodule.cpp b/src/envmodule.cpp
index abbe02e5..160a54fa 100644
--- a/src/envmodule.cpp
+++ b/src/envmodule.cpp
@@ -511,4 +511,10 @@ DWORD getProcessParentID(DWORD pid)
return ppid;
}
+
+DWORD getProcessParentID(HANDLE handle)
+{
+ return getProcessParentID(GetProcessId(handle));
+}
+
} // namespace
diff --git a/src/envmodule.h b/src/envmodule.h
index 212f6f7b..6c0a028d 100644
--- a/src/envmodule.h
+++ b/src/envmodule.h
@@ -121,7 +121,9 @@ std::vector getRunningProcesses();
std::vector getLoadedModules();
QString getProcessName(HANDLE process);
+
DWORD getProcessParentID(DWORD pid);
+DWORD getProcessParentID(HANDLE handle);
} // namespace env
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 2a228fda..2a97b998 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -1355,17 +1355,13 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
if (!Settings::instance().interface().lockGUI())
return true;
- ILockedWaitingForProcess* uilock = nullptr;
- if (m_MainWindow != nullptr) {
- uilock = m_MainWindow->lock();
- }
+ bool r = false;
- ON_BLOCK_EXIT([&] () {
- if (m_MainWindow != nullptr) {
- m_MainWindow->unlock();
- } });
+ withLock([&](auto* uilock) {
+ r = waitForProcessCompletion(handle, exitCode, uilock);
+ });
- return waitForProcessCompletion(handle, exitCode, uilock);
+ return r;
}
bool OrganizerCore::waitForProcessCompletion(
@@ -1387,6 +1383,9 @@ bool OrganizerCore::waitForProcessCompletion(
bool OrganizerCore::waitForAllUSVFSProcessesWithLock()
{
+ if (!Settings::instance().interface().lockGUI())
+ return true;
+
bool r = false;
withLock([&](auto* uilock) {
@@ -1396,178 +1395,81 @@ bool OrganizerCore::waitForAllUSVFSProcessesWithLock()
return r;
}
-bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
+HANDLE getInterestingProcess(
+ const std::vector& handles,
+ const std::vector& hidden, DWORD preferedParentPid)
{
- // Certain process names we wish to "hide" for aesthetic reason:
- std::vector hiddenList;
- hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
-
- bool originalHandle = true;
- bool newHandle = true;
- bool uiunlocked = false;
-
- HANDLE handle = findAndOpenAUSVFSProcess(hiddenList, GetCurrentProcessId());
- DWORD* exitCode = nullptr;
- DWORD currentPID = 0;
- QString processName;
-
- auto waitForChildUntil = GetTickCount64();
- if (handle != INVALID_HANDLE_VALUE) {
- currentPID = GetProcessId(handle);
- processName = env::getProcessName(handle);
- }
-
- bool waitingOnHidden = false;
- for (QString hide : hiddenList)
- if (processName.contains(hide, Qt::CaseInsensitive))
- waitingOnHidden = true;
-
- // The main reason for adding the hidden list is to hide the MO proxy we use to spawn virtualized processes.
- // On the one hand we want to display the real executable without it feeling laggy, on the other we don't want
- // to requery processes all the time if for some reason we are waiting on hidden processes and find no "unhidden"
- // process. For this reason we use exponential backoff and also start with a delibrately low value to improve
- // the responsiveness of the initial update
- DWORD64 nextHiddenCheck = GetTickCount64();
- DWORD64 nextHiddenCheckDelay = 50;
-
- constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1;
- DWORD res = WAIT_TIMEOUT;
- while (handle != INVALID_HANDLE_VALUE && (newHandle || res == WAIT_TIMEOUT || res == INPUT_EVENT))
- {
- if (newHandle) {
- processName += QString(" (%1)").arg(currentPID);
- if (uilock)
- uilock->setProcessName(processName);
-
- log::debug(
- "Waiting for {} process completion: {}",
- (originalHandle ? "spawned" : "usvfs"), processName);
-
- newHandle = false;
- }
+ HANDLE best_match = INVALID_HANDLE_VALUE;
+ bool best_match_hidden = true;
- // Wait for a an event on the handle, a key press, mouse click or timeout
- res = MsgWaitForMultipleObjects(1, &handle, FALSE, 200, QS_KEY | QS_MOUSEBUTTON);
- if (res == WAIT_FAILED) {
- log::warn("Failed waiting for process completion : MsgWaitForMultipleObjects WAIT_FAILED {}", GetLastError());
- break;
- }
+ for (auto handle : handles) {
+ const QString pname = env::getProcessName(handle);
- // keep processing events so the app doesn't appear dead
- QCoreApplication::sendPostedEvents();
- QCoreApplication::processEvents();
+ bool phidden = false;
+ for (auto h : hidden)
+ if (pname.contains(h, Qt::CaseInsensitive))
+ phidden = true;
- if (uilock && uilock->unlockForced()) {
- uiunlocked = true;
- break;
- }
+ bool pprefered = preferedParentPid && env::getProcessParentID(handle) == preferedParentPid;
- if (res == WAIT_OBJECT_0) {
- // process we were waiting on has completed
- if (originalHandle && exitCode && !::GetExitCodeProcess(handle, exitCode))
- log::warn("Failed getting exit code of complete process: {}", GetLastError());
- CloseHandle(handle);
- handle = INVALID_HANDLE_VALUE;
- originalHandle = false;
- // if the previous process spawned a child process and immediately exits we may miss it if we check immediately
- waitForChildUntil = GetTickCount64() + 800;
+ if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) {
+ best_match = handle;
+ best_match_hidden = phidden;
}
- // search for another process to wait on if either:
- // 1. we just completed waiting for a process and need to find/wait for an inject child
- // 2. we are currently waiting on a hidden process so periodically check if there is a non-hidden process to wait on
- bool firstIteration = true;
- while ((handle == INVALID_HANDLE_VALUE && GetTickCount64() <= waitForChildUntil)
- || (waitingOnHidden && GetTickCount64() >= nextHiddenCheck))
- {
- if (firstIteration)
- firstIteration = false;
- else {
- QThread::msleep(200);
- QCoreApplication::sendPostedEvents();
- QCoreApplication::processEvents();
- }
-
- // search if there is another usvfs process active
- handle = findAndOpenAUSVFSProcess(hiddenList, currentPID);
- waitingOnHidden = false;
- newHandle = handle != INVALID_HANDLE_VALUE;
- if (newHandle) {
- currentPID = GetProcessId(handle);
- processName = env::getProcessName(handle);
- for (QString hide : hiddenList)
- if (processName.contains(hide, Qt::CaseInsensitive))
- waitingOnHidden = true;
- }
- if (waitingOnHidden) {
- nextHiddenCheck = GetTickCount64() + nextHiddenCheckDelay;
- nextHiddenCheckDelay = std::min(nextHiddenCheckDelay * 2, (DWORD64) 2000);
- }
- else {
- nextHiddenCheck = GetTickCount64();
- nextHiddenCheckDelay = 200;
- }
- }
+ if (!phidden && pprefered)
+ return best_match;
}
- if (res == WAIT_OBJECT_0)
- log::debug("Waiting for process completion successfull");
- else if (uiunlocked)
- log::debug("Waiting for process completion aborted by UI");
- else
- log::debug("Waiting for process completion not successfull: {}", res);
-
- if (handle != INVALID_HANDLE_VALUE)
- ::CloseHandle(handle);
-
- return res == WAIT_OBJECT_0;
+ return best_match;
}
-HANDLE OrganizerCore::findAndOpenAUSVFSProcess(const std::vector& hiddenList, DWORD preferedParentPid) {
- // for practical reasons a querySize of 1 is probably enough, we use a larger query as a heuristics
- // to find a more "aesthetic injected processes (attempting to comply to hiddenList and preferedParentPid)
- constexpr size_t querySize = 100;
- DWORD pids[querySize];
- size_t found = querySize;
- if (!::GetVFSProcessList(&found, pids)) {
- log::warn("Failed seeking USVFS processes : GetVFSProcessList failed?!");
- return INVALID_HANDLE_VALUE;
- }
-
- HANDLE best_match = INVALID_HANDLE_VALUE;
- bool best_match_hidden = true;
- for (size_t i = 0; i < found; ++i) {
- if (pids[i] == GetCurrentProcessId())
- continue; // obviously don't wait for MO process
+bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
+{
+ // Certain process names we wish to "hide" for aesthetic reason:
+ std::vector hiddenList;
+ hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
- HANDLE handle = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pids[i]);
- if (handle == INVALID_HANDLE_VALUE) {
- log::warn("Failed opening USVFS process {}: OpenProcess failed {}", pids[i], GetLastError());
- continue;
+ for (;;) {
+ const auto handles = getRunningUSVFSProcesses();
+ if (handles.empty()) {
+ break;
}
- QString pname = env::getProcessName(handle);
- bool phidden = false;
- for (auto hide : hiddenList)
- if (pname.contains(hide, Qt::CaseInsensitive))
- phidden = true;
+ const auto interesting = getInterestingProcess(
+ handles, hiddenList, GetCurrentProcessId());
- bool pprefered = preferedParentPid && env::getProcessParentID(pids[i]) == preferedParentPid;
+ if (uilock) {
+ const DWORD pid = ::GetProcessId(interesting);
+ const QString processName = QString("%1 (%2)")
+ .arg(env::getProcessName(interesting))
+ .arg(pid);
- if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) {
- if (best_match != INVALID_HANDLE_VALUE)
- CloseHandle(best_match);
- best_match = handle;
- best_match_hidden = phidden;
+ uilock->setProcessName(processName);
}
- else
- CloseHandle(handle);
- if (!phidden && pprefered)
- return best_match;
+ const auto r = spawn::waitForProcess(interesting, nullptr, uilock);
+
+ switch (r)
+ {
+ case spawn::WaitResults::Completed:
+ // this process is completed, check for others
+ break;
+
+ case spawn::WaitResults::Unlocked:
+ // force unlocked
+ log::debug("waiting for process completion aborted by UI");
+ return true;
+
+ case spawn::WaitResults::Error: // fall-through
+ default:
+ log::debug("waiting for process completion not successful");
+ return false;
+ }
}
- return best_match;
+ log::debug("Waiting for process completion successful");
+ return true;
}
bool OrganizerCore::onAboutToRun(
diff --git a/src/spawn.cpp b/src/spawn.cpp
index f0b3b2c7..1003024f 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -1094,7 +1094,8 @@ FileExecutionContext getFileExecutionContext(
return {{}, {}, FileExecutionTypes::Other};
}
-WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
+WaitResults waitForProcess(
+ HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
{
if (handle == INVALID_HANDLE_VALUE) {
return WaitResults::Error;
@@ -1108,37 +1109,62 @@ WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProc
if (uilock)
uilock->setProcessName(processName);
- constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1;
- DWORD res = WAIT_TIMEOUT;
-
log::debug(
"waiting for process completion '{}' ({})",
processName, pid);
+ std::vector handles;
+ handles.push_back(handle);
+
+ std::vector exitCodes;
+
+ const auto r = waitForProcesses(handles, exitCodes, uilock);
+ if (exitCode && !exitCodes.empty()) {
+ *exitCode = exitCodes[0];
+ }
+
+ return r;
+}
+
+WaitResults waitForProcesses(
+ const std::vector& handles, std::vector& exitCodes,
+ ILockedWaitingForProcess* uilock)
+{
+ if (handles.empty()) {
+ return WaitResults::Completed;
+ }
+
+ const auto WAIT_OBJECT_N = static_cast(WAIT_OBJECT_0 + handles.size());
+
for (;;) {
// Wait for a an event on the handle, a key press, mouse click or timeout
const auto res = MsgWaitForMultipleObjects(
- 1, &handle, FALSE, 50, QS_KEY | QS_MOUSEBUTTON);
+ static_cast(handles.size()), &handles[0],
+ TRUE, 50, QS_KEY | QS_MOUSEBUTTON);
if (res == WAIT_FAILED) {
// error
const auto e = ::GetLastError();
log::error(
- "failed waiting for process completion '{}' ({}), {}",
- processName, pid, formatSystemMessage(e));
+ "failed waiting for process completion, {}", formatSystemMessage(e));
return WaitResults::Error;
- } else if (res == WAIT_OBJECT_0) {
+ } else if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_N) {
// completed
- log::debug("process '{}' ({}) completed", processName, pid);
+ exitCodes.resize(handles.size());
+ std::fill(exitCodes.begin(), exitCodes.end(), 0);
+
+ for (std::size_t i=0; iunlockForced()) {
- log::debug("waiting for process '{}' ({}) aborted by UI", processName, pid);
return WaitResults::Unlocked;
}
}
diff --git a/src/spawn.h b/src/spawn.h
index 441cad2c..6b947f2f 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -136,6 +136,10 @@ enum class WaitResults
WaitResults waitForProcess(
HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock);
+WaitResults waitForProcesses(
+ const std::vector& handles, std::vector& exitCodes,
+ ILockedWaitingForProcess* uilock);
+
} // namespace
diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp
index 311c6dd3..3dba3efc 100644
--- a/src/usvfsconnector.cpp
+++ b/src/usvfsconnector.cpp
@@ -20,6 +20,7 @@ along with Mod Organizer. If not, see .
#include "usvfsconnector.h"
#include "settings.h"
#include "organizercore.h"
+#include "envmodule.h"
#include "shared/util.h"
#include
#include
@@ -256,3 +257,49 @@ void UsvfsConnector::updateForcedLibraries(const QList getRunningUSVFSProcesses()
+{
+ std::vector pids;
+
+ {
+ size_t count = 0;
+ DWORD* buffer = nullptr;
+ if (!::GetVFSProcessList2(&count, &buffer)) {
+ log::error("failed to get usvfs process list");
+ return {};
+ }
+
+ if (buffer) {
+ pids.assign(buffer, buffer + count);
+ std::free(buffer);
+ }
+ }
+
+ const auto thisPid = GetCurrentProcessId();
+ std::vector v;
+
+ for (auto&& pid : pids) {
+ if (pid == thisPid) {
+ continue; // obviously don't wait for MO process
+ }
+
+ HANDLE handle = ::OpenProcess(
+ PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pid);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ const auto e = GetLastError();
+
+ log::warn(
+ "failed to open usvfs process {}: {}",
+ pid, formatSystemMessage(e));
+
+ continue;
+ }
+
+ v.push_back(handle);
+ }
+
+ return v;
+}
diff --git a/src/usvfsconnector.h b/src/usvfsconnector.h
index d0071678..5982778b 100644
--- a/src/usvfsconnector.h
+++ b/src/usvfsconnector.h
@@ -103,4 +103,6 @@ private:
CrashDumpsType crashDumpsType(int type);
+std::vector getRunningUSVFSProcesses();
+
#endif // USVFSCONNECTOR_H
--
cgit v1.3.1
From 8c72077febaea485200adcf1e9f615902e930def Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Tue, 29 Oct 2019 10:03:59 -0400
Subject: replaced uilock by a progress callback in spawn waiting for process
now gets the whole process tree to find an interesting process
---
src/env.h | 17 ------
src/envmodule.cpp | 107 +++++++++++++++++++++++++++++++++-
src/envmodule.h | 35 ++++++++++-
src/envsecurity.cpp | 1 +
src/envwindows.cpp | 1 +
src/ilockedwaitingforprocess.h | 1 +
src/lockeddialogbase.cpp | 7 ++-
src/lockeddialogbase.h | 4 +-
src/organizercore.cpp | 128 ++++++++++++++++++++++++++---------------
src/spawn.cpp | 29 ++++------
src/spawn.h | 7 +--
11 files changed, 246 insertions(+), 91 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/env.h b/src/env.h
index 1760c7fe..f95d1013 100644
--- a/src/env.h
+++ b/src/env.h
@@ -13,23 +13,6 @@ class WindowsInfo;
class Metrics;
-// used by HandlePtr, calls CloseHandle() as the deleter
-//
-struct HandleCloser
-{
- using pointer = HANDLE;
-
- void operator()(HANDLE h)
- {
- if (h != INVALID_HANDLE_VALUE) {
- ::CloseHandle(h);
- }
- }
-};
-
-using HandlePtr = std::unique_ptr;
-
-
// used by DesktopDCPtr, calls ReleaseDC(0, dc) as the deleter
//
struct DesktopDCReleaser
diff --git a/src/envmodule.cpp b/src/envmodule.cpp
index 160a54fa..0e2e8ec7 100644
--- a/src/envmodule.cpp
+++ b/src/envmodule.cpp
@@ -320,19 +320,61 @@ QString Module::getMD5() const
}
-Process::Process(DWORD pid, QString name)
- : m_pid(pid), m_name(std::move(name))
+Process::Process()
+ : Process(0, 0, {})
{
}
+Process::Process(HANDLE h)
+ : Process(::GetProcessId(h), 0, {})
+{
+}
+
+Process::Process(DWORD pid, DWORD ppid, QString name)
+ : m_pid(pid), m_ppid(ppid), m_name(std::move(name))
+{
+}
+
+bool Process::isValid() const
+{
+ return (m_pid != 0);
+}
+
DWORD Process::pid() const
{
return m_pid;
}
+DWORD Process::ppid() const
+{
+ if (!m_ppid) {
+ m_ppid = getProcessParentID(m_pid);
+ }
+
+ return *m_ppid;
+}
+
const QString& Process::name() const
{
- return m_name;
+ if (!m_name) {
+ m_name = getProcessName(m_pid);
+ }
+
+ return *m_name;
+}
+
+HandlePtr Process::openHandleForWait() const
+{
+ HandlePtr h(OpenProcess(
+ PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, m_pid));
+
+ if (!h) {
+ const auto e = GetLastError();
+ log::error("can't get name of process {}, {}", m_pid, formatSystemMessage(e));
+ return {};
+ }
+
+ return h;
}
// whether this process can be accessed; fails if the current process doesn't
@@ -353,6 +395,16 @@ bool Process::canAccess() const
return true;
}
+void Process::addChild(Process p)
+{
+ m_children.push_back(p);
+}
+
+std::vector& Process::children()
+{
+ return m_children;
+}
+
std::vector getLoadedModules()
{
@@ -458,6 +510,7 @@ std::vector getRunningProcesses()
forEachRunningProcess([&](auto&& entry) {
v.push_back(Process(
entry.th32ProcessID,
+ entry.th32ParentProcessID,
QString::fromStdWString(entry.szExeFile)));
return true;
@@ -466,6 +519,54 @@ std::vector getRunningProcesses()
return v;
}
+void findChildren(Process& parent, const std::vector& processes)
+{
+ for (auto&& p : processes) {
+ if (p.ppid() == parent.pid()) {
+ Process child = p;
+ findChildren(child, processes);
+
+ parent.addChild(child);
+ }
+ }
+}
+
+Process getProcessTree(HANDLE parent)
+{
+ const auto parentPID = ::GetProcessId(parent);
+ const auto v = getRunningProcesses();
+
+ Process root;
+ for (auto&& p : v) {
+ if (p.pid() == parentPID) {
+ root = p;
+ break;
+ }
+ }
+
+ if (root.pid() == 0) {
+ log::error("process {} is not running", parentPID);
+ return {};
+ }
+
+ findChildren(root, v);
+
+ return root;
+}
+
+QString getProcessName(DWORD pid)
+{
+ HandlePtr h(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
+
+ if (!h) {
+ const auto e = GetLastError();
+ log::error("can't get name of process {}, {}", pid, formatSystemMessage(e));
+ return {};
+ }
+
+ return getProcessName(h.get());
+}
+
QString getProcessName(HANDLE process)
{
const QString badName = "unknown";
diff --git a/src/envmodule.h b/src/envmodule.h
index 6c0a028d..d152b840 100644
--- a/src/envmodule.h
+++ b/src/envmodule.h
@@ -7,6 +7,23 @@
namespace env
{
+// used by HandlePtr, calls CloseHandle() as the deleter
+//
+struct HandleCloser
+{
+ using pointer = HANDLE;
+
+ void operator()(HANDLE h)
+ {
+ if (h != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(h);
+ }
+ }
+};
+
+using HandlePtr = std::unique_ptr;
+
+
// represents one module
//
class Module
@@ -101,25 +118,39 @@ private:
class Process
{
public:
- Process(DWORD pid, QString name);
+ Process();
+ explicit Process(HANDLE h);
+ Process(DWORD pid, DWORD ppid, QString name);
+ bool isValid() const;
DWORD pid() const;
+ DWORD ppid() const;
const QString& name() const;
+ HandlePtr openHandleForWait() const;
+
// whether this process can be accessed; fails if the current process doesn't
// have the proper permissions
//
bool canAccess() const;
+ void addChild(Process p);
+ std::vector& children();
+
private:
DWORD m_pid;
- QString m_name;
+ mutable std::optional m_ppid;
+ mutable std::optional m_name;
+ std::vector m_children;
};
std::vector getRunningProcesses();
std::vector getLoadedModules();
+Process getProcessTree(HANDLE parent);
+
+QString getProcessName(DWORD pid);
QString getProcessName(HANDLE process);
DWORD getProcessParentID(DWORD pid);
diff --git a/src/envsecurity.cpp b/src/envsecurity.cpp
index 786291c6..6d62728b 100644
--- a/src/envsecurity.cpp
+++ b/src/envsecurity.cpp
@@ -1,5 +1,6 @@
#include "envsecurity.h"
#include "env.h"
+#include "envmodule.h"
#include
#include
diff --git a/src/envwindows.cpp b/src/envwindows.cpp
index 3932a9b5..98e78a3e 100644
--- a/src/envwindows.cpp
+++ b/src/envwindows.cpp
@@ -1,5 +1,6 @@
#include "envwindows.h"
#include "env.h"
+#include "envmodule.h"
#include
#include
diff --git a/src/ilockedwaitingforprocess.h b/src/ilockedwaitingforprocess.h
index 9475ddb9..4d1e786f 100644
--- a/src/ilockedwaitingforprocess.h
+++ b/src/ilockedwaitingforprocess.h
@@ -8,6 +8,7 @@ class ILockedWaitingForProcess
public:
virtual bool unlockForced() const = 0;
virtual void setProcessName(QString const &) = 0;
+ virtual void setProcessInformation(DWORD pid, const QString& name) = 0;
};
#endif // ILOCKEDWAITINGFORPROCESS_H
diff --git a/src/lockeddialogbase.cpp b/src/lockeddialogbase.cpp
index b18f7429..0876a511 100644
--- a/src/lockeddialogbase.cpp
+++ b/src/lockeddialogbase.cpp
@@ -18,7 +18,7 @@ along with Mod Organizer. If not, see .
*/
#include "lockeddialogbase.h"
-
+#include "envmodule.h"
#include
#include
#include
@@ -63,6 +63,11 @@ bool LockedDialogBase::canceled() const {
return m_Canceled;
}
+void LockedDialogBase::setProcessInformation(DWORD pid, const QString& name)
+{
+ setProcessName(QString("%1 (%2)").arg(name).arg(pid));
+}
+
void LockedDialogBase::unlock() {
m_Unlocked = true;
}
diff --git a/src/lockeddialogbase.h b/src/lockeddialogbase.h
index 3c974a38..4ebad4c7 100644
--- a/src/lockeddialogbase.h
+++ b/src/lockeddialogbase.h
@@ -30,7 +30,7 @@ class QWidget;
/**
* a small borderless dialog displayed while the Mod Organizer UI is locked
* The dialog contains only a label and a button to force the UI to be unlocked
- *
+ *
* The UI gets locked while running external applications since they may modify the
* data on which Mod Organizer works. After the UI is unlocked (manually or after the
* external application closed) MO will refresh all of its data sources
@@ -46,6 +46,8 @@ public:
virtual bool canceled() const;
+ void setProcessInformation(DWORD pid, const QString& name) override;
+
protected:
virtual void resizeEvent(QResizeEvent *event);
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 2a97b998..eda64c1d 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -34,6 +34,7 @@
#include "instancemanager.h"
#include
#include "previewdialog.h"
+#include "env.h"
#include "envmodule.h"
#include
@@ -85,6 +86,45 @@ QStringList toStringList(InputIterator current, InputIterator end)
return result;
}
+env::Process* getInterestingProcess(std::vector& processes)
+{
+ if (processes.empty()) {
+ return nullptr;
+ }
+
+ // Certain process names we wish to "hide" for aesthetic reason:
+ const std::vector hiddenList = {
+ QFileInfo(QCoreApplication::applicationFilePath()).fileName()
+ };
+
+ auto isHidden = [&](auto&& p) {
+ for (auto h : hiddenList) {
+ if (p.name().contains(h, Qt::CaseInsensitive)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+
+ for (auto&& root : processes) {
+ if (!isHidden(root)) {
+ return &root;
+ }
+
+ for (auto&& child : root.children()) {
+ if (!isHidden(child)) {
+ return &child;
+ }
+ }
+ }
+
+
+ // everything is hidden, just pick the first one
+ return &processes[0];
+}
+
OrganizerCore::OrganizerCore(Settings &settings)
: m_MainWindow(nullptr)
@@ -1367,12 +1407,31 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
bool OrganizerCore::waitForProcessCompletion(
HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock)
{
- const auto r = spawn::waitForProcess(handle, exitCode, uilock);
+ const auto tree = env::getProcessTree(handle);
+ std::vector processes = {tree};
+
+ const auto* interesting = getInterestingProcess(processes);
+ if (!interesting) {
+ return true;
+ }
+
+ if (uilock) {
+ uilock->setProcessInformation(interesting->pid(), interesting->name());
+ }
+
+ auto interestingHandle = interesting->openHandleForWait();
+ if (!interestingHandle) {
+ return true;
+ }
+
+ auto progress = [&]{ return uilock->unlockForced(); };
+ const auto r = spawn::waitForProcess(
+ interestingHandle.get(), exitCode, progress);
switch (r)
{
case spawn::WaitResults::Completed: // fall-through
- case spawn::WaitResults::Unlocked:
+ case spawn::WaitResults::Cancelled:
return true;
case spawn::WaitResults::Error: // fall-through
@@ -1395,60 +1454,39 @@ bool OrganizerCore::waitForAllUSVFSProcessesWithLock()
return r;
}
-HANDLE getInterestingProcess(
- const std::vector& handles,
- const std::vector& hidden, DWORD preferedParentPid)
-{
- HANDLE best_match = INVALID_HANDLE_VALUE;
- bool best_match_hidden = true;
-
- for (auto handle : handles) {
- const QString pname = env::getProcessName(handle);
-
- bool phidden = false;
- for (auto h : hidden)
- if (pname.contains(h, Qt::CaseInsensitive))
- phidden = true;
-
- bool pprefered = preferedParentPid && env::getProcessParentID(handle) == preferedParentPid;
-
- if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) {
- best_match = handle;
- best_match_hidden = phidden;
- }
-
- if (!phidden && pprefered)
- return best_match;
- }
-
- return best_match;
-}
-
bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
{
- // Certain process names we wish to "hide" for aesthetic reason:
- std::vector hiddenList;
- hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
-
for (;;) {
const auto handles = getRunningUSVFSProcesses();
if (handles.empty()) {
break;
}
- const auto interesting = getInterestingProcess(
- handles, hiddenList, GetCurrentProcessId());
+ std::vector processes;
+ for (auto&& h : handles) {
+ auto p = env::getProcessTree(h);
+ if (p.isValid()) {
+ processes.emplace_back(std::move(p));
+ }
+ }
+
+ const auto* interesting = getInterestingProcess(processes);
+ if (!interesting) {
+ break;
+ }
if (uilock) {
- const DWORD pid = ::GetProcessId(interesting);
- const QString processName = QString("%1 (%2)")
- .arg(env::getProcessName(interesting))
- .arg(pid);
+ uilock->setProcessInformation(interesting->pid(), interesting->name());
+ }
- uilock->setProcessName(processName);
+ auto interestingHandle = interesting->openHandleForWait();
+ if (!interestingHandle) {
+ break;
}
- const auto r = spawn::waitForProcess(interesting, nullptr, uilock);
+ auto progress = [&]{ return uilock->unlockForced(); };
+ const auto r = spawn::waitForProcess(
+ interestingHandle.get(), nullptr, progress);
switch (r)
{
@@ -1456,7 +1494,7 @@ bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
// this process is completed, check for others
break;
- case spawn::WaitResults::Unlocked:
+ case spawn::WaitResults::Cancelled:
// force unlocked
log::debug("waiting for process completion aborted by UI");
return true;
@@ -1468,7 +1506,7 @@ bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
}
}
- log::debug("Waiting for process completion successful");
+ log::debug("waiting for process completion successful");
return true;
}
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 1003024f..0ea60641 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -1095,32 +1095,25 @@ FileExecutionContext getFileExecutionContext(
}
WaitResults waitForProcess(
- HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
+ HANDLE handle, DWORD* exitCode, std::function progress)
{
if (handle == INVALID_HANDLE_VALUE) {
return WaitResults::Error;
}
- const DWORD pid = ::GetProcessId(handle);
- const QString processName = QString("%1 (%2)")
- .arg(env::getProcessName(handle))
- .arg(pid);
-
- if (uilock)
- uilock->setProcessName(processName);
-
- log::debug(
- "waiting for process completion '{}' ({})",
- processName, pid);
+ log::debug("waiting for completion on pid {}", ::GetProcessId(handle));
std::vector handles;
handles.push_back(handle);
std::vector exitCodes;
- const auto r = waitForProcesses(handles, exitCodes, uilock);
- if (exitCode && !exitCodes.empty()) {
- *exitCode = exitCodes[0];
+ const auto r = waitForProcesses(handles, exitCodes, progress);
+
+ if (r == WaitResults::Completed) {
+ if (exitCode && !exitCodes.empty()) {
+ *exitCode = exitCodes[0];
+ }
}
return r;
@@ -1128,7 +1121,7 @@ WaitResults waitForProcess(
WaitResults waitForProcesses(
const std::vector& handles, std::vector& exitCodes,
- ILockedWaitingForProcess* uilock)
+ std::function progress)
{
if (handles.empty()) {
return WaitResults::Completed;
@@ -1175,8 +1168,8 @@ WaitResults waitForProcesses(
QCoreApplication::sendPostedEvents();
QCoreApplication::processEvents();
- if (uilock && uilock->unlockForced()) {
- return WaitResults::Unlocked;
+ if (progress && progress()) {
+ return WaitResults::Cancelled;
}
}
}
diff --git a/src/spawn.h b/src/spawn.h
index 6b947f2f..ad50bde6 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -27,7 +27,6 @@ along with Mod Organizer. If not, see .
#include
class Settings;
-class ILockedWaitingForProcess;
namespace MOBase { class IPluginGame; }
@@ -130,15 +129,15 @@ enum class WaitResults
{
Completed = 1,
Error,
- Unlocked
+ Cancelled
};
WaitResults waitForProcess(
- HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock);
+ HANDLE handle, DWORD* exitCode, std::function progress);
WaitResults waitForProcesses(
const std::vector& handles, std::vector& exitCodes,
- ILockedWaitingForProcess* uilock);
+ std::function progress);
} // namespace
--
cgit v1.3.1
From 4d269c2e1a625e6d50b7e6272b4f474a921c6bfa Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Tue, 29 Oct 2019 11:34:06 -0400
Subject: split to processrunner added IUserInterface::qtWidget() put back
IUserInterface in OrganizerCore now that there's a way to get the widget
---
src/CMakeLists.txt | 3 +
src/iuserinterface.h | 2 +
src/main.cpp | 4 +-
src/mainwindow.cpp | 13 +-
src/mainwindow.h | 5 +-
src/modinfodialogconflicts.cpp | 2 +-
src/organizercore.cpp | 601 ++++++++------------------------------
src/organizercore.h | 60 +---
src/organizerproxy.cpp | 4 +-
src/processrunner.cpp | 634 +++++++++++++++++++++++++++++++++++++++++
src/processrunner.h | 104 +++++++
src/spawn.cpp | 198 -------------
src/spawn.h | 48 ----
13 files changed, 892 insertions(+), 786 deletions(-)
create mode 100644 src/processrunner.cpp
create mode 100644 src/processrunner.h
(limited to 'src/spawn.h')
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 180422ef..5e909760 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -143,6 +143,7 @@ SET(organizer_SRCS
envwindows.cpp
colortable.cpp
sanitychecks.cpp
+ processrunner.cpp
shared/windows_error.cpp
shared/error_report.cpp
@@ -266,6 +267,7 @@ SET(organizer_HDRS
envshortcut.h
envwindows.h
colortable.h
+ processrunner.h
shared/windows_error.h
shared/error_report.h
@@ -348,6 +350,7 @@ set(core
organizercore
organizerproxy
apiuseraccount
+ processrunner
)
set(dialogs
diff --git a/src/iuserinterface.h b/src/iuserinterface.h
index a309ed9b..91487aee 100644
--- a/src/iuserinterface.h
+++ b/src/iuserinterface.h
@@ -33,6 +33,8 @@ public:
virtual ILockedWaitingForProcess* lock() = 0;
virtual void unlock() = 0;
+
+ virtual QWidget* qtWidget() = 0;
};
#endif // IUSERINTERFACE_H
diff --git a/src/main.cpp b/src/main.cpp
index 49ecc084..5ed7da5d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -632,7 +632,7 @@ int runApplication(MOApplication &application, SingleInstance &instance,
if (MOShortcut shortcut{ arguments.at(1) }) {
if (shortcut.hasExecutable()) {
try {
- organizer.runShortcut(shortcut);
+ organizer.processRunner().runShortcut(shortcut);
return 0;
}
catch (const std::exception &e) {
@@ -653,7 +653,7 @@ int runApplication(MOApplication &application, SingleInstance &instance,
arguments.removeFirst(); // remove binary name
// pass the remaining parameters to the binary
try {
- organizer.runExecutableOrExecutableFile(
+ organizer.processRunner().runExecutableOrExecutableFile(
exeName, arguments, QString(), QString());
return 0;
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index bbb63333..ce5280a6 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1312,7 +1312,7 @@ bool MainWindow::canExit()
}
m_exitAfterWait = true;
- m_OrganizerCore.waitForAllUSVFSProcessesWithLock();
+ m_OrganizerCore.processRunner().waitForAllUSVFSProcessesWithLock();
if (!m_exitAfterWait) { // if operation cancelled
return false;
}
@@ -1536,7 +1536,7 @@ void MainWindow::startExeAction()
}
action->setEnabled(false);
- m_OrganizerCore.runExecutable(*itor);
+ m_OrganizerCore.processRunner().runExecutable(*itor);
action->setEnabled(true);
}
@@ -2294,6 +2294,11 @@ void MainWindow::unlock()
}
}
+QWidget* MainWindow::qtWidget()
+{
+ return this;
+}
+
void MainWindow::on_btnRefreshData_clicked()
{
m_OrganizerCore.refreshDirectoryStructure();
@@ -2363,7 +2368,7 @@ void MainWindow::on_startButton_clicked()
forcedLibraries.clear();
}
- m_OrganizerCore.runExecutableFile(
+ m_OrganizerCore.processRunner().runExecutableFile(
selectedExecutable->binaryInfo(),
selectedExecutable->arguments(),
selectedExecutable->workingDirectory().length() != 0 ?
@@ -5453,7 +5458,7 @@ void MainWindow::openDataFile()
const QString path = m_ContextItem->data(0, Qt::UserRole).toString();
const QFileInfo targetInfo(path);
- m_OrganizerCore.runFile(this, targetInfo);
+ m_OrganizerCore.processRunner().runFile(this, targetInfo);
}
void MainWindow::openDataOriginExplorer_clicked()
diff --git a/src/mainwindow.h b/src/mainwindow.h
index dbbd0bd9..19723480 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -118,8 +118,9 @@ public:
void processUpdates(Settings& settings);
- virtual ILockedWaitingForProcess* lock() override;
- virtual void unlock() override;
+ ILockedWaitingForProcess* lock() override;
+ void unlock() override;
+ QWidget* qtWidget() override;
bool addProfile();
void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives);
diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp
index 68b1be6b..758112cc 100644
--- a/src/modinfodialogconflicts.cpp
+++ b/src/modinfodialogconflicts.cpp
@@ -527,7 +527,7 @@ void ConflictsTab::openItems(QTreeView* tree)
// the menu item is only shown for a single selection, but handle all of them
// in case this changes
for_each_in_selection(tree, [&](const ConflictItem* item) {
- core().runFile(parentWidget(), item->fileName());
+ core().processRunner().runFile(parentWidget(), item->fileName());
return true;
});
}
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index eda64c1d..0f767f46 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -1,5 +1,4 @@
#include "organizercore.h"
-#include "mainwindow.h"
#include "delayedfilewriter.h"
#include "guessedvalue.h"
#include "imodinterface.h"
@@ -86,51 +85,13 @@ QStringList toStringList(InputIterator current, InputIterator end)
return result;
}
-env::Process* getInterestingProcess(std::vector& processes)
-{
- if (processes.empty()) {
- return nullptr;
- }
-
- // Certain process names we wish to "hide" for aesthetic reason:
- const std::vector hiddenList = {
- QFileInfo(QCoreApplication::applicationFilePath()).fileName()
- };
-
- auto isHidden = [&](auto&& p) {
- for (auto h : hiddenList) {
- if (p.name().contains(h, Qt::CaseInsensitive)) {
- return true;
- }
- }
-
- return false;
- };
-
-
- for (auto&& root : processes) {
- if (!isHidden(root)) {
- return &root;
- }
-
- for (auto&& child : root.children()) {
- if (!isHidden(child)) {
- return &child;
- }
- }
- }
-
-
- // everything is hidden, just pick the first one
- return &processes[0];
-}
-
OrganizerCore::OrganizerCore(Settings &settings)
- : m_MainWindow(nullptr)
+ : m_UserInterface(nullptr)
, m_PluginContainer(nullptr)
, m_GameName()
, m_CurrentProfile(nullptr)
+ , m_Runner(*this)
, m_Settings(settings)
, m_Updater(NexusInterface::instance(m_PluginContainer))
, m_AboutToRun()
@@ -190,7 +151,7 @@ OrganizerCore::~OrganizerCore()
m_RefresherThread.exit();
m_RefresherThread.wait();
- prepareStart();
+ saveCurrentProfile();
// profile has to be cleaned up before the modinfo-buffer is cleared
delete m_CurrentProfile;
@@ -249,43 +210,49 @@ void OrganizerCore::updateExecutablesList()
m_PluginContainer, m_Settings.interface().displayForeign(), managedGame());
}
-void OrganizerCore::setUserInterface(MainWindow* mainWindow)
+void OrganizerCore::setUserInterface(IUserInterface* ui)
{
storeSettings();
- m_MainWindow = mainWindow;
+ m_UserInterface = ui;
+
+ QWidget* w = nullptr;
+ if (m_UserInterface) {
+ w = m_UserInterface->qtWidget();
+ }
- if (m_MainWindow != nullptr) {
- connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), m_MainWindow,
+ if (w) {
+ connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), w,
SLOT(modlistChanged(QModelIndex, int)));
- connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), m_MainWindow,
+ connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), w,
SLOT(modlistChanged(QModelIndexList, int)));
- connect(&m_ModList, SIGNAL(showMessage(QString)), m_MainWindow,
+ connect(&m_ModList, SIGNAL(showMessage(QString)), w,
SLOT(showMessage(QString)));
- connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), m_MainWindow,
+ connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), w,
SLOT(modRenamed(QString, QString)));
- connect(&m_ModList, SIGNAL(modUninstalled(QString)), m_MainWindow,
+ connect(&m_ModList, SIGNAL(modUninstalled(QString)), w,
SLOT(modRemoved(QString)));
- connect(&m_ModList, SIGNAL(removeSelectedMods()), m_MainWindow,
+ connect(&m_ModList, SIGNAL(removeSelectedMods()), w,
SLOT(removeMod_clicked()));
- connect(&m_ModList, SIGNAL(clearOverwrite()), m_MainWindow,
+ connect(&m_ModList, SIGNAL(clearOverwrite()), w,
SLOT(clearOverwrite()));
- connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), m_MainWindow,
+ connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), w,
SLOT(displayColumnSelection(QPoint)));
- connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), m_MainWindow,
+ connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), w,
SLOT(fileMoved(QString, QString, QString)));
- connect(&m_ModList, SIGNAL(modorder_changed()), m_MainWindow,
+ connect(&m_ModList, SIGNAL(modorder_changed()), w,
SLOT(modorder_changed()));
- connect(&m_PluginList, SIGNAL(writePluginsList()), m_MainWindow,
+ connect(&m_PluginList, SIGNAL(writePluginsList()), w,
SLOT(esplist_changed()));
- connect(&m_PluginList, SIGNAL(esplist_changed()), m_MainWindow,
+ connect(&m_PluginList, SIGNAL(esplist_changed()), w,
SLOT(esplist_changed()));
- connect(&m_DownloadManager, SIGNAL(showMessage(QString)), m_MainWindow,
+ connect(&m_DownloadManager, SIGNAL(showMessage(QString)), w,
SLOT(showMessage(QString)));
}
- m_InstallationManager.setParentWidget(m_MainWindow);
- m_Updater.setUserInterface(m_MainWindow);
+ m_InstallationManager.setParentWidget(w);
+ m_Updater.setUserInterface(w);
+ m_Runner.setUserInterface(ui);
checkForUpdates();
}
@@ -294,7 +261,7 @@ void OrganizerCore::checkForUpdates()
{
// this currently wouldn't work reliably if the ui isn't initialized yet to
// display the result
- if (m_MainWindow != nullptr) {
+ if (m_UserInterface != nullptr) {
m_Updater.testForUpdate(m_Settings);
}
}
@@ -390,7 +357,7 @@ void OrganizerCore::externalMessage(const QString &message)
{
if (MOShortcut moshortcut{ message } ) {
if(moshortcut.hasExecutable())
- runShortcut(moshortcut);
+ m_Runner.runShortcut(moshortcut);
}
else if (isNxmLink(message)) {
MessageDialog::showMessage(tr("Download started"), qApp->activeWindow());
@@ -754,13 +721,13 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName,
int modIndex = ModInfo::getIndex(modName);
if (modIndex != UINT_MAX) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
- if (hasIniTweaks && (m_MainWindow != nullptr)
+ if (hasIniTweaks && (m_UserInterface != nullptr)
&& (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"),
tr("This mod contains ini tweaks. Do you "
"want to configure them now?"),
QMessageBox::Yes | QMessageBox::No)
== QMessageBox::Yes)) {
- m_MainWindow->displayModInformation(
+ m_UserInterface->displayModInformation(
modInfo, modIndex, ModInfoTabIDs::IniFiles);
}
m_ModInstalled(modName);
@@ -821,13 +788,13 @@ void OrganizerCore::installDownload(int index)
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
modInfo->addInstalledFile(modID, fileID);
- if (hasIniTweaks && m_MainWindow != nullptr
+ if (hasIniTweaks && m_UserInterface != nullptr
&& (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"),
tr("This mod contains ini tweaks. Do you "
"want to configure them now?"),
QMessageBox::Yes | QMessageBox::No)
== QMessageBox::Yes)) {
- m_MainWindow->displayModInformation(
+ m_UserInterface->displayModInformation(
modInfo, modIndex, ModInfoTabIDs::IniFiles);
}
@@ -1104,412 +1071,6 @@ bool OrganizerCore::previewFile(
return true;
}
-bool OrganizerCore::runFile(
- QWidget* parent, const QFileInfo& targetInfo)
-{
- const auto fec = spawn::getFileExecutionContext(parent, targetInfo);
-
- switch (fec.type)
- {
- case spawn::FileExecutionTypes::Executable:
- {
- runExecutableFile(fec.binary, fec.arguments, targetInfo.absoluteDir());
- return true;
- }
-
- case spawn::FileExecutionTypes::Other: // fall-through
- default:
- {
- auto r = shell::Open(targetInfo.absoluteFilePath());
- if (!r.success()) {
- return false;
- }
-
- // not all files will return a valid handle even if opening them was
- // successful, such as inproc handlers (like the photo viewer)
- if (r.processHandle() != INVALID_HANDLE_VALUE) {
- // steal because it gets closed after the wait
- return waitForProcessCompletionWithLock(r.stealProcessHandle(), nullptr);
- }
-
- return true;
- }
- }
-}
-
-bool OrganizerCore::runExecutableFile(
- const QFileInfo &binary, const QString &arguments,
- const QDir ¤tDirectory, const QString &steamAppID,
- const QString &customOverwrite,
- const QList &forcedLibraries,
- bool refresh)
-{
- DWORD processExitCode = 0;
- HANDLE processHandle = spawnAndWait(
- binary, arguments, m_CurrentProfile->name(), currentDirectory, steamAppID,
- customOverwrite, forcedLibraries, &processExitCode);
-
- if (processHandle == INVALID_HANDLE_VALUE) {
- // failed
- return false;
- }
-
- if (refresh) {
- refreshDirectoryStructure();
-
- // need to remove our stored load order because it may be outdated if a foreign tool changed the
- // file time. After removing that file, refreshESPList will use the file time as the order
- if (managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) {
- log::debug("removing loadorder.txt");
- QFile::remove(m_CurrentProfile->getLoadOrderFileName());
- }
-
- refreshDirectoryStructure();
-
- refreshESPList(true);
- savePluginList();
-
- //These callbacks should not fiddle with directoy structure and ESPs.
- m_FinishedRun(binary.absoluteFilePath(), processExitCode);
- }
-
- return true;
-}
-
-bool OrganizerCore::runExecutable(const Executable& exe, bool refresh)
-{
- const QString customOverwrite = m_CurrentProfile->setting(
- "custom_overwrites", exe.title()).toString();
-
- QList forcedLibraries;
-
- if (m_CurrentProfile->forcedLibrariesEnabled(exe.title())) {
- forcedLibraries = m_CurrentProfile->determineForcedLibraries(exe.title());
- }
-
- return runExecutableFile(
- exe.binaryInfo(),
- exe.arguments(),
- exe.workingDirectory().length() != 0 ? exe.workingDirectory() : exe.binaryInfo().absolutePath(),
- exe.steamAppID(),
- customOverwrite,
- forcedLibraries,
- refresh);
-}
-
-bool OrganizerCore::runShortcut(const MOShortcut& shortcut)
-{
- if (shortcut.hasInstance() && shortcut.instance() != InstanceManager::instance().currentInstance()) {
- throw std::runtime_error(
- QString("Refusing to run executable from different instance %1:%2")
- .arg(shortcut.instance(),shortcut.executable())
- .toLocal8Bit().constData());
- }
-
- const Executable& exe = m_ExecutablesList.get(shortcut.executable());
- return runExecutable(exe, false);
-}
-
-HANDLE OrganizerCore::runExecutableOrExecutableFile(
- const QString &executable, const QStringList &args, const QString &cwd,
- const QString &profile, const QString &forcedCustomOverwrite,
- bool ignoreCustomOverwrite)
-{
- QString profileName = profile;
- if (profile == "") {
- if (m_CurrentProfile != nullptr) {
- profileName = m_CurrentProfile->name();
- } else {
- throw MyException(tr("No profile set"));
- }
- }
-
- QFileInfo binary;
- QString arguments = args.join(" ");
- QString currentDirectory = cwd;
- QString steamAppID;
- QString customOverwrite;
- QList forcedLibraries;
-
- if (executable.contains('\\') || executable.contains('/')) {
- // file path
-
- binary = QFileInfo(executable);
- if (binary.isRelative()) {
- // relative path, should be relative to game directory
- binary = managedGame()->gameDirectory().absoluteFilePath(executable);
- }
-
- if (currentDirectory == "") {
- currentDirectory = binary.absolutePath();
- }
-
- try {
- const Executable& exe = m_ExecutablesList.getByBinary(binary);
- steamAppID = exe.steamAppID();
- customOverwrite = m_CurrentProfile->setting("custom_overwrites", exe.title()).toString();
- if (m_CurrentProfile->forcedLibrariesEnabled(exe.title())) {
- forcedLibraries = m_CurrentProfile->determineForcedLibraries(exe.title());
- }
- } catch (const std::runtime_error &) {
- // nop
- }
- } else {
- // only a file name, search executables list
- try {
- const Executable &exe = m_ExecutablesList.get(executable);
- steamAppID = exe.steamAppID();
- customOverwrite = m_CurrentProfile->setting("custom_overwrites", exe.title()).toString();
- if (m_CurrentProfile->forcedLibrariesEnabled(exe.title())) {
- forcedLibraries = m_CurrentProfile->determineForcedLibraries(exe.title());
- }
- if (arguments == "") {
- arguments = exe.arguments();
- }
- binary = exe.binaryInfo();
- if (currentDirectory == "") {
- currentDirectory = exe.workingDirectory();
- }
- } catch (const std::runtime_error &) {
- log::warn("\"{}\" not set up as executable", executable);
- binary = QFileInfo(executable);
- }
- }
-
- if (!forcedCustomOverwrite.isEmpty())
- customOverwrite = forcedCustomOverwrite;
-
- if (ignoreCustomOverwrite)
- customOverwrite.clear();
-
- return spawnAndWait(binary,
- arguments,
- profileName,
- currentDirectory,
- steamAppID,
- customOverwrite,
- forcedLibraries);
-}
-
-HANDLE OrganizerCore::spawnAndWait(
- const QFileInfo &binary, const QString &arguments, const QString &profileName,
- const QDir ¤tDirectory, const QString &steamAppID,
- const QString &customOverwrite,
- const QList &forcedLibraries,
- LPDWORD exitCode)
-{
- spawn::SpawnParameters sp;
- sp.binary = binary;
- sp.arguments = arguments;
- sp.currentDirectory = currentDirectory;
- sp.steamAppID = steamAppID;
- sp.hooked = true;
-
- prepareStart();
-
- while (m_DirectoryUpdate) {
- ::Sleep(100);
- QCoreApplication::processEvents();
- }
-
- // need to make sure all data is saved before we start the application
- if (m_CurrentProfile != nullptr) {
- m_CurrentProfile->writeModlistNow(true);
- }
-
- // TODO: should also pass arguments
- if (!m_AboutToRun(binary.absoluteFilePath())) {
- log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath());
- return INVALID_HANDLE_VALUE;
- }
-
- try {
- m_USVFS.updateMapping(fileMapping(profileName, customOverwrite));
- m_USVFS.updateForcedLibraries(forcedLibraries);
-
- } catch (const UsvfsConnectorException &e) {
- log::debug(e.what());
- return INVALID_HANDLE_VALUE;
- } catch (const std::exception &e) {
- QMessageBox::warning(m_MainWindow, tr("Error"), e.what());
- return INVALID_HANDLE_VALUE;
- }
-
- HANDLE handle = spawn::Spawner()
- .spawn(m_MainWindow, m_GamePlugin, sp, m_Settings)
- .releaseHandle();
-
- if (handle == INVALID_HANDLE_VALUE) {
- // failed
- return INVALID_HANDLE_VALUE;
- }
-
- waitForProcessCompletionWithLock(handle, exitCode);
- return handle;
-}
-
-void OrganizerCore::withLock(std::function f)
-{
- std::unique_ptr dlg;
- ILockedWaitingForProcess* uilock = nullptr;
-
- if (m_MainWindow != nullptr) {
- uilock = m_MainWindow->lock();
- }
- else {
- // i.e. when running command line shortcuts there is no user interface
- dlg.reset(new LockedDialog);
- dlg->show();
- dlg->setEnabled(true);
- uilock = dlg.get();
- }
-
- ON_BLOCK_EXIT([&]() {
- if (m_MainWindow != nullptr) {
- m_MainWindow->unlock();
- } });
-
- f(uilock);
-}
-
-bool OrganizerCore::waitForProcessCompletionWithLock(
- HANDLE handle, LPDWORD exitCode)
-{
- if (!Settings::instance().interface().lockGUI()) {
- return true;
- }
-
- bool r = false;
-
- withLock([&](auto* uilock) {
- DWORD ignoreExitCode;
- r = waitForProcessCompletion(handle, exitCode ? exitCode : &ignoreExitCode, uilock);
- cycleDiagnostics();
- });
-
- return r;
-}
-
-bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
-{
- if (!Settings::instance().interface().lockGUI())
- return true;
-
- bool r = false;
-
- withLock([&](auto* uilock) {
- r = waitForProcessCompletion(handle, exitCode, uilock);
- });
-
- return r;
-}
-
-bool OrganizerCore::waitForProcessCompletion(
- HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock)
-{
- const auto tree = env::getProcessTree(handle);
- std::vector processes = {tree};
-
- const auto* interesting = getInterestingProcess(processes);
- if (!interesting) {
- return true;
- }
-
- if (uilock) {
- uilock->setProcessInformation(interesting->pid(), interesting->name());
- }
-
- auto interestingHandle = interesting->openHandleForWait();
- if (!interestingHandle) {
- return true;
- }
-
- auto progress = [&]{ return uilock->unlockForced(); };
- const auto r = spawn::waitForProcess(
- interestingHandle.get(), exitCode, progress);
-
- switch (r)
- {
- case spawn::WaitResults::Completed: // fall-through
- case spawn::WaitResults::Cancelled:
- return true;
-
- case spawn::WaitResults::Error: // fall-through
- default:
- return false;
- }
-}
-
-bool OrganizerCore::waitForAllUSVFSProcessesWithLock()
-{
- if (!Settings::instance().interface().lockGUI())
- return true;
-
- bool r = false;
-
- withLock([&](auto* uilock) {
- r = waitForAllUSVFSProcesses(uilock);
- });
-
- return r;
-}
-
-bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
-{
- for (;;) {
- const auto handles = getRunningUSVFSProcesses();
- if (handles.empty()) {
- break;
- }
-
- std::vector processes;
- for (auto&& h : handles) {
- auto p = env::getProcessTree(h);
- if (p.isValid()) {
- processes.emplace_back(std::move(p));
- }
- }
-
- const auto* interesting = getInterestingProcess(processes);
- if (!interesting) {
- break;
- }
-
- if (uilock) {
- uilock->setProcessInformation(interesting->pid(), interesting->name());
- }
-
- auto interestingHandle = interesting->openHandleForWait();
- if (!interestingHandle) {
- break;
- }
-
- auto progress = [&]{ return uilock->unlockForced(); };
- const auto r = spawn::waitForProcess(
- interestingHandle.get(), nullptr, progress);
-
- switch (r)
- {
- case spawn::WaitResults::Completed:
- // this process is completed, check for others
- break;
-
- case spawn::WaitResults::Cancelled:
- // force unlocked
- log::debug("waiting for process completion aborted by UI");
- return true;
-
- case spawn::WaitResults::Error: // fall-through
- default:
- log::debug("waiting for process completion not successful");
- return false;
- }
- }
-
- log::debug("waiting for process completion successful");
- return true;
-}
-
bool OrganizerCore::onAboutToRun(
const std::function &func)
{
@@ -1594,8 +1155,8 @@ void OrganizerCore::refreshBSAList()
m_ActiveArchives = m_DefaultArchives;
}
- if (m_MainWindow != nullptr) {
- m_MainWindow->updateBSAList(m_DefaultArchives, m_ActiveArchives);
+ if (m_UserInterface != nullptr) {
+ m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives);
}
m_ArchivesInit = true;
@@ -1711,8 +1272,8 @@ void OrganizerCore::updateModsInDirectoryStructure(QMaparchivesWriter().writeImmediately(false);
+ if (m_UserInterface != nullptr) {
+ m_UserInterface->archivesWriter().writeImmediately(false);
}
std::vector archives = enabledArchives();
@@ -1896,8 +1457,8 @@ void OrganizerCore::modStatusChanged(unsigned int index)
= m_DirectoryStructure->getOriginByName(ToWString(modInfo->name()));
origin.enable(false);
}
- if (m_MainWindow != nullptr) {
- m_MainWindow->archivesWriter().write();
+ if (m_UserInterface != nullptr) {
+ m_UserInterface->archivesWriter().write();
}
}
modInfo->clearCaches();
@@ -1946,8 +1507,8 @@ void OrganizerCore::modStatusChanged(QList index) {
origin.enable(false);
}
}
- if (m_MainWindow != nullptr) {
- m_MainWindow->archivesWriter().write();
+ if (m_UserInterface != nullptr) {
+ m_UserInterface->archivesWriter().write();
}
}
@@ -2122,8 +1683,8 @@ bool OrganizerCore::saveCurrentLists()
try {
savePluginList();
- if (m_MainWindow != nullptr) {
- m_MainWindow->archivesWriter().write();
+ if (m_UserInterface != nullptr) {
+ m_UserInterface->archivesWriter().write();
}
} catch (const std::exception &e) {
reportError(tr("failed to save load order: %1").arg(e.what()));
@@ -2147,11 +1708,12 @@ void OrganizerCore::savePluginList()
m_PluginList.saveLoadOrder(*m_DirectoryStructure);
}
-void OrganizerCore::prepareStart()
+void OrganizerCore::saveCurrentProfile()
{
if (m_CurrentProfile == nullptr) {
return;
}
+
m_CurrentProfile->writeModlist();
m_CurrentProfile->createTweakedIniFile();
saveCurrentLists();
@@ -2159,6 +1721,79 @@ void OrganizerCore::prepareStart()
storeSettings();
}
+ProcessRunner& OrganizerCore::processRunner()
+{
+ return m_Runner;
+}
+
+bool OrganizerCore::beforeRun(
+ const QFileInfo& binary, const QString& profileName,
+ const QString& customOverwrite,
+ const QList& forcedLibraries)
+{
+ saveCurrentProfile();
+
+ while (m_DirectoryUpdate) {
+ ::Sleep(100);
+ QCoreApplication::processEvents();
+ }
+
+ // need to make sure all data is saved before we start the application
+ if (m_CurrentProfile != nullptr) {
+ m_CurrentProfile->writeModlistNow(true);
+ }
+
+ // TODO: should also pass arguments
+ if (!m_AboutToRun(binary.absoluteFilePath())) {
+ log::debug("start of \"{}\" cancelled by plugin", binary.absoluteFilePath());
+ return false;
+ }
+
+ try
+ {
+ m_USVFS.updateMapping(fileMapping(profileName, customOverwrite));
+ m_USVFS.updateForcedLibraries(forcedLibraries);
+ }
+ catch (const UsvfsConnectorException &e)
+ {
+ log::debug(e.what());
+ return false;
+ }
+ catch (const std::exception &e)
+ {
+ QWidget* w = nullptr;
+ if (m_UserInterface) {
+ w = m_UserInterface->qtWidget();
+ }
+ QMessageBox::warning(w, tr("Error"), e.what());
+ return false;
+ }
+
+ return true;
+}
+
+void OrganizerCore::afterRun(const QFileInfo& binary, DWORD exitCode)
+{
+ refreshDirectoryStructure();
+
+ // need to remove our stored load order because it may be outdated if a
+ // foreign tool changed the file time. After removing that file,
+ // refreshESPList will use the file time as the order
+ if (managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) {
+ log::debug("removing loadorder.txt");
+ QFile::remove(m_CurrentProfile->getLoadOrderFileName());
+ }
+
+ refreshDirectoryStructure();
+
+ refreshESPList(true);
+ savePluginList();
+ cycleDiagnostics();
+
+ //These callbacks should not fiddle with directoy structure and ESPs.
+ m_FinishedRun(binary.absoluteFilePath(), exitCode);
+}
+
std::vector OrganizerCore::fileMapping(const QString &profileName,
const QString &customOverwrite)
{
diff --git a/src/organizercore.h b/src/organizercore.h
index ffdb6830..2252c118 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -3,7 +3,6 @@
#include "selfupdater.h"
-#include "ilockedwaitingforprocess.h"
#include "settings.h"
#include "modlist.h"
#include "modinfo.h"
@@ -14,6 +13,7 @@
#include "executableslist.h"
#include "usvfsconnector.h"
#include "moshortcut.h"
+#include "processrunner.h"
#include
#include
#include
@@ -26,7 +26,7 @@
class ModListSortProxy;
class PluginListSortProxy;
class Profile;
-class MainWindow;
+class IUserInterface;
namespace MOBase {
template class GuessedValue;
@@ -97,7 +97,7 @@ public:
~OrganizerCore();
- void setUserInterface(MainWindow* mainWindow);
+ void setUserInterface(IUserInterface* ui);
void connectPlugins(PluginContainer *container);
void disconnectPlugins();
@@ -134,7 +134,14 @@ public:
bool saveCurrentLists();
- void prepareStart();
+ ProcessRunner& processRunner();
+
+ bool beforeRun(
+ const QFileInfo& binary, const QString& profileName,
+ const QString& customOverwrite,
+ const QList& forcedLibraries);
+
+ void afterRun(const QFileInfo& binary, DWORD exitCode);
void refreshESPList(bool force = false);
void refreshBSAList();
@@ -149,27 +156,6 @@ public:
bool previewFileWithAlternatives(QWidget* parent, QString filename, int selectedOrigin=-1);
bool previewFile(QWidget* parent, const QString& originName, const QString& path);
- bool runFile(QWidget* parent, const QFileInfo& targetInfo);
-
- bool runExecutableFile(
- const QFileInfo &binary, const QString &arguments,
- const QDir ¤tDirectory, const QString &steamAppID={},
- const QString &customOverwrite={},
- const QList &forcedLibraries={},
- bool refresh=true);
-
- bool runExecutable(const Executable& exe, bool refresh=true);
-
- bool runShortcut(const MOShortcut& shortcut);
-
- HANDLE runExecutableOrExecutableFile(
- const QString &executable, const QStringList &args, const QString &cwd,
- const QString &profile, const QString &forcedCustomOverwrite = "",
- bool ignoreCustomOverwrite = false);
-
- bool waitForApplication(HANDLE processHandle, LPDWORD exitCode = nullptr);
- bool waitForAllUSVFSProcessesWithLock();
-
void loginSuccessfulUpdate(bool necessary);
void loginFailedUpdate(const QString &message);
@@ -273,6 +259,7 @@ signals:
private:
+ void saveCurrentProfile();
void storeSettings();
bool queryApi(QString &apiKey);
@@ -298,26 +285,6 @@ private:
const MOShared::DirectoryEntry *directoryEntry,
int createDestination);
- HANDLE spawnAndWait(const QFileInfo &binary, const QString &arguments,
- const QString &profileName,
- const QDir ¤tDirectory,
- const QString &steamAppID,
- const QString &customOverwrite,
- const QList &forcedLibraries = QList(),
- LPDWORD exitCode = nullptr);
-
- bool waitForProcessCompletionWithLock(HANDLE handle, LPDWORD exitCode);
-
- bool waitForProcessCompletion(
- HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock);
-
- bool waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock);
-
- void withLock(std::function f);
-
- HANDLE findAndOpenAUSVFSProcess(
- const std::vector& hiddenList, DWORD preferedParentPid);
-
private slots:
void directory_refreshed();
@@ -331,13 +298,14 @@ private:
static const unsigned int PROBLEM_MO1SCRIPTEXTENDERWORKAROUND = 1;
private:
- MainWindow* m_MainWindow;
+ IUserInterface* m_UserInterface;
PluginContainer *m_PluginContainer;
QString m_GameName;
MOBase::IPluginGame *m_GamePlugin;
Profile *m_CurrentProfile;
+ ProcessRunner m_Runner;
Settings& m_Settings;
SelfUpdater m_Updater;
diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp
index 2ea1761a..75b3ea41 100644
--- a/src/organizerproxy.cpp
+++ b/src/organizerproxy.cpp
@@ -112,14 +112,14 @@ HANDLE OrganizerProxy::startApplication(
const QString &profile, const QString &forcedCustomOverwrite,
bool ignoreCustomOverwrite)
{
- return m_Proxied->runExecutableOrExecutableFile(
+ return m_Proxied->processRunner().runExecutableOrExecutableFile(
executable, args, cwd, profile,
forcedCustomOverwrite, ignoreCustomOverwrite);
}
bool OrganizerProxy::waitForApplication(HANDLE handle, LPDWORD exitCode) const
{
- return m_Proxied->waitForApplication(handle, exitCode);
+ return m_Proxied->processRunner().waitForApplication(handle, exitCode);
}
bool OrganizerProxy::onAboutToRun(const std::function &func)
diff --git a/src/processrunner.cpp b/src/processrunner.cpp
new file mode 100644
index 00000000..81c4b99e
--- /dev/null
+++ b/src/processrunner.cpp
@@ -0,0 +1,634 @@
+#include "processrunner.h"
+#include "organizercore.h"
+#include "instancemanager.h"
+#include "lockeddialog.h"
+#include "iuserinterface.h"
+#include "envmodule.h"
+#include
+
+using namespace MOBase;
+
+void adjustForVirtualized(
+ const IPluginGame* game, spawn::SpawnParameters& sp, const Settings& settings)
+{
+ const QString modsPath = settings.paths().mods();
+
+ // Check if this a request with either an executable or a working directory
+ // under our mods folder then will start the process in a virtualized
+ // "environment" with the appropriate paths fixed:
+ // (i.e. mods\FNIS\path\exe => game\data\path\exe)
+ QString cwdPath = sp.currentDirectory.absolutePath();
+ bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive);
+ QString binPath = sp.binary.absoluteFilePath();
+ bool virtualizedBin = binPath.startsWith(modsPath, Qt::CaseInsensitive);
+ if (virtualizedCwd || virtualizedBin) {
+ if (virtualizedCwd) {
+ int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1);
+ QString adjustedCwd = cwdPath.mid(cwdOffset, -1);
+ cwdPath = game->dataDirectory().absolutePath();
+ if (cwdOffset >= 0)
+ cwdPath += adjustedCwd;
+
+ }
+
+ if (virtualizedBin) {
+ int binOffset = binPath.indexOf('/', modsPath.length() + 1);
+ QString adjustedBin = binPath.mid(binOffset, -1);
+ binPath = game->dataDirectory().absolutePath();
+ if (binOffset >= 0)
+ binPath += adjustedBin;
+ }
+
+ QString cmdline
+ = QString("launch \"%1\" \"%2\" %3")
+ .arg(QDir::toNativeSeparators(cwdPath),
+ QDir::toNativeSeparators(binPath), sp.arguments);
+
+ sp.binary = QFileInfo(QCoreApplication::applicationFilePath());
+ sp.arguments = cmdline;
+ sp.currentDirectory.setPath(QCoreApplication::applicationDirPath());
+ }
+}
+
+env::Process* getInterestingProcess(std::vector& processes)
+{
+ if (processes.empty()) {
+ return nullptr;
+ }
+
+ // Certain process names we wish to "hide" for aesthetic reason:
+ const std::vector hiddenList = {
+ QFileInfo(QCoreApplication::applicationFilePath()).fileName()
+ };
+
+ auto isHidden = [&](auto&& p) {
+ for (auto h : hiddenList) {
+ if (p.name().contains(h, Qt::CaseInsensitive)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+
+ for (auto&& root : processes) {
+ if (!isHidden(root)) {
+ return &root;
+ }
+
+ for (auto&& child : root.children()) {
+ if (!isHidden(child)) {
+ return &child;
+ }
+ }
+ }
+
+
+ // everything is hidden, just pick the first one
+ return &processes[0];
+}
+
+
+SpawnedProcess::SpawnedProcess(HANDLE handle, spawn::SpawnParameters sp)
+ : m_handle(handle), m_parameters(std::move(sp))
+{
+}
+
+SpawnedProcess::SpawnedProcess(SpawnedProcess&& other)
+ : m_handle(other.m_handle), m_parameters(std::move(other.m_parameters))
+{
+ other.m_handle = INVALID_HANDLE_VALUE;
+}
+
+SpawnedProcess& SpawnedProcess::operator=(SpawnedProcess&& other)
+{
+ if (this != &other) {
+ destroy();
+
+ m_handle = other.m_handle;
+ other.m_handle = INVALID_HANDLE_VALUE;
+
+ m_parameters = std::move(other.m_parameters);
+ }
+
+ return *this;
+}
+
+SpawnedProcess::~SpawnedProcess()
+{
+ destroy();
+}
+
+HANDLE SpawnedProcess::releaseHandle()
+{
+ const auto h = m_handle;
+ m_handle = INVALID_HANDLE_VALUE;
+ return h;
+}
+
+void SpawnedProcess::destroy()
+{
+ if (m_handle != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(m_handle);
+ m_handle = INVALID_HANDLE_VALUE;
+ }
+}
+
+
+ProcessRunner::ProcessRunner(OrganizerCore& core)
+ : m_core(core), m_ui(nullptr)
+{
+}
+
+void ProcessRunner::setUserInterface(IUserInterface* ui)
+{
+ m_ui = ui;
+}
+
+bool ProcessRunner::runFile(QWidget* parent, const QFileInfo& targetInfo)
+{
+ if (!parent && m_ui) {
+ parent = m_ui->qtWidget();
+ }
+
+ const auto fec = spawn::getFileExecutionContext(parent, targetInfo);
+
+ switch (fec.type)
+ {
+ case spawn::FileExecutionTypes::Executable:
+ {
+ runExecutableFile(fec.binary, fec.arguments, targetInfo.absoluteDir());
+ return true;
+ }
+
+ case spawn::FileExecutionTypes::Other: // fall-through
+ default:
+ {
+ auto r = shell::Open(targetInfo.absoluteFilePath());
+ if (!r.success()) {
+ return false;
+ }
+
+ // not all files will return a valid handle even if opening them was
+ // successful, such as inproc handlers (like the photo viewer)
+ if (r.processHandle() != INVALID_HANDLE_VALUE) {
+ // steal because it gets closed after the wait
+ return waitForProcessCompletionWithLock(r.stealProcessHandle(), nullptr);
+ }
+
+ return true;
+ }
+ }
+}
+
+bool ProcessRunner::runExecutableFile(
+ const QFileInfo &binary, const QString &arguments,
+ const QDir ¤tDirectory, const QString &steamAppID,
+ const QString &customOverwrite,
+ const QList &forcedLibraries,
+ bool refresh)
+{
+ DWORD processExitCode = 0;
+ HANDLE processHandle = spawnAndWait(
+ binary, arguments, m_core.currentProfile()->name(),
+ currentDirectory, steamAppID, customOverwrite, forcedLibraries,
+ &processExitCode);
+
+ if (processHandle == INVALID_HANDLE_VALUE) {
+ // failed
+ return false;
+ }
+
+ if (refresh) {
+ m_core.afterRun(binary, processExitCode);
+ }
+
+ return true;
+}
+
+bool ProcessRunner::runExecutable(const Executable& exe, bool refresh)
+{
+ const auto* profile = m_core.currentProfile();
+ if (!profile) {
+ throw MyException(QObject::tr("No profile set"));
+ }
+
+ const QString customOverwrite = profile->setting(
+ "custom_overwrites", exe.title()).toString();
+
+ QList forcedLibraries;
+
+ if (profile->forcedLibrariesEnabled(exe.title())) {
+ forcedLibraries = profile->determineForcedLibraries(exe.title());
+ }
+
+ return runExecutableFile(
+ exe.binaryInfo(),
+ exe.arguments(),
+ exe.workingDirectory().length() != 0 ? exe.workingDirectory() : exe.binaryInfo().absolutePath(),
+ exe.steamAppID(),
+ customOverwrite,
+ forcedLibraries,
+ refresh);
+}
+
+bool ProcessRunner::runShortcut(const MOShortcut& shortcut)
+{
+ if (shortcut.hasInstance() && shortcut.instance() != InstanceManager::instance().currentInstance()) {
+ throw std::runtime_error(
+ QString("Refusing to run executable from different instance %1:%2")
+ .arg(shortcut.instance(),shortcut.executable())
+ .toLocal8Bit().constData());
+ }
+
+ const Executable& exe = m_core.executablesList()->get(shortcut.executable());
+ return runExecutable(exe, false);
+}
+
+HANDLE ProcessRunner::runExecutableOrExecutableFile(
+ const QString& executable, const QStringList &args, const QString &cwd,
+ const QString& profileOverride, const QString &forcedCustomOverwrite,
+ bool ignoreCustomOverwrite)
+{
+ const auto* profile = m_core.currentProfile();
+ if (!profile) {
+ throw MyException(QObject::tr("No profile set"));
+ }
+
+ QString profileName = profileOverride;
+ if (profileName == "") {
+ profileName = profile->name();
+ }
+
+ QFileInfo binary;
+ QString arguments = args.join(" ");
+ QString currentDirectory = cwd;
+ QString steamAppID;
+ QString customOverwrite;
+ QList forcedLibraries;
+
+ if (executable.contains('\\') || executable.contains('/')) {
+ // file path
+
+ binary = QFileInfo(executable);
+ if (binary.isRelative()) {
+ // relative path, should be relative to game directory
+ binary = m_core.managedGame()->gameDirectory().absoluteFilePath(executable);
+ }
+
+ if (currentDirectory == "") {
+ currentDirectory = binary.absolutePath();
+ }
+
+ try {
+ const Executable& exe = m_core.executablesList()->getByBinary(binary);
+ steamAppID = exe.steamAppID();
+ customOverwrite = profile->setting("custom_overwrites", exe.title()).toString();
+ if (profile->forcedLibrariesEnabled(exe.title())) {
+ forcedLibraries = profile->determineForcedLibraries(exe.title());
+ }
+ } catch (const std::runtime_error &) {
+ // nop
+ }
+ } else {
+ // only a file name, search executables list
+ try {
+ const Executable &exe = m_core.executablesList()->get(executable);
+ steamAppID = exe.steamAppID();
+ customOverwrite = profile->setting("custom_overwrites", exe.title()).toString();
+ if (profile->forcedLibrariesEnabled(exe.title())) {
+ forcedLibraries = profile->determineForcedLibraries(exe.title());
+ }
+ if (arguments == "") {
+ arguments = exe.arguments();
+ }
+ binary = exe.binaryInfo();
+ if (currentDirectory == "") {
+ currentDirectory = exe.workingDirectory();
+ }
+ } catch (const std::runtime_error &) {
+ log::warn("\"{}\" not set up as executable", executable);
+ binary = QFileInfo(executable);
+ }
+ }
+
+ if (!forcedCustomOverwrite.isEmpty())
+ customOverwrite = forcedCustomOverwrite;
+
+ if (ignoreCustomOverwrite)
+ customOverwrite.clear();
+
+ return spawnAndWait(
+ binary,
+ arguments,
+ profileName,
+ currentDirectory,
+ steamAppID,
+ customOverwrite,
+ forcedLibraries);
+}
+
+HANDLE ProcessRunner::spawnAndWait(
+ const QFileInfo &binary, const QString &arguments, const QString &profileName,
+ const QDir ¤tDirectory, const QString &steamAppID,
+ const QString &customOverwrite,
+ const QList &forcedLibraries,
+ LPDWORD exitCode)
+{
+ spawn::SpawnParameters sp;
+ sp.binary = binary;
+ sp.arguments = arguments;
+ sp.currentDirectory = currentDirectory;
+ sp.steamAppID = steamAppID;
+ sp.hooked = true;
+
+ if (!m_core.beforeRun(binary, profileName, customOverwrite, forcedLibraries)) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ HANDLE handle = spawn(sp).releaseHandle();
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ // failed
+ return INVALID_HANDLE_VALUE;
+ }
+
+ waitForProcessCompletionWithLock(handle, exitCode);
+ return handle;
+}
+
+SpawnedProcess ProcessRunner::spawn(spawn::SpawnParameters sp)
+{
+ QWidget* parent = nullptr;
+ if (m_ui) {
+ parent = m_ui->qtWidget();
+ }
+
+ if (!checkBinary(parent, sp)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ const auto* game = m_core.managedGame();
+ auto& settings = m_core.settings();
+
+ if (!checkSteam(parent, sp, game->gameDirectory(), sp.steamAppID, settings)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!checkEnvironment(parent, sp)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!checkBlacklist(parent, sp, settings)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ adjustForVirtualized(game, sp, settings);
+
+ return {startBinary(parent, sp), sp};
+}
+
+void ProcessRunner::withLock(std::function f)
+{
+ std::unique_ptr dlg;
+ ILockedWaitingForProcess* uilock = nullptr;
+
+ if (m_ui != nullptr) {
+ uilock = m_ui->lock();
+ }
+ else {
+ // i.e. when running command line shortcuts there is no user interface
+ dlg.reset(new LockedDialog);
+ dlg->show();
+ dlg->setEnabled(true);
+ uilock = dlg.get();
+ }
+
+ Guard g([&]() {
+ if (m_ui != nullptr) {
+ m_ui->unlock();
+ }
+ });
+
+ f(uilock);
+}
+
+bool ProcessRunner::waitForProcessCompletionWithLock(
+ HANDLE handle, LPDWORD exitCode)
+{
+ if (!Settings::instance().interface().lockGUI()) {
+ return true;
+ }
+
+ bool r = false;
+
+ withLock([&](auto* uilock) {
+ DWORD ignoreExitCode;
+ r = waitForProcessCompletion(handle, exitCode ? exitCode : &ignoreExitCode, uilock);
+ });
+
+ return r;
+}
+
+bool ProcessRunner::waitForApplication(HANDLE handle, LPDWORD exitCode)
+{
+ if (!Settings::instance().interface().lockGUI())
+ return true;
+
+ bool r = false;
+
+ withLock([&](auto* uilock) {
+ r = waitForProcessCompletion(handle, exitCode, uilock);
+ });
+
+ return r;
+}
+
+bool ProcessRunner::waitForProcessCompletion(
+ HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock)
+{
+ const auto tree = env::getProcessTree(handle);
+ std::vector processes = {tree};
+
+ const auto* interesting = getInterestingProcess(processes);
+ if (!interesting) {
+ return true;
+ }
+
+ if (uilock) {
+ uilock->setProcessInformation(interesting->pid(), interesting->name());
+ }
+
+ auto interestingHandle = interesting->openHandleForWait();
+ if (!interestingHandle) {
+ return true;
+ }
+
+ auto progress = [&]{ return uilock->unlockForced(); };
+ const auto r = waitForProcess(
+ interestingHandle.get(), exitCode, progress);
+
+ switch (r)
+ {
+ case WaitResults::Completed: // fall-through
+ case WaitResults::Cancelled:
+ return true;
+
+ case WaitResults::Error: // fall-through
+ default:
+ return false;
+ }
+}
+
+bool ProcessRunner::waitForAllUSVFSProcessesWithLock()
+{
+ if (!Settings::instance().interface().lockGUI())
+ return true;
+
+ bool r = false;
+
+ withLock([&](auto* uilock) {
+ r = waitForAllUSVFSProcesses(uilock);
+ });
+
+ return r;
+}
+
+bool ProcessRunner::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
+{
+ for (;;) {
+ const auto handles = getRunningUSVFSProcesses();
+ if (handles.empty()) {
+ break;
+ }
+
+ std::vector processes;
+ for (auto&& h : handles) {
+ auto p = env::getProcessTree(h);
+ if (p.isValid()) {
+ processes.emplace_back(std::move(p));
+ }
+ }
+
+ const auto* interesting = getInterestingProcess(processes);
+ if (!interesting) {
+ break;
+ }
+
+ if (uilock) {
+ uilock->setProcessInformation(interesting->pid(), interesting->name());
+ }
+
+ auto interestingHandle = interesting->openHandleForWait();
+ if (!interestingHandle) {
+ break;
+ }
+
+ auto progress = [&]{ return uilock->unlockForced(); };
+ const auto r = waitForProcess(
+ interestingHandle.get(), nullptr, progress);
+
+ switch (r)
+ {
+ case WaitResults::Completed:
+ // this process is completed, check for others
+ break;
+
+ case WaitResults::Cancelled:
+ // force unlocked
+ log::debug("waiting for process completion aborted by UI");
+ return true;
+
+ case WaitResults::Error: // fall-through
+ default:
+ log::debug("waiting for process completion not successful");
+ return false;
+ }
+ }
+
+ log::debug("waiting for process completion successful");
+ return true;
+}
+
+
+
+WaitResults waitForProcess(
+ HANDLE handle, DWORD* exitCode, std::function progress)
+{
+ if (handle == INVALID_HANDLE_VALUE) {
+ return WaitResults::Error;
+ }
+
+ log::debug("waiting for completion on pid {}", ::GetProcessId(handle));
+
+ std::vector handles;
+ handles.push_back(handle);
+
+ std::vector exitCodes;
+
+ const auto r = waitForProcesses(handles, exitCodes, progress);
+
+ if (r == WaitResults::Completed) {
+ if (exitCode && !exitCodes.empty()) {
+ *exitCode = exitCodes[0];
+ }
+ }
+
+ return r;
+}
+
+WaitResults waitForProcesses(
+ const std::vector& handles, std::vector& exitCodes,
+ std::function progress)
+{
+ if (handles.empty()) {
+ return WaitResults::Completed;
+ }
+
+ const auto WAIT_OBJECT_N = static_cast(WAIT_OBJECT_0 + handles.size());
+
+ for (;;) {
+ // Wait for a an event on the handle, a key press, mouse click or timeout
+ const auto res = MsgWaitForMultipleObjects(
+ static_cast(handles.size()), &handles[0],
+ TRUE, 50, QS_KEY | QS_MOUSEBUTTON);
+
+ if (res == WAIT_FAILED) {
+ // error
+ const auto e = ::GetLastError();
+
+ log::error(
+ "failed waiting for process completion, {}", formatSystemMessage(e));
+
+ return WaitResults::Error;
+ } else if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_N) {
+ // completed
+ exitCodes.resize(handles.size());
+ std::fill(exitCodes.begin(), exitCodes.end(), 0);
+
+ for (std::size_t i=0; i
+
+class OrganizerCore;
+class ILockedWaitingForProcess;
+class IUserInterface;
+class Executable;
+class MOShortcut;
+
+class SpawnedProcess
+{
+public:
+ SpawnedProcess(HANDLE handle, spawn::SpawnParameters sp);
+
+ SpawnedProcess(const SpawnedProcess&) = delete;
+ SpawnedProcess& operator=(const SpawnedProcess&) = delete;
+ SpawnedProcess(SpawnedProcess&& other);
+ SpawnedProcess& operator=(SpawnedProcess&& other);
+ ~SpawnedProcess();
+
+ HANDLE releaseHandle();
+ void wait();
+
+private:
+ HANDLE m_handle;
+ spawn::SpawnParameters m_parameters;
+
+ void destroy();
+};
+
+
+class ProcessRunner
+{
+public:
+ ProcessRunner(OrganizerCore& core);
+
+ void setUserInterface(IUserInterface* ui);
+
+ bool runFile(QWidget* parent, const QFileInfo& targetInfo);
+
+ bool runExecutableFile(
+ const QFileInfo &binary, const QString &arguments,
+ const QDir ¤tDirectory, const QString &steamAppID={},
+ const QString &customOverwrite={},
+ const QList &forcedLibraries={},
+ bool refresh=true);
+
+ bool runExecutable(const Executable& exe, bool refresh=true);
+
+ bool runShortcut(const MOShortcut& shortcut);
+
+ HANDLE runExecutableOrExecutableFile(
+ const QString &executable, const QStringList &args, const QString &cwd,
+ const QString &profile, const QString &forcedCustomOverwrite = "",
+ bool ignoreCustomOverwrite = false);
+
+ bool waitForApplication(HANDLE processHandle, LPDWORD exitCode = nullptr);
+
+ bool waitForAllUSVFSProcessesWithLock();
+
+private:
+ OrganizerCore& m_core;
+ IUserInterface* m_ui;
+
+ HANDLE spawnAndWait(
+ const QFileInfo &binary, const QString &arguments,
+ const QString &profileName,
+ const QDir ¤tDirectory,
+ const QString &steamAppID,
+ const QString &customOverwrite,
+ const QList &forcedLibraries={},
+ LPDWORD exitCode = nullptr);
+
+ SpawnedProcess spawn(spawn::SpawnParameters sp);
+
+ void withLock(std::function f);
+
+ bool waitForProcessCompletionWithLock(HANDLE handle, LPDWORD exitCode);
+
+ bool waitForProcessCompletion(
+ HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock);
+
+ bool waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock);
+};
+
+
+enum class WaitResults
+{
+ Completed = 1,
+ Error,
+ Cancelled
+};
+
+WaitResults waitForProcess(
+ HANDLE handle, DWORD* exitCode, std::function progress);
+
+WaitResults waitForProcesses(
+ const std::vector& handles, std::vector& exitCodes,
+ std::function progress);
+
+#endif // PROCESSRUNNER_H
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 0ea60641..c8d7c76a 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -772,50 +772,6 @@ bool checkBlacklist(
}
}
-
-void adjustForVirtualized(
- const IPluginGame* game, SpawnParameters& sp, const Settings& settings)
-{
- const QString modsPath = settings.paths().mods();
-
- // Check if this a request with either an executable or a working directory
- // under our mods folder then will start the process in a virtualized
- // "environment" with the appropriate paths fixed:
- // (i.e. mods\FNIS\path\exe => game\data\path\exe)
- QString cwdPath = sp.currentDirectory.absolutePath();
- bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive);
- QString binPath = sp.binary.absoluteFilePath();
- bool virtualizedBin = binPath.startsWith(modsPath, Qt::CaseInsensitive);
- if (virtualizedCwd || virtualizedBin) {
- if (virtualizedCwd) {
- int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1);
- QString adjustedCwd = cwdPath.mid(cwdOffset, -1);
- cwdPath = game->dataDirectory().absolutePath();
- if (cwdOffset >= 0)
- cwdPath += adjustedCwd;
-
- }
-
- if (virtualizedBin) {
- int binOffset = binPath.indexOf('/', modsPath.length() + 1);
- QString adjustedBin = binPath.mid(binOffset, -1);
- binPath = game->dataDirectory().absolutePath();
- if (binOffset >= 0)
- binPath += adjustedBin;
- }
-
- QString cmdline
- = QString("launch \"%1\" \"%2\" %3")
- .arg(QDir::toNativeSeparators(cwdPath),
- QDir::toNativeSeparators(binPath), sp.arguments);
-
- sp.binary = QFileInfo(QCoreApplication::applicationFilePath());
- sp.arguments = cmdline;
- sp.currentDirectory.setPath(QCoreApplication::applicationDirPath());
- }
-}
-
-
HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
{
HANDLE handle = INVALID_HANDLE_VALUE;
@@ -842,80 +798,6 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
}
}
-
-
-SpawnedProcess::SpawnedProcess(HANDLE handle, SpawnParameters sp)
- : m_handle(handle), m_parameters(std::move(sp))
-{
-}
-
-SpawnedProcess::SpawnedProcess(SpawnedProcess&& other)
- : m_handle(other.m_handle), m_parameters(std::move(other.m_parameters))
-{
- other.m_handle = INVALID_HANDLE_VALUE;
-}
-
-SpawnedProcess& SpawnedProcess::operator=(SpawnedProcess&& other)
-{
- if (this != &other) {
- destroy();
-
- m_handle = other.m_handle;
- other.m_handle = INVALID_HANDLE_VALUE;
-
- m_parameters = std::move(other.m_parameters);
- }
-
- return *this;
-}
-
-SpawnedProcess::~SpawnedProcess()
-{
- destroy();
-}
-
-HANDLE SpawnedProcess::releaseHandle()
-{
- const auto h = m_handle;
- m_handle = INVALID_HANDLE_VALUE;
- return h;
-}
-
-void SpawnedProcess::destroy()
-{
- if (m_handle != INVALID_HANDLE_VALUE) {
- ::CloseHandle(m_handle);
- m_handle = INVALID_HANDLE_VALUE;
- }
-}
-
-
-SpawnedProcess Spawner::spawn(
- QWidget* parent, const IPluginGame* game,
- SpawnParameters sp, Settings& settings)
-{
- if (!checkBinary(parent, sp)) {
- return {INVALID_HANDLE_VALUE, sp};
- }
-
- if (!checkSteam(parent, sp, game->gameDirectory(), sp.steamAppID, settings)) {
- return {INVALID_HANDLE_VALUE, sp};
- }
-
- if (!checkEnvironment(parent, sp)) {
- return {INVALID_HANDLE_VALUE, sp};
- }
-
- if (!checkBlacklist(parent, sp, settings)) {
- return {INVALID_HANDLE_VALUE, sp};
- }
-
- adjustForVirtualized(game, sp, settings);
-
- return {startBinary(parent, sp), sp};
-}
-
-
QString getExecutableForJarFile(const QString& jarFile)
{
const std::wstring jarFileW = jarFile.toStdWString();
@@ -1094,86 +976,6 @@ FileExecutionContext getFileExecutionContext(
return {{}, {}, FileExecutionTypes::Other};
}
-WaitResults waitForProcess(
- HANDLE handle, DWORD* exitCode, std::function progress)
-{
- if (handle == INVALID_HANDLE_VALUE) {
- return WaitResults::Error;
- }
-
- log::debug("waiting for completion on pid {}", ::GetProcessId(handle));
-
- std::vector handles;
- handles.push_back(handle);
-
- std::vector exitCodes;
-
- const auto r = waitForProcesses(handles, exitCodes, progress);
-
- if (r == WaitResults::Completed) {
- if (exitCode && !exitCodes.empty()) {
- *exitCode = exitCodes[0];
- }
- }
-
- return r;
-}
-
-WaitResults waitForProcesses(
- const std::vector& handles, std::vector& exitCodes,
- std::function progress)
-{
- if (handles.empty()) {
- return WaitResults::Completed;
- }
-
- const auto WAIT_OBJECT_N = static_cast(WAIT_OBJECT_0 + handles.size());
-
- for (;;) {
- // Wait for a an event on the handle, a key press, mouse click or timeout
- const auto res = MsgWaitForMultipleObjects(
- static_cast(handles.size()), &handles[0],
- TRUE, 50, QS_KEY | QS_MOUSEBUTTON);
-
- if (res == WAIT_FAILED) {
- // error
- const auto e = ::GetLastError();
-
- log::error(
- "failed waiting for process completion, {}", formatSystemMessage(e));
-
- return WaitResults::Error;
- } else if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_N) {
- // completed
- exitCodes.resize(handles.size());
- std::fill(exitCodes.begin(), exitCodes.end(), 0);
-
- for (std::size_t i=0; i progress);
-
-WaitResults waitForProcesses(
- const std::vector& handles, std::vector& exitCodes,
- std::function progress);
-
} // namespace
--
cgit v1.3.1
From 72dd230cdc60e74446caceb5cfb4c6d32e4f6f68 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 31 Oct 2019 05:58:41 -0400
Subject: removed unused files fixed handle leak when starting steam
---
src/CMakeLists.txt | 17 +-----
src/ilockedwaitingforprocess.h | 14 -----
src/iuserinterface.h | 1 -
src/lockeddialog.cpp | 71 ------------------------
src/lockeddialog.h | 57 --------------------
src/lockeddialog.ui | 98 ---------------------------------
src/lockeddialogbase.cpp | 78 ---------------------------
src/lockeddialogbase.h | 64 ----------------------
src/mainwindow.cpp | 1 -
src/organizercore.cpp | 1 -
src/spawn.cpp | 3 +-
src/spawn.h | 2 -
src/waitingonclosedialog.cpp | 71 ------------------------
src/waitingonclosedialog.h | 56 -------------------
src/waitingonclosedialog.ui | 119 -----------------------------------------
15 files changed, 2 insertions(+), 651 deletions(-)
delete mode 100644 src/ilockedwaitingforprocess.h
delete mode 100644 src/lockeddialog.cpp
delete mode 100644 src/lockeddialog.h
delete mode 100644 src/lockeddialog.ui
delete mode 100644 src/lockeddialogbase.cpp
delete mode 100644 src/lockeddialogbase.h
delete mode 100644 src/waitingonclosedialog.cpp
delete mode 100644 src/waitingonclosedialog.h
delete mode 100644 src/waitingonclosedialog.ui
(limited to 'src/spawn.h')
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 168b79dc..06f2cd1e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -82,9 +82,6 @@ SET(organizer_SRCS
main.cpp
loghighlighter.cpp
loglist.cpp
- lockeddialogbase.cpp
- lockeddialog.cpp
- waitingonclosedialog.cpp
loadmechanism.cpp
installationmanager.cpp
filedialogmemory.cpp
@@ -206,9 +203,6 @@ SET(organizer_HDRS
mainwindow.h
loghighlighter.h
loglist.h
- lockeddialogbase.h
- lockeddialog.h
- waitingonclosedialog.h
loadmechanism.h
installationmanager.h
filedialogmemory.h
@@ -246,7 +240,6 @@ SET(organizer_HDRS
viewmarkingscrollbar.h
plugincontainer.h
organizercore.h
- ilockedwaitingforprocess.h
iuserinterface.h
instancemanager.h
usvfsconnector.h
@@ -293,8 +286,6 @@ SET(organizer_UIS
modinfodialog.ui
messagedialog.ui
mainwindow.ui
- lockeddialog.ui
- waitingonclosedialog.ui
editexecutablesdialog.ui
credentialsdialog.ui
categoriesdialog.ui
@@ -397,12 +388,6 @@ set(executables
editexecutablesdialog
)
-set(locking
- ilockedwaitingforprocess
- lockeddialog
- lockeddialogbase
-)
-
set(modinfo
modinfo
modinfobackup
@@ -500,7 +485,7 @@ set(widgets
)
set(src_filters
- application core browser dialogs downloads env executables locking modinfo
+ application core browser dialogs downloads env executables modinfo
modinfo\\dialog modlist plugins previews profiles settings settingsdialog
utilities widgets
)
diff --git a/src/ilockedwaitingforprocess.h b/src/ilockedwaitingforprocess.h
deleted file mode 100644
index 4d1e786f..00000000
--- a/src/ilockedwaitingforprocess.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef ILOCKEDWAITINGFORPROCESS_H
-#define ILOCKEDWAITINGFORPROCESS_H
-
-class QString;
-
-class ILockedWaitingForProcess
-{
-public:
- virtual bool unlockForced() const = 0;
- virtual void setProcessName(QString const &) = 0;
- virtual void setProcessInformation(DWORD pid, const QString& name) = 0;
-};
-
-#endif // ILOCKEDWAITINGFORPROCESS_H
diff --git a/src/iuserinterface.h b/src/iuserinterface.h
index e5755f03..99caceb1 100644
--- a/src/iuserinterface.h
+++ b/src/iuserinterface.h
@@ -3,7 +3,6 @@
#include "modinfodialogfwd.h"
-#include "ilockedwaitingforprocess.h"
#include "lockwidget.h"
#include
#include
diff --git a/src/lockeddialog.cpp b/src/lockeddialog.cpp
deleted file mode 100644
index 143d5838..00000000
--- a/src/lockeddialog.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see .
-*/
-
-#include "lockeddialog.h"
-#include "ui_lockeddialog.h"
-
-#include
-#include
-#include
-#include // for Qt::FramelessWindowHint, etc
-
-LockedDialog::LockedDialog(QWidget *parent, bool unlockByButton)
- : LockedDialogBase(parent, !unlockByButton)
- , ui(new Ui::LockedDialog)
-{
- ui->setupUi(this);
-
- // Supposedly the Qt::CustomizeWindowHint should use a customized window
- // allowing us to select if there is a close button. In practice this doesn't
- // seem to work. We will ignore pressing the close button if unlockByButton == true
- Qt::WindowFlags flags =
- this->windowFlags() | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint;
- if (m_allowClose)
- flags |= Qt::WindowCloseButtonHint;
- this->setWindowFlags(flags);
-
- if (!unlockByButton)
- {
- ui->unlockButton->hide();
- ui->verticalLayout->addItem(
- new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
- }
-}
-
-LockedDialog::~LockedDialog()
-{
- delete ui;
-}
-
-
-void LockedDialog::setProcessName(const QString &name)
-{
- ui->processLabel->setText(name);
-}
-
-void LockedDialog::on_unlockButton_clicked()
-{
- unlock();
-}
-
-void LockedDialog::unlock() {
- LockedDialogBase::unlock();
- ui->label->setText("unlocking may take a few seconds");
- ui->unlockButton->setEnabled(false);
-}
diff --git a/src/lockeddialog.h b/src/lockeddialog.h
deleted file mode 100644
index 36c16429..00000000
--- a/src/lockeddialog.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see .
-*/
-
-#pragma once
-
-#include "lockeddialogbase.h"
-
-namespace Ui {
- class LockedDialog;
-}
-
-/**
- * a small borderless dialog displayed while the Mod Organizer UI is locked
- * The dialog contains only a label and a button to force the UI to be unlocked
- *
- * The UI gets locked while running external applications since they may modify the
- * data on which Mod Organizer works. After the UI is unlocked (manually or after the
- * external application closed) MO will refresh all of its data sources
- **/
-class LockedDialog : public LockedDialogBase
-{
- Q_OBJECT
-
-public:
- explicit LockedDialog(QWidget *parent = 0, bool unlockByButton = false);
- ~LockedDialog();
-
- void setProcessName(const QString &name) override;
-
-protected:
-
- void unlock() override;
-
-private slots:
-
- void on_unlockButton_clicked();
-
-private:
-
- Ui::LockedDialog *ui;
-};
diff --git a/src/lockeddialog.ui b/src/lockeddialog.ui
deleted file mode 100644
index 0ec2e467..00000000
--- a/src/lockeddialog.ui
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
- LockedDialog
-
-
-
- 0
- 0
- 317
- 151
-
-
-
- Running virtualized processes
-
-
- -
-
-
- This dialog should disappear automatically if the application/game is done. Click unlock if it didn't.
-
-
- MO is locked while the executable is running.
-
-
- Qt::AlignCenter
-
-
- true
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 10
-
-
-
-
- -
-
-
-
- true
-
-
-
- color: grey;
-
-
-
-
-
- Qt::AlignCenter
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 10
-
-
-
-
- -
-
-
- Unlock
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 10
-
-
-
-
-
-
-
-
-
diff --git a/src/lockeddialogbase.cpp b/src/lockeddialogbase.cpp
deleted file mode 100644
index 0876a511..00000000
--- a/src/lockeddialogbase.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see .
-*/
-
-#include "lockeddialogbase.h"
-#include "envmodule.h"
-#include
-#include
-#include
-#include // for Qt::FramelessWindowHint, etc
-
-LockedDialogBase::LockedDialogBase(QWidget *parent, bool allowClose)
- : QDialog(parent)
- , m_Unlocked(false)
- , m_Canceled(false)
- , m_allowClose(allowClose)
-{
- if (parent != nullptr) {
- QPoint position = parent->mapToGlobal(QPoint(parent->width() / 2, parent->height() / 2));
- position.rx() -= this->width() / 2;
- position.ry() -= this->height() / 2;
- move(position);
- }
-}
-
-void LockedDialogBase::resizeEvent(QResizeEvent *event)
-{
- QWidget *par = parentWidget();
- if (par != nullptr) {
- QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height() / 2));
- position.rx() -= event->size().width() / 2;
- position.ry() -= event->size().height() / 2;
- move(position);
- }
-}
-
-void LockedDialogBase::reject()
-{
- if (m_allowClose)
- unlock();
-}
-
-bool LockedDialogBase::unlockForced() const {
- return m_Unlocked;
-}
-
-bool LockedDialogBase::canceled() const {
- return m_Canceled;
-}
-
-void LockedDialogBase::setProcessInformation(DWORD pid, const QString& name)
-{
- setProcessName(QString("%1 (%2)").arg(name).arg(pid));
-}
-
-void LockedDialogBase::unlock() {
- m_Unlocked = true;
-}
-
-void LockedDialogBase::cancel() {
- m_Canceled = true;
-}
-
diff --git a/src/lockeddialogbase.h b/src/lockeddialogbase.h
deleted file mode 100644
index 4ebad4c7..00000000
--- a/src/lockeddialogbase.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see .
-*/
-
-#pragma once
-
-#include "ilockedwaitingforprocess.h"
-#include // for QDialog
-#include // for Q_OBJECT, slots
-#include // for QString
-
-class QResizeEvent;
-class QWidget;
-
-/**
- * a small borderless dialog displayed while the Mod Organizer UI is locked
- * The dialog contains only a label and a button to force the UI to be unlocked
- *
- * The UI gets locked while running external applications since they may modify the
- * data on which Mod Organizer works. After the UI is unlocked (manually or after the
- * external application closed) MO will refresh all of its data sources
- **/
-class LockedDialogBase : public QDialog, public ILockedWaitingForProcess
-{
- Q_OBJECT
-
-public:
- explicit LockedDialogBase(QWidget *parent, bool allowClose);
-
- bool unlockForced() const override;
-
- virtual bool canceled() const;
-
- void setProcessInformation(DWORD pid, const QString& name) override;
-
-protected:
-
- virtual void resizeEvent(QResizeEvent *event);
-
- virtual void reject();
-
- virtual void unlock();
-
- virtual void cancel();
-
- bool m_Unlocked;
- bool m_Canceled;
- bool m_allowClose;
-};
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 631a31d8..9453f07c 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -57,7 +57,6 @@ along with Mod Organizer. If not, see .
#include "downloadlistwidget.h"
#include "messagedialog.h"
#include "installationmanager.h"
-#include "waitingonclosedialog.h"
#include "downloadlistsortproxy.h"
#include "motddialog.h"
#include "filedialogmemory.h"
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 3c535868..dda60f76 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -29,7 +29,6 @@
#include "appconfig.h"
#include
#include
-#include "lockeddialog.h"
#include "instancemanager.h"
#include
#include "previewdialog.h"
diff --git a/src/spawn.cpp b/src/spawn.cpp
index b331db01..62745542 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -27,8 +27,6 @@ along with Mod Organizer. If not, see .
#include "envmodule.h"
#include "settings.h"
#include "settingsdialogworkarounds.h"
-#include
-#include
#include
#include
#include
@@ -645,6 +643,7 @@ bool startSteam(QWidget* parent)
HANDLE ph = INVALID_HANDLE_VALUE;
const auto e = spawn(sp, ph);
+ ::CloseHandle(ph);
if (e != ERROR_SUCCESS) {
// make sure username and passwords are not shown
diff --git a/src/spawn.h b/src/spawn.h
index 0464ffd6..9fb346b0 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -28,8 +28,6 @@ along with Mod Organizer. If not, see .
class Settings;
-namespace MOBase { class IPluginGame; }
-
namespace spawn
{
diff --git a/src/waitingonclosedialog.cpp b/src/waitingonclosedialog.cpp
deleted file mode 100644
index 565d0a36..00000000
--- a/src/waitingonclosedialog.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see .
-*/
-
-#include "waitingonclosedialog.h"
-#include "ui_waitingonclosedialog.h"
-
-#include
-#include
-#include
-#include // for Qt::FramelessWindowHint, etc
-
-WaitingOnCloseDialog::WaitingOnCloseDialog(QWidget *parent)
- : LockedDialogBase(parent,true)
- , ui(new Ui::WaitingOnCloseDialog)
-{
- ui->setupUi(this);
-
- // Supposedly the Qt::CustomizeWindowHint should use a customized window
- // allowing us to select if there is a close button. In practice this doesn't
- // seem to work. We will ignore pressing the close button if unlockByButton == true
- Qt::WindowFlags flags =
- this->windowFlags() | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint;
- if (m_allowClose)
- flags |= Qt::WindowCloseButtonHint;
- this->setWindowFlags(flags);
-}
-
-WaitingOnCloseDialog::~WaitingOnCloseDialog()
-{
- delete ui;
-}
-
-
-void WaitingOnCloseDialog::setProcessName(const QString &name)
-{
- ui->processLabel->setText(name);
-}
-
-void WaitingOnCloseDialog::on_closeButton_clicked()
-{
- unlock();
-}
-
-void WaitingOnCloseDialog::on_cancelButton_clicked()
-{
- cancel();
- unlock();
-}
-
-void WaitingOnCloseDialog::unlock() {
- LockedDialogBase::unlock();
- ui->label->setText("unlocking may take a few seconds");
- ui->closeButton->setEnabled(false);
- ui->cancelButton->setEnabled(false);
-}
diff --git a/src/waitingonclosedialog.h b/src/waitingonclosedialog.h
deleted file mode 100644
index 6650c390..00000000
--- a/src/waitingonclosedialog.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see .
-*/
-
-#pragma once
-
-#include "lockeddialogbase.h"
-
-namespace Ui {
- class WaitingOnCloseDialog;
-}
-
-/**
- * Similar to the LockedDialog but used for waiting on running process during
- * a process close request which requries a slightly different dialog.
- **/
-class WaitingOnCloseDialog : public LockedDialogBase
-{
- Q_OBJECT
-
-public:
- explicit WaitingOnCloseDialog(QWidget *parent = 0);
- ~WaitingOnCloseDialog();
-
- bool canceled() const { return m_Canceled; }
-
- void setProcessName(const QString &name) override;
-
-protected:
-
- void unlock() override;
-
-private slots:
-
- void on_closeButton_clicked();
- void on_cancelButton_clicked();
-
-private:
-
- Ui::WaitingOnCloseDialog *ui;
-};
diff --git a/src/waitingonclosedialog.ui b/src/waitingonclosedialog.ui
deleted file mode 100644
index 9c7818e0..00000000
--- a/src/waitingonclosedialog.ui
+++ /dev/null
@@ -1,119 +0,0 @@
-
-
- WaitingOnCloseDialog
-
-
-
- 0
- 0
- 317
- 151
-
-
-
- Waiting for virtualized processes
-
-
- -
-
-
- This dialog should disappear automatically if the application/game is done.
-
-
- Virtualized processes are still running, it is prefered to keep MO running until they are finished.
-
-
- true
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 10
-
-
-
-
- -
-
-
-
- true
-
-
-
- color: grey;
-
-
-
-
-
- Qt::AlignCenter
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 10
-
-
-
-
- -
-
-
-
-
-
- Close Now
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Cancel
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 10
-
-
-
-
-
-
-
-
-
--
cgit v1.3.1
From ada2ac0cb5d0ef2039ce55794928c4d05255ed65 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 31 Oct 2019 07:37:24 -0400
Subject: removed redundant checkBinary(), which used to check for non existing
binaries, that's handled when spawning removed useless checkEnvironment(),
which checked for EventLog removed CREATE_BREAKAWAY_FROM_JOB from
CreateProcess() calls, didn't do anything fixed setFromFileOrExecutable()
when running just a filename that's not an executable name split run() fixed
lock widget being disabled when running without a ui
---
src/lockwidget.cpp | 12 ++-
src/processrunner.cpp | 197 ++++++++++++++++++++++++++++++++------------------
src/processrunner.h | 76 ++++++++++++++++++-
src/spawn.cpp | 43 +----------
src/spawn.h | 4 -
5 files changed, 212 insertions(+), 120 deletions(-)
(limited to 'src/spawn.h')
diff --git a/src/lockwidget.cpp b/src/lockwidget.cpp
index bbffd390..35d51dab 100644
--- a/src/lockwidget.cpp
+++ b/src/lockwidget.cpp
@@ -229,10 +229,14 @@ void LockWidget::disableAll()
}
if (auto* d=dynamic_cast(w)) {
- // no central widget, just disable the children, except for the overlay
- for (auto* child : findChildrenImmediate(d)) {
- if (child != m_overlay.get()) {
- disable(child);
+ // don't disable stuff if this dialog is the overlay, which happens when
+ // there's no ui
+ if (d != m_overlay.get()) {
+ // no central widget, just disable the children, except for the overlay
+ for (auto* child : findChildrenImmediate(d)) {
+ if (child != m_overlay.get()) {
+ disable(child);
+ }
}
}
}
diff --git a/src/processrunner.cpp b/src/processrunner.cpp
index 58b2a771..220ffe38 100644
--- a/src/processrunner.cpp
+++ b/src/processrunner.cpp
@@ -111,6 +111,9 @@ QString toString(Interest i)
}
+// returns a process that's in the hidden list, or the top-level process if
+// they're all hidden; returns an invalid process if the list is empty
+//
std::pair findInterestingProcessInTrees(
std::vector& processes)
{
@@ -150,6 +153,8 @@ std::pair findInterestingProcessInTrees(
return {processes[0], Interest::Weak};
}
+// gets the most interesting process in the list
+//
std::pair getInterestingProcess(
const std::vector& initialProcesses)
{
@@ -160,7 +165,7 @@ std::pair getInterestingProcess(
std::vector processes;
- log::debug("getting process tree for {} processes", initialProcesses.size());
+ // getting process trees for all processes
for (auto&& h : initialProcesses) {
auto tree = env::getProcessTree(h);
if (tree.isValid()) {
@@ -169,12 +174,15 @@ std::pair getInterestingProcess(
}
if (processes.empty()) {
+ // if the initial list wasn't empty but this one is, it means all the
+ // processes were already completed
log::debug("processes are already completed");
return {{}, Interest::None};
}
const auto interest = findInterestingProcessInTrees(processes);
if (!interest.first.isValid()) {
+ // this shouldn't happen
log::debug("no interesting process to wait for");
return {{}, Interest::None};
}
@@ -184,6 +192,8 @@ std::pair getInterestingProcess(
const std::chrono::milliseconds Infinite(-1);
+// waits for completion, times out after `wait` if not Infinite
+//
std::optional timedWait(
HANDLE handle, DWORD pid, LockWidget& lock, std::chrono::milliseconds wait)
{
@@ -195,18 +205,21 @@ std::optional