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