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/organizercore.cpp | 286 ++------------------------------------------------ 1 file changed, 11 insertions(+), 275 deletions(-) (limited to 'src/organizercore.cpp') 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()); -- 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/organizercore.cpp') 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 09b95e39434b9efc49a606957c94cd42309b7fb6 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 13 Sep 2019 15:15:18 -0400 Subject: moved all spawn dialogs into a namespace starting steam with spawn() instead of QProcess dialogs for bad steam registry key and failure refactored credentials code, added logging add environment variables to env --- src/env.cpp | 42 +++++ src/env.h | 12 +- src/organizercore.cpp | 4 - src/settingsutilities.cpp | 148 ++++++++++++----- src/settingsutilities.h | 4 +- src/spawn.cpp | 410 +++++++++++++++++++++++++--------------------- 6 files changed, 383 insertions(+), 237 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/env.cpp b/src/env.cpp index 34f53294..1aaaa8ef 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -171,6 +171,48 @@ void Environment::dumpDisks(const Settings& s) const } +QString path() +{ + return get("PATH"); +} + +QString addPath(const QString& s) +{ + auto old = path(); + set("PATH", get("PATH") + ";" + s); + return old; +} + +QString setPath(const QString& s) +{ + return set("PATH", s); +} + +QString get(const QString& name) +{ + std::wstring s(4000, L' '); + + DWORD realSize = ::GetEnvironmentVariableW( + name.toStdWString().c_str(), s.data(), static_cast(s.size())); + + if (realSize > s.size()) { + s.resize(realSize); + + ::GetEnvironmentVariableW( + name.toStdWString().c_str(), s.data(), static_cast(s.size())); + } + + return QString::fromStdWString(s); +} + +QString set(const QString& n, const QString& v) +{ + auto old = get(n); + ::SetEnvironmentVariableW(n.toStdWString().c_str(), v.toStdWString().c_str()); + return old; +} + + // returns the filename of the given process or the current one // std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE) diff --git a/src/env.h b/src/env.h index 6222c86b..7bdc9b85 100644 --- a/src/env.h +++ b/src/env.h @@ -162,6 +162,16 @@ private: }; +// environment variables +// +QString get(const QString& name); +QString set(const QString& name, const QString& value); + +QString path(); +QString addPath(const QString& s); +QString setPath(const QString& s); + + enum class CoreDumpTypes { Mini = 1, @@ -169,7 +179,7 @@ enum class CoreDumpTypes Full }; -// creates a minidump file for the given process +// creates a minidump file for this process // bool coredump(CoreDumpTypes type); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index dd4edbce..fbc9083b 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1288,7 +1288,6 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, sp.currentDirectory = currentDirectory; sp.hooked = true; - prepareStart(); QWidget *window = qApp->activeWindow(); @@ -1296,7 +1295,6 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, window = nullptr; } - if (!spawn::checkBinary(window, sp)) { return INVALID_HANDLE_VALUE; } @@ -1369,8 +1367,6 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, .arg(QDir::toNativeSeparators(cwdPath), QDir::toNativeSeparators(binPath), arguments); - log::debug("Spawning proxyed process <{}>", cmdline); - sp.binary = QFileInfo(QCoreApplication::applicationFilePath()); sp.arguments = cmdline; sp.currentDirectory.setPath(QCoreApplication::applicationDirPath()); diff --git a/src/settingsutilities.cpp b/src/settingsutilities.cpp index 7a9dcc35..6c99a602 100644 --- a/src/settingsutilities.cpp +++ b/src/settingsutilities.cpp @@ -213,55 +213,117 @@ void warnIfNotCheckable(const QAbstractButton* b) } -bool setWindowsCredential(const QString key, const QString data) +QString credentialName(const QString& key) { - QString finalKey("ModOrganizer2_" + key); - wchar_t* keyData = new wchar_t[finalKey.size()+1]; - finalKey.toWCharArray(keyData); - keyData[finalKey.size()] = L'\0'; - bool result = false; - if (data.isEmpty()) { - result = CredDeleteW(keyData, CRED_TYPE_GENERIC, 0); - if (!result) - if (GetLastError() == ERROR_NOT_FOUND) - result = true; - } else { - wchar_t* charData = new wchar_t[data.size()]; - data.toWCharArray(charData); - - CREDENTIALW cred = {}; - cred.Flags = 0; - cred.Type = CRED_TYPE_GENERIC; - cred.TargetName = keyData; - cred.CredentialBlob = (LPBYTE)charData; - cred.CredentialBlobSize = sizeof(wchar_t) * data.size(); - cred.Persist = CRED_PERSIST_LOCAL_MACHINE; - - result = CredWriteW(&cred, 0); - delete[] charData; + return "ModOrganizer2_" + key; +} + +bool deleteWindowsCredential(const QString& key) +{ + const auto credName = credentialName(key); + + if (!CredDeleteW(credName.toStdWString().c_str(), CRED_TYPE_GENERIC, 0)) { + const auto e = GetLastError(); + 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; + } } - delete[] keyData; - return result; + + log::debug("deleted windows credential {}", credName); + + return true; } -QString getWindowsCredential(const QString key) -{ - QString result; - QString finalKey("ModOrganizer2_" + key); - wchar_t* keyData = new wchar_t[finalKey.size()+1]; - finalKey.toWCharArray(keyData); - keyData[finalKey.size()] = L'\0'; - PCREDENTIALW creds; - if (CredReadW(keyData, 1, 0, &creds)) { - wchar_t *charData = (wchar_t *)creds->CredentialBlob; - result = QString::fromWCharArray(charData, creds->CredentialBlobSize / sizeof(wchar_t)); - CredFree(creds); - } else { +bool addWindowsCredential(const QString& key, const QString& data) +{ + const auto credName = credentialName(key); + + const auto wname = credName.toStdWString(); + const auto wdata = data.toStdWString(); + + const auto* blob = reinterpret_cast(wdata.data()); + const auto blobSize = wdata.size() * sizeof(decltype(wdata)::value_type); + + CREDENTIALW cred = {}; + cred.Flags = 0; + cred.Type = CRED_TYPE_GENERIC; + cred.TargetName = const_cast(wname.c_str()); + cred.CredentialBlob = const_cast(blob); + cred.CredentialBlobSize = static_cast(blobSize); + cred.Persist = CRED_PERSIST_LOCAL_MACHINE; + + if (!CredWriteW(&cred, 0)) { const auto e = GetLastError(); + + log::error( + "failed to delete windows credential {}, {}", + credName, formatSystemMessage(e)); + + return false; + } + + log::debug("added windows credential {}", credName); + + return true; +} + +struct CredentialFreer +{ + void operator()(CREDENTIALW* c) + { + if (c) { + CredFree(c); + } + } +}; + +using CredentialPtr = std::unique_ptr; + +QString getWindowsCredential(const QString& key) +{ + const QString credName = credentialName(key); + + CREDENTIALW* rawCreds = nullptr; + + const auto ret = CredReadW( + credName.toStdWString().c_str(), CRED_TYPE_GENERIC, 0, &rawCreds); + + CredentialPtr creds(rawCreds); + + if (!ret) { + const auto e = GetLastError(); + if (e != ERROR_NOT_FOUND) { - log::error("Retrieving encrypted data failed: {}", formatSystemMessage(e)); + log::error( + "failed to retrieve windows credential {}: {}", + credName, formatSystemMessage(e)); } + + return {}; + } + + QString value; + if (creds->CredentialBlob) { + value = QString::fromWCharArray( + reinterpret_cast(creds->CredentialBlob), + creds->CredentialBlobSize / sizeof(wchar_t)); + } + + return value; +} + +bool setWindowsCredential(const QString& key, const QString& data) +{ + if (data.isEmpty()) { + return deleteWindowsCredential(key); + } else { + return addWindowsCredential(key, data); } - delete[] keyData; - return result; } diff --git a/src/settingsutilities.h b/src/settingsutilities.h index c3eef12f..a6737144 100644 --- a/src/settingsutilities.h +++ b/src/settingsutilities.h @@ -270,7 +270,7 @@ QString checkedSettingName(const QAbstractButton* b); void warnIfNotCheckable(const QAbstractButton* b); -bool setWindowsCredential(const QString key, const QString data); -QString getWindowsCredential(const QString key); +bool setWindowsCredential(const QString& key, const QString& data); +QString getWindowsCredential(const QString& key); #endif // SETTINGSUTILITIES_H diff --git a/src/spawn.cpp b/src/spawn.cpp index 80f82a28..94737871 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -43,96 +43,8 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; -namespace spawn -{ - -// details -namespace -{ - -std::wstring pathEnv() +namespace spawn::dialogs { - std::wstring s(4000, L' '); - - DWORD realSize = ::GetEnvironmentVariableW( - L"PATH", s.data(), static_cast(s.size())); - - if (realSize > s.size()) { - s.resize(realSize); - - ::GetEnvironmentVariableW( - TEXT("PATH"), s.data(), static_cast(s.size())); - } - - return s; -} - -void setPathEnv(const std::wstring& s) -{ - ::SetEnvironmentVariableW(L"PATH", s.c_str()); -} - -DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHandle) -{ - BOOL inheritHandles = FALSE; - - STARTUPINFO si = {}; - si.cb = sizeof(si); - - // inherit handles if we plan to use stdout or stderr reroute - if (sp.stdOut != INVALID_HANDLE_VALUE) { - si.hStdOutput = sp.stdOut; - inheritHandles = TRUE; - si.dwFlags |= STARTF_USESTDHANDLES; - } - - if (sp.stdErr != INVALID_HANDLE_VALUE) { - si.hStdError = sp.stdErr; - inheritHandles = TRUE; - si.dwFlags |= STARTF_USESTDHANDLES; - } - - const auto bin = QDir::toNativeSeparators(sp.binary.absoluteFilePath()).toStdWString(); - - std::wstring commandLine = L"\"" + bin + L"\""; - if (sp.arguments[0] != L'\0') { - commandLine += L" " + sp.arguments.toStdWString(); - } - - QString moPath = QCoreApplication::applicationDirPath(); - - const auto oldPath = pathEnv(); - setPathEnv(oldPath + L";" + QDir::toNativeSeparators(moPath).toStdWString()); - - 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, - cwd.c_str(), &si, &pi); - } else { - success = ::CreateProcess( - nullptr, const_cast(commandLine.c_str()), nullptr, nullptr, - inheritHandles, CREATE_BREAKAWAY_FROM_JOB, nullptr, - cwd.c_str(), &si, &pi); - } - - const auto e = GetLastError(); - setPathEnv(oldPath); - - if (!success) { - return e; - } - - processHandle = pi.hProcess; - threadHandle = pi.hThread; - - return ERROR_SUCCESS; -} std::wstring makeRightsDetails(const env::FileSecurity& fs) { @@ -196,7 +108,7 @@ std::wstring makeDetails(const SpawnParameters& sp, DWORD code) elevated = L"(not available)"; } - return fmt::format( + std::wstring f = L"Error {code} {codename}: {error}\n" L" . binary: '{bin}'\n" L" . owner: {owner}\n" @@ -204,8 +116,13 @@ std::wstring makeDetails(const SpawnParameters& sp, DWORD code) L" . arguments: '{args}'\n" L" . cwd: '{cwd}'{cwdexists}\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}", + L" . MO elevated: {elevated}"; + + if (sp.hooked) { + f += L"\n . usvfs x86:{x86_dll} x64:{x64_dll} proxy_x86:{x86_proxy} proxy_x64:{x64_proxy}"; + } + + return fmt::format(f, fmt::arg(L"code", code), fmt::arg(L"codename", errorCodeName(code)), fmt::arg(L"bin", QDir::toNativeSeparators(sp.binary.absoluteFilePath()).toStdWString()), @@ -225,36 +142,82 @@ std::wstring makeDetails(const SpawnParameters& sp, DWORD code) fmt::arg(L"elevated", elevated)); } -void spawnFailed(const SpawnParameters& sp, DWORD code) +QString makeContent(const SpawnParameters& sp, DWORD code) { - const auto details = QString::fromStdWString(makeDetails(sp, code)); - log::error("{}", details); - - const auto title = QObject::tr("Cannot launch program"); - - const auto mainText = QObject::tr("Cannot start %1") - .arg(sp.binary.fileName()); - - QString content; - if (code == ERROR_INVALID_PARAMETER) { - content = QObject::tr( + return QObject::tr( "This error typically happens because an antivirus has deleted critical " "files from Mod Organizer's installation folder or has made them " "generally inaccessible. Add an exclusion for Mod Organizer's " "installation folder in your antivirus, reinstall Mod Organizer and try " "again."); } else if (code == ERROR_ACCESS_DENIED) { - content = QObject::tr( + return QObject::tr( "This error typically happens because an antivirus is preventing Mod " "Organizer from starting programs. Add an exclusion for Mod Organizer's " "installation folder in your antivirus and try again."); } else if (code == ERROR_FILE_NOT_FOUND) { - content = QObject::tr("The file '%1' does not exist.") + return QObject::tr("The file '%1' does not exist.") .arg(QDir::toNativeSeparators(sp.binary.absoluteFilePath())); } else { - content = QString::fromStdWString(formatSystemMessage(code)); + return QString::fromStdWString(formatSystemMessage(code)); } +} + +QMessageBox::StandardButton badSteamReg( + QWidget* parent, const QString& keyName, const QString& valueName) +{ + const auto details = QString( + "can't start steam, registry value at '%1' is empty or doesn't exist") + .arg(keyName + "\\" + valueName); + + log::error("{}", details); + + return MOBase::TaskDialog(parent, QObject::tr("Cannot start Steam")) + .main(QObject::tr("Cannot start Steam")) + .content(QObject::tr( + "The path to the Steam executable cannot be found. You might try " + "reinstalling Steam.")) + .details(details) + .button({ + QObject::tr("Continue without starting Steam"), + QObject::tr("The program may fail to launch."), + QMessageBox::Yes}) + .button({ + QObject::tr("Cancel"), + QMessageBox::Cancel}) + .exec(); +} + +QMessageBox::StandardButton startSteamFailed( + QWidget* parent, const SpawnParameters& sp, DWORD e) +{ + const auto details = makeDetails(sp, e); + log::error("{}", details); + + return MOBase::TaskDialog(parent, QObject::tr("Cannot start Steam")) + .main(QObject::tr("Cannot start Steam")) + .content(makeContent(sp, e)) + .details(QString::fromStdWString(details)) + .button({ + QObject::tr("Continue without starting Steam"), + QObject::tr("The program may fail to launch."), + QMessageBox::Yes}) + .button({ + QObject::tr("Cancel"), + QMessageBox::Cancel}) + .exec(); +} + +void spawnFailed(const SpawnParameters& sp, DWORD code) +{ + const auto details = QString::fromStdWString(makeDetails(sp, code)); + log::error("{}", details); + + const auto title = QObject::tr("Cannot launch program"); + + const auto mainText = QObject::tr("Cannot start %1") + .arg(sp.binary.fileName()); QWidget *window = qApp->activeWindow(); if ((window != nullptr) && (!window->isVisible())) { @@ -263,7 +226,7 @@ void spawnFailed(const SpawnParameters& sp, DWORD code) MOBase::TaskDialog(window, title) .main(mainText) - .content(content) + .content(makeContent(sp, code)) .details(details) .exec(); } @@ -301,48 +264,143 @@ bool confirmRestartAsAdmin(const SpawnParameters& sp) .content(content) .details(details) .button({ - QObject::tr("Restart Mod Organizer as administrator"), - QObject::tr("You must allow \"helper.exe\" to make changes to the system."), - QMessageBox::Yes}) + 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("Cancel"), - QMessageBox::Cancel}) + QObject::tr("Cancel"), + QMessageBox::Cancel}) .exec(); return (r == QMessageBox::Yes); } -void startBinaryAdmin(const SpawnParameters& sp) +QuestionBoxMemory::Button confirmStartSteam(QWidget* parent, const SpawnParameters& sp) { - if (!confirmRestartAsAdmin(sp)) { - log::debug("user declined"); - return; + return 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?"), + QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); +} + +QuestionBoxMemory::Button 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); +} + +} // namepsace + + +namespace spawn +{ + +DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHandle) +{ + BOOL inheritHandles = FALSE; + + STARTUPINFO si = {}; + si.cb = sizeof(si); + + // inherit handles if we plan to use stdout or stderr reroute + if (sp.stdOut != INVALID_HANDLE_VALUE) { + si.hStdOutput = sp.stdOut; + inheritHandles = TRUE; + si.dwFlags |= STARTF_USESTDHANDLES; } - log::info("restarting MO as administrator"); + if (sp.stdErr != INVALID_HANDLE_VALUE) { + si.hStdError = sp.stdErr; + inheritHandles = TRUE; + si.dwFlags |= STARTF_USESTDHANDLES; + } + + const auto bin = QDir::toNativeSeparators(sp.binary.absoluteFilePath()).toStdWString(); + const auto cwd = QDir::toNativeSeparators(sp.currentDirectory.absolutePath()).toStdWString(); + + std::wstring commandLine = L"\"" + bin + L"\""; + if (sp.arguments[0] != L'\0') { + commandLine += L" " + sp.arguments.toStdWString(); + } + + QString moPath = QCoreApplication::applicationDirPath(); + const auto oldPath = env::addPath(QDir::toNativeSeparators(moPath)); + + PROCESS_INFORMATION pi; + BOOL success = FALSE; + + if (sp.hooked) { + success = ::CreateProcessHooked( + nullptr, const_cast(commandLine.c_str()), nullptr, nullptr, + inheritHandles, CREATE_BREAKAWAY_FROM_JOB, nullptr, + cwd.c_str(), &si, &pi); + } else { + success = ::CreateProcess( + nullptr, const_cast(commandLine.c_str()), nullptr, nullptr, + inheritHandles, CREATE_BREAKAWAY_FROM_JOB, nullptr, + cwd.c_str(), &si, &pi); + } + + const auto e = GetLastError(); + env::setPath(oldPath); + + if (!success) { + return e; + } + processHandle = pi.hProcess; + threadHandle = pi.hThread; + + return ERROR_SUCCESS; +} + +bool restartAsAdmin() +{ WCHAR cwd[MAX_PATH] = {}; if (!GetCurrentDirectory(MAX_PATH, cwd)) { cwd[0] = L'\0'; } - if (Helper::adminLaunch( + if (!Helper::adminLaunch( qApp->applicationDirPath().toStdWString(), qApp->applicationFilePath().toStdWString(), std::wstring(cwd))) { - log::debug("exiting MO"); - qApp->exit(0); + // todo + log::error("admin launch failed"); + return false; } + + log::debug("exiting MO"); + qApp->exit(0); + return true; } -} // namespace +void startBinaryAdmin(const SpawnParameters& sp) +{ + if (!dialogs::confirmRestartAsAdmin(sp)) { + log::debug("user declined"); + return; + } + + log::info("restarting MO as administrator"); + restartAsAdmin(); +} bool checkBinary(QWidget* parent, const SpawnParameters& sp) { if (!sp.binary.exists()) { - spawnFailed(sp, ERROR_FILE_NOT_FOUND); + dialogs::spawnFailed(sp, ERROR_FILE_NOT_FOUND); return false; } @@ -379,10 +437,23 @@ SteamStatus getSteamStatus() return ss; } -bool startSteam(QWidget *widget) +QString makeSteamArguments(const QString& username, const QString& password) { - log::debug("starting steam"); + QString args; + + if (username != "") { + args += "-login " + username; + + if (password != "") { + args += " " + password; + } + } + + return args; +} +bool startSteam(QWidget* parent) +{ const QString keyName = "HKEY_CURRENT_USER\\Software\\Valve\\Steam"; const QString valueName = "SteamExe"; @@ -390,42 +461,41 @@ bool startSteam(QWidget *widget) const QString exe = steamSettings.value(valueName, "").toString(); if (exe.isEmpty()) { - log::error( - "can't start steam, registry value at '{}' is empty", - keyName + "\\" + valueName); - - return false; + return (dialogs::badSteamReg(parent, keyName, valueName) == QMessageBox::Yes); } - const QString program = QString("\"%1\"").arg(exe); + SpawnParameters sp; + sp.binary = exe; // See if username and password supplied. If so, pass them into steam. - QStringList args; QString username, password; if (Settings::instance().steam().login(username, password)) { - args.push_back("-login"); - args.push_back(username); - - if (password != "") { - args.push_back(password); - } + sp.arguments = makeSteamArguments(username, password); } log::debug( "starting steam process:\n" " . program: '{}'\n" " . username={}, password={}", - program, + sp.binary.filePath().toStdString(), (username.isEmpty() ? "no" : "yes"), (password.isEmpty() ? "no" : "yes")); - if (!QProcess::startDetached(program, args)) { - reportError(QObject::tr("Failed to start \"%1\"").arg(program)); - return false; + HANDLE ph = INVALID_HANDLE_VALUE; + HANDLE th = INVALID_HANDLE_VALUE; + const auto e = spawn(sp, ph, th); + + if (e != ERROR_SUCCESS) { + // make sure username and passwords are not shown + sp.arguments = makeSteamArguments( + (username.isEmpty() ? "" : "USERNAME"), + (password.isEmpty() ? "" : "PASSWORD")); + + return (dialogs::startSteamFailed(parent, sp, e) == QMessageBox::Yes); } QMessageBox::information( - widget, QObject::tr("Waiting"), + parent, QObject::tr("Waiting"), QObject::tr("Please press OK once you're logged into steam.")); return true; @@ -448,29 +518,6 @@ bool gameRequiresSteam(const QDir& gameDirectory, const Settings& settings) return false; } -QuestionBoxMemory::Button confirmStartSteam(QWidget* parent, const SpawnParameters& sp) -{ - return 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?"), - QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); -} - -QuestionBoxMemory::Button 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); -} - bool checkSteam( QWidget* parent, const SpawnParameters& sp, const QDir& gameDirectory, const QString &steamAppID, const Settings& settings) @@ -492,16 +539,20 @@ bool checkSteam( if (!ss.running) { log::debug("steam isn't running, asking to start steam"); - const auto c = confirmStartSteam(parent, sp); + const auto c = dialogs::confirmStartSteam(parent, sp); if (c == QDialogButtonBox::Yes) { log::debug("user wants to start steam"); - startSteam(parent); + + if (!startSteam(parent)) { + // cancel + return false; + } // double-check that Steam is started ss = getSteamStatus(); if (!ss.running) { - log::error("could not start steam, continuing and hoping for the best"); + log::error("steam is still not running, continuing and hoping for the best"); return true; } } else if (c == QDialogButtonBox::No) { @@ -515,25 +566,10 @@ bool checkSteam( if (ss.running && !ss.accessible) { log::debug("steam is running but is not accessible, asking to restart MO"); - const auto c = confirmRestartAsAdminForSteam(parent, sp); + const auto c = dialogs::confirmRestartAsAdminForSteam(parent, sp); if (c == QDialogButtonBox::Yes) { - WCHAR cwd[MAX_PATH]; - if (!GetCurrentDirectory(MAX_PATH, cwd)) { - cwd[0] = L'\0'; - } - - if (Helper::adminLaunch( - qApp->applicationDirPath().toStdWString(), - qApp->applicationFilePath().toStdWString(), - std::wstring(cwd))) - { - log::debug("exiting MO"); - qApp->exit(0); - return false; - } - - log::error("unable to relaunch MO as admin"); + restartAsAdmin(); return false; } else if (c == QDialogButtonBox::No) { log::debug("user declined to restart MO, continuing"); @@ -686,7 +722,7 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp) default: { - spawnFailed(sp, e); + dialogs::spawnFailed(sp, e); return INVALID_HANDLE_VALUE; } } -- cgit v1.3.1 From c50722100c485d2945082d573158a7083efe2f23 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Sep 2019 10:11:56 -0400 Subject: removed helper.h and helper.cpp, merged into spawn --- src/CMakeLists.txt | 3 --- src/helper.cpp | 20 -------------------- src/helper.h | 26 -------------------------- src/main.cpp | 1 - src/organizercore.cpp | 1 - src/settingsdialogworkarounds.cpp | 2 +- src/spawn.cpp | 1 - 7 files changed, 1 insertion(+), 53 deletions(-) delete mode 100644 src/helper.cpp delete mode 100644 src/helper.h (limited to 'src/organizercore.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 60822834..7c29ff48 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -87,7 +87,6 @@ SET(organizer_SRCS waitingonclosedialog.cpp loadmechanism.cpp installationmanager.cpp - helper.cpp filedialogmemory.cpp executableslist.cpp editexecutablesdialog.cpp @@ -208,7 +207,6 @@ SET(organizer_HDRS waitingonclosedialog.h loadmechanism.h installationmanager.h - helper.h filedialogmemory.h executableslist.h editexecutablesdialog.h @@ -464,7 +462,6 @@ set(utilities csvbuilder shared/error_report eventfilter - helper shared/leaktrace persistentcookiejar serverinfo diff --git a/src/helper.cpp b/src/helper.cpp deleted file mode 100644 index 24446cb8..00000000 --- a/src/helper.cpp +++ /dev/null @@ -1,20 +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 . -*/ - -// moved to spawn.cpp diff --git a/src/helper.h b/src/helper.h deleted file mode 100644 index 335b8647..00000000 --- a/src/helper.h +++ /dev/null @@ -1,26 +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 . -*/ - -#ifndef HELPER_H -#define HELPER_H - -// all helper code moved to spawn.h and spawn.cpp -#include "spawn.h" - -#endif // HELPER_H diff --git a/src/main.cpp b/src/main.cpp index 74b04970..ba988ae3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,7 +38,6 @@ along with Mod Organizer. If not, see . #include "executableslist.h" #include "singleinstance.h" #include "utility.h" -#include "helper.h" #include "loglist.h" #include "selectiondialog.h" #include "moapplication.h" diff --git a/src/organizercore.cpp b/src/organizercore.cpp index fbc9083b..0da5b604 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -33,7 +33,6 @@ #include "lockeddialog.h" #include "instancemanager.h" #include -#include "helper.h" #include "previewdialog.h" #include diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp index f89b021c..ccbfcbfe 100644 --- a/src/settingsdialogworkarounds.cpp +++ b/src/settingsdialogworkarounds.cpp @@ -1,6 +1,6 @@ #include "settingsdialogworkarounds.h" #include "ui_settingsdialog.h" -#include "helper.h" +#include "spawn.h" #include WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d) diff --git a/src/spawn.cpp b/src/spawn.cpp index 6c524681..64766adf 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -33,7 +33,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include "helper.h" #include #include #include -- cgit v1.3.1