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.cpp | 414 +++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 307 insertions(+), 107 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/spawn.cpp b/src/spawn.cpp
index f77da35f..614bc92f 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -21,164 +21,364 @@ along with Mod Organizer. If not, see .
#include "report.h"
#include "utility.h"
+#include "env.h"
+#include "envwindows.h"
+#include "envsecurity.h"
+#include
#include
+#include
#include
#include
#include
#include
#include "helper.h"
-
#include
#include
#include
-
-
#include
-
-#include
+#include
using namespace MOBase;
using namespace MOShared;
+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;
+};
-static const int BUFSIZE = 4096;
-static bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory,
- bool suspended, bool hooked,
- HANDLE stdOut, HANDLE stdErr,
- HANDLE& processHandle, HANDLE& threadHandle)
+std::wstring pathEnv()
+{
+ 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;
- ::ZeroMemory(&si, sizeof(si));
- if (stdOut != INVALID_HANDLE_VALUE) {
- si.hStdOutput = stdOut;
+
+ 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 (stdErr != INVALID_HANDLE_VALUE) {
- si.hStdError = stdErr;
+
+ if (sp.stdErr != INVALID_HANDLE_VALUE) {
+ si.hStdError = sp.stdErr;
inheritHandles = TRUE;
si.dwFlags |= STARTF_USESTDHANDLES;
}
- si.cb = sizeof(si);
- size_t length = wcslen(binary) + wcslen(arguments) + 4;
- wchar_t *commandLine = nullptr;
- if (arguments[0] != L'\0') {
- commandLine = new wchar_t[length];
- _snwprintf(commandLine, length, L"\"%ls\" %ls", binary, arguments);
- } else {
- commandLine = new wchar_t[length];
- _snwprintf_s(commandLine, length, _TRUNCATE, L"\"%ls\"", binary);
- }
- QString moPath = QCoreApplication::applicationDirPath();
+ std::wstring commandLine;
- boost::scoped_array oldPath(new TCHAR[BUFSIZE]);
- DWORD offset = ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), BUFSIZE);
- if (offset > BUFSIZE) {
- oldPath.reset(new TCHAR[offset]);
- ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), offset);
+ if (sp.arguments[0] != L'\0') {
+ commandLine = L"\"" + sp.binary + L"\" " + sp.arguments;
+ } else {
+ commandLine = L"\"" + sp.binary + L"\"";
}
- {
- boost::scoped_array newPath(new TCHAR[offset + moPath.length() + 2]);
- _tcsncpy(newPath.get(), oldPath.get(), offset);
- newPath.get()[offset] = '\0';
- _tcsncat(newPath.get(), TEXT(";"), 1);
- _tcsncat(newPath.get(), ToWString(QDir::toNativeSeparators(moPath)).c_str(), moPath.length());
+ QString moPath = QCoreApplication::applicationDirPath();
- ::SetEnvironmentVariable(TEXT("PATH"), newPath.get());
- }
+ const auto oldPath = pathEnv();
+ setPathEnv(oldPath + L";" + QDir::toNativeSeparators(moPath).toStdWString());
PROCESS_INFORMATION pi;
BOOL success = FALSE;
- if (hooked) {
- success = ::CreateProcessHooked(nullptr,
- commandLine,
- nullptr, nullptr, // no special process or thread attributes
- inheritHandles, // inherit handles if we plan to use stdout or stderr reroute
- CREATE_BREAKAWAY_FROM_JOB,
- nullptr, // same environment as parent
- currentDirectory, // current directory
- &si, &pi // startup and process information
- );
+
+ 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);
} else {
- success = ::CreateProcess(nullptr,
- commandLine,
- nullptr, nullptr, // no special process or thread attributes
- inheritHandles, // inherit handles if we plan to use stdout or stderr reroute
- CREATE_BREAKAWAY_FROM_JOB,
- nullptr, // same environment as parent
- currentDirectory, // current directory
- &si, &pi // startup and process information
- );
+ success = ::CreateProcess(
+ nullptr, const_cast(commandLine.c_str()), nullptr, nullptr,
+ inheritHandles, CREATE_BREAKAWAY_FROM_JOB, nullptr,
+ sp.currentDirectory.c_str(), &si, &pi);
}
- ::SetEnvironmentVariable(TEXT("PATH"), oldPath.get());
-
- delete [] commandLine;
+ const auto e = GetLastError();
+ setPathEnv(oldPath);
if (!success) {
- throw windows_error("failed to start process");
+ return e;
}
processHandle = pi.hProcess;
threadHandle = pi.hThread;
- return true;
+
+ return ERROR_SUCCESS;
}
+std::wstring makeRightsDetails(const env::FileSecurity& fs)
+{
+ if (fs.rights.normalRights) {
+ return L"(normal rights)";
+ }
+
+ if (fs.rights.list.isEmpty()) {
+ return L"(none)";
+ }
+
+ std::wstring s = fs.rights.list.join("|").toStdWString();
+ if (!fs.rights.hasExecute) {
+ s += L" (execute is missing)";
+ }
+
+ return s;
+}
-HANDLE startBinary(const QFileInfo &binary,
- const QString &arguments,
- const QDir ¤tDirectory,
- bool hooked,
- HANDLE stdOut,
- HANDLE stdErr)
+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 (fs.error.isEmpty()) {
+ owner = fs.owner.toStdWString();
+ rights = makeRightsDetails(fs);
+ } else {
+ owner = fs.error.toStdWString();
+ rights = fs.error.toStdWString();
+ }
+ } else {
+ owner = L"(file not found)";
+ rights = L"(file not found)";
+ }
+
+ const bool cwdExists = (sp.currentDirectory.empty() ?
+ true : QFileInfo(QString::fromStdWString(sp.currentDirectory)).isDir());
+
+ const auto appDir = QCoreApplication::applicationDirPath();
+ const auto sep = QDir::separator();
+
+ const std::wstring usvfs_x86_dll =
+ QFileInfo(appDir + sep + "usvfs_x86.dll").isFile() ? L"ok" : L"not found";
+
+ const std::wstring usvfs_x64_dll =
+ QFileInfo(appDir + sep + "usvfs_x64.dll").isFile() ? L"ok" : L"not found";
+
+ const std::wstring usvfs_x86_proxy =
+ QFileInfo(appDir + sep + "usvfs_proxy_x86.exe").isFile() ? L"ok" : L"not found";
+
+ const std::wstring usvfs_x64_proxy =
+ QFileInfo(appDir + sep + "usvfs_proxy_x64.exe").isFile() ? L"ok" : L"not found";
+
+ std::wstring elevated;
+ if (auto b=env::Environment().windowsInfo().isElevated()) {
+ elevated = (*b ? L"yes" : L"no");
+ } else {
+ elevated = L"(not available)";
+ }
+
+ return fmt::format(
+ L"Error {code} {codename}: {error}\n"
+ L" . binary: '{bin}'\n"
+ L" . owner: {owner}\n"
+ L" . rights: {rights}\n"
+ L" . arguments: '{args}'\n"
+ L" . cwd: '{cwd}'{cwdexists}\n"
+ L" . stdout: {stdout}, stderr: {stderr}, suspended: {susp}, 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"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"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),
+ fmt::arg(L"x86_proxy", usvfs_x86_proxy),
+ fmt::arg(L"x64_proxy", usvfs_x64_proxy),
+ fmt::arg(L"elevated", elevated));
+}
+
+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());
+
+ QString content;
+
+ if (code == ERROR_INVALID_PARAMETER) {
+ content = 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(
+ "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 {
+ content = QString::fromStdWString(formatSystemMessage(code));
+ }
+
+ QWidget *window = qApp->activeWindow();
+ if ((window != nullptr) && (!window->isVisible())) {
+ window = nullptr;
+ }
+
+ MOBase::TaskDialog(window, title)
+ .main(mainText)
+ .content(content)
+ .details(details)
+ .exec();
+}
+
+bool confirmRestartAsAdmin(const SpawnParameters& sp)
+{
+ const auto details = QString::fromStdWString(
+ makeDetails(sp, ERROR_ELEVATION_REQUIRED));
+
+ 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());
+
+ const auto content = QObject::tr(
+ "This program is requesting to run as administrator but Mod Organizer "
+ "itself is not running as administrator. Running programs as administrator "
+ "is typically unnecessary as long as the game and Mod Organizer have been "
+ "installed outside \"Program Files\".\r\n\r\n"
+ "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)
+ .main(mainText)
+ .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})
+ .button({
+ QObject::tr("Cancel"),
+ QMessageBox::Cancel})
+ .exec();
+
+ return (r == QMessageBox::Yes);
+}
+
+} // namespace
+
+void startBinaryAdmin(const SpawnParameters& sp)
+{
+ if (!confirmRestartAsAdmin(sp)) {
+ log::debug("user declined");
+ return;
+ }
+
+ log::info("restarting MO as administrator");
+
+ 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))) {
+ qApp->exit(0);
+ }
+}
+
+HANDLE startBinary(
+ const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory,
+ bool hooked, HANDLE stdOut, HANDLE stdErr)
+{
+ 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;
- std::wstring binaryName = ToWString(QDir::toNativeSeparators(binary.absoluteFilePath()));
- std::wstring currentDirectoryName = ToWString(QDir::toNativeSeparators(currentDirectory.absolutePath()));
+ const auto e = spawn(sp, processHandle, threadHandle);
- try {
- if (!spawn(binaryName.c_str(), ToWString(arguments).c_str(), currentDirectoryName.c_str(),
- true, hooked, stdOut, stdErr, processHandle, threadHandle)) {
- reportError(QObject::tr("failed to spawn \"%1\"").arg(binary.fileName()));
- return INVALID_HANDLE_VALUE;
+ switch (e)
+ {
+ case ERROR_SUCCESS:
+ {
+ ::CloseHandle(threadHandle);
+ return processHandle;
}
- } catch (const windows_error &e) {
- if (e.getErrorCode() == ERROR_ELEVATION_REQUIRED) {
- if (QMessageBox::question(QApplication::activeModalWidget(), QObject::tr("Elevation required"),
- QObject::tr("This process requires elevation to run.\n"
- "This is a potential security risk so I highly advise you to investigate if\n"
- "\"%1\"\n"
- "can be installed to work without elevation.\n\n"
- "Restart Mod Organizer as an elevated process?\n"
- "You will be asked if you want to allow helper.exe to make changes to the system. "
- "You will need to relaunch the process above manually.").arg(
- QDir::toNativeSeparators(binary.absoluteFilePath())),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- WCHAR cwd[MAX_PATH];
- if (!GetCurrentDirectory(MAX_PATH, cwd)) {
- reportError(QObject::tr("failed to spawn \"%1\": %2").arg(binary.fileName()).arg(::GetLastError()));
- cwd[0] = L'\0';
- }
- if (!Helper::adminLaunch(
- qApp->applicationDirPath().toStdWString(),
- qApp->applicationFilePath().toStdWString(),
- std::wstring(cwd))) {
- return INVALID_HANDLE_VALUE;
- }
- qApp->exit(0);
- }
+
+ case ERROR_ELEVATION_REQUIRED:
+ {
+ startBinaryAdmin(sp);
return INVALID_HANDLE_VALUE;
+ }
- } else {
- reportError(QObject::tr("failed to spawn \"%1\": %2").arg(binary.fileName()).arg(e.what()));
+ default:
+ {
+ spawnFailed(sp, e);
return INVALID_HANDLE_VALUE;
}
}
-
- ::CloseHandle(threadHandle);
- return processHandle;
}
--
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.cpp')
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.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 ac6bc5fd01e115d523de65a02e46b2cde1188d37 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 12 Sep 2019 02:45:03 -0400
Subject: testForSteam() now uses env to get processes moved processes from
env.cpp to envmodule.cpp, merged what crash dumps did with what was in
testForSteam()
---
src/env.cpp | 105 ++++++------------------------------------------------
src/env.h | 5 +++
src/envmodule.cpp | 81 +++++++++++++++++++++++++++++++++++++++++
src/envmodule.h | 24 ++++++++++++-
src/spawn.cpp | 62 +++++++-------------------------
5 files changed, 131 insertions(+), 146 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/env.cpp b/src/env.cpp
index e2b85560..34f53294 100644
--- a/src/env.cpp
+++ b/src/env.cpp
@@ -72,6 +72,11 @@ const std::vector& Environment::loadedModules() const
return m_modules;
}
+std::vector Environment::runningProcesses() const
+{
+ return getRunningProcesses();
+}
+
const WindowsInfo& Environment::windowsInfo() const
{
if (!m_windows) {
@@ -166,18 +171,6 @@ void Environment::dumpDisks(const Settings& s) const
}
-struct Process
-{
- std::wstring filename;
- DWORD pid;
-
- Process(std::wstring f, DWORD id)
- : filename(std::move(f)), pid(id)
- {
- }
-};
-
-
// returns the filename of the given process or the current one
//
std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
@@ -233,84 +226,6 @@ std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE)
return {};
}
-std::vector runningProcessesIds()
-{
- // double the buffer size 10 times
- const int MaxTries = 10;
-
- // initial size of 300 processes, unlikely to be more than that
- std::size_t size = 300;
-
- for (int tries=0; tries(size);
- std::fill(ids.get(), ids.get() + size, 0);
-
- DWORD bytesGiven = static_cast(size * sizeof(ids[0]));
- DWORD bytesWritten = 0;
-
- if (!EnumProcesses(ids.get(), bytesGiven, &bytesWritten))
- {
- const auto e = GetLastError();
-
- std::wcerr
- << L"failed to enumerate processes, "
- << formatSystemMessage(e) << L"\n";
-
- return {};
- }
-
- if (bytesWritten == bytesGiven) {
- // no way to distinguish between an exact fit and not enough space,
- // just try again
- size *= 2;
- continue;
- }
-
- const auto count = bytesWritten / sizeof(ids[0]);
- return std::vector(ids.get(), ids.get() + count);
- }
-
- std::cerr << L"too many processes to enumerate";
- return {};
-}
-
-std::vector runningProcesses()
-{
- const auto pids = runningProcessesIds();
- std::vector v;
-
- for (const auto& pid : pids) {
- if (pid == 0) {
- // the idle process has pid 0 and seems to be picked up by EnumProcesses()
- continue;
- }
-
- HandlePtr h(OpenProcess(
- PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
-
- if (!h) {
- const auto e = GetLastError();
-
- if (e != ERROR_ACCESS_DENIED) {
- // don't log access denied, will happen a lot for system processes, even
- // when elevated
- std::wcerr
- << L"failed to open process " << pid << L", "
- << formatSystemMessage(e) << L"\n";
- }
-
- continue;
- }
-
- auto filename = processFilename(h.get());
- if (!filename.empty()) {
- v.emplace_back(std::move(filename), pid);
- }
- }
-
- return v;
-}
-
DWORD findOtherPid()
{
const std::wstring defaultName = L"ModOrganizer.exe";
@@ -322,7 +237,7 @@ DWORD findOtherPid()
std::wclog << L"this process id is " << thisPid << L"\n";
// getting the filename for this process, assumes the other process has the
- // smae one
+ // same one
auto filename = processFilename();
if (filename.empty()) {
std::wcerr
@@ -335,15 +250,15 @@ DWORD findOtherPid()
}
// getting all running processes
- const auto processes = runningProcesses();
+ const auto processes = getRunningProcesses();
std::wclog << L"there are " << processes.size() << L" processes running\n";
// going through processes, trying to find one with the same name and a
// different pid than this process has
for (const auto& p : processes) {
- if (p.filename == filename) {
- if (p.pid != thisPid) {
- return p.pid;
+ if (p.name() == filename) {
+ if (p.pid() != thisPid) {
+ return p.pid();
}
}
}
diff --git a/src/env.h b/src/env.h
index 46095ca3..6222c86b 100644
--- a/src/env.h
+++ b/src/env.h
@@ -7,6 +7,7 @@ namespace env
{
class Module;
+class Process;
class SecurityProduct;
class WindowsInfo;
class Metrics;
@@ -129,6 +130,10 @@ public:
//
const std::vector& loadedModules() const;
+ // list of running processes; not cached
+ //
+ std::vector runningProcesses() const;
+
// information about the operating system
//
const WindowsInfo& windowsInfo() const;
diff --git a/src/envmodule.cpp b/src/envmodule.cpp
index 8cea414a..3f1f8912 100644
--- a/src/envmodule.cpp
+++ b/src/envmodule.cpp
@@ -320,6 +320,40 @@ QString Module::getMD5() const
}
+Process::Process(DWORD pid, QString name)
+ : m_pid(pid), m_name(std::move(name))
+{
+}
+
+DWORD Process::pid() const
+{
+ return m_pid;
+}
+
+const QString& Process::name() const
+{
+ return m_name;
+}
+
+// whether this process can be accessed; fails if the current process doesn't
+// have the proper permissions
+//
+bool Process::canAccess() const
+{
+ HandlePtr h(OpenProcess(
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, m_pid));
+
+ if (!h) {
+ const auto e = GetLastError();
+ if (e == ERROR_ACCESS_DENIED) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
std::vector getLoadedModules()
{
HandlePtr snapshot(CreateToolhelp32Snapshot(
@@ -373,4 +407,51 @@ std::vector getLoadedModules()
return v;
}
+
+std::vector getRunningProcesses()
+{
+ HandlePtr snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+
+ if (snapshot.get() == INVALID_HANDLE_VALUE)
+ {
+ const auto e = GetLastError();
+ log::error("CreateToolhelp32Snapshot() failed, {}", formatSystemMessage(e));
+ return {};
+ }
+
+ PROCESSENTRY32 entry = {};
+ entry.dwSize = sizeof(entry);
+
+ // first process, this shouldn't fail because there's at least one process
+ // running
+ if (!Process32First(snapshot.get(), &entry)) {
+ const auto e = GetLastError();
+ log::error("Process32First() failed, {}", formatSystemMessage(e));
+ return {};
+ }
+
+ std::vector v;
+
+ for (;;)
+ {
+ v.push_back(Process(
+ entry.th32ProcessID,
+ QString::fromStdWString(entry.szExeFile)));
+
+ // next process
+ if (!Process32Next(snapshot.get(), &entry))
+ {
+ const auto e = GetLastError();
+
+ // no more processes is not an error
+ if (e != ERROR_NO_MORE_FILES)
+ log::error("Process32Next() failed, {}", formatSystemMessage(e));
+
+ break;
+ }
+ }
+
+ return v;
+}
+
} // namespace
diff --git a/src/envmodule.h b/src/envmodule.h
index 3f0f99ab..deb7520f 100644
--- a/src/envmodule.h
+++ b/src/envmodule.h
@@ -12,7 +12,7 @@ namespace env
class Module
{
public:
- explicit Module(QString path, std::size_t fileSize);
+ Module(QString path, std::size_t fileSize);
// returns the module's path
//
@@ -96,6 +96,28 @@ private:
};
+// represents one process
+//
+class Process
+{
+public:
+ Process(DWORD pid, QString name);
+
+ DWORD pid() const;
+ const QString& name() const;
+
+ // whether this process can be accessed; fails if the current process doesn't
+ // have the proper permissions
+ //
+ bool canAccess() const;
+
+private:
+ DWORD m_pid;
+ QString m_name;
+};
+
+
+std::vector getRunningProcesses();
std::vector getLoadedModules();
} // namespace env
diff --git a/src/spawn.cpp b/src/spawn.cpp
index a56df23a..604f9ffa 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 "envmodule.h"
#include "settings.h"
#include
#include
@@ -49,7 +50,6 @@ namespace spawn
namespace
{
-
std::wstring pathEnv()
{
std::wstring s(4000, L' ');
@@ -249,6 +249,9 @@ void spawnFailed(const SpawnParameters& sp, DWORD code)
"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.")
+ .arg(QDir::toNativeSeparators(sp.binary.absoluteFilePath()));
} else {
content = QString::fromStdWString(formatSystemMessage(code));
}
@@ -337,10 +340,7 @@ void startBinaryAdmin(const SpawnParameters& sp)
bool checkBinary(QWidget* parent, const SpawnParameters& sp)
{
if (!sp.binary.exists()) {
- reportError(
- QObject::tr("Executable not found: %1")
- .arg(sp.binary.absoluteFilePath()));
-
+ spawnFailed(sp, ERROR_FILE_NOT_FOUND);
return false;
}
@@ -349,61 +349,23 @@ bool checkBinary(QWidget* parent, const SpawnParameters& sp)
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;
- }
-
+ const auto ps = env::Environment().runningProcesses();
*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)) {
+ for (const auto& p : ps) {
+ if ((p.name().compare("Steam.exe", Qt::CaseInsensitive) == 0) ||
+ (p.name().compare("SteamService.exe", Qt::CaseInsensitive) == 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);
- }
+ *access = p.canAccess();
break;
}
+ }
- } while(Process32Next(hProcessSnap, &pe32));
-
- CloseHandle(hProcessSnap);
return true;
}
--
cgit v1.3.1
From b867b0bccdb32d94e1646d8f3f8e8a39d4b2446e Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 12 Sep 2019 03:49:17 -0400
Subject: refactored steam handling
---
src/spawn.cpp | 247 +++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 159 insertions(+), 88 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 604f9ffa..80f82a28 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -329,7 +329,9 @@ void startBinaryAdmin(const SpawnParameters& sp)
if (Helper::adminLaunch(
qApp->applicationDirPath().toStdWString(),
qApp->applicationFilePath().toStdWString(),
- std::wstring(cwd))) {
+ std::wstring(cwd)))
+ {
+ log::debug("exiting MO");
qApp->exit(0);
}
}
@@ -347,129 +349,198 @@ bool checkBinary(QWidget* parent, const SpawnParameters& sp)
return true;
}
-bool testForSteam(bool *found, bool *access)
+struct SteamStatus
{
- if (found == nullptr || access == nullptr) {
- return false;
- }
+ bool running=false;
+ bool accessible=false;
+};
+
+SteamStatus getSteamStatus()
+{
+ SteamStatus ss;
const auto ps = env::Environment().runningProcesses();
- *found = false;
for (const auto& p : ps) {
if ((p.name().compare("Steam.exe", Qt::CaseInsensitive) == 0) ||
(p.name().compare("SteamService.exe", Qt::CaseInsensitive) == 0))
{
- *found = true;
- *access = p.canAccess();
+ ss.running = true;
+ ss.accessible = p.canAccess();
+
+ log::debug(
+ "'{}' is running, accessible={}",
+ p.name(), (ss.accessible ? "yes" : "no"));
+
break;
}
}
- return true;
+ return ss;
}
-void startSteam(QWidget *widget)
+bool 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;
- }
+ log::debug("starting steam");
+
+ const QString keyName = "HKEY_CURRENT_USER\\Software\\Valve\\Steam";
+ const QString valueName = "SteamExe";
+
+ const QSettings steamSettings(keyName, QSettings::NativeFormat);
+ 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;
+ }
+
+ const QString program = QString("\"%1\"").arg(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);
}
- 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."));
+ }
+
+ log::debug(
+ "starting steam process:\n"
+ " . program: '{}'\n"
+ " . username={}, password={}",
+ program,
+ (username.isEmpty() ? "no" : "yes"),
+ (password.isEmpty() ? "no" : "yes"));
+
+ if (!QProcess::startDetached(program, args)) {
+ reportError(QObject::tr("Failed to start \"%1\"").arg(program));
+ return false;
+ }
+
+ QMessageBox::information(
+ widget, QObject::tr("Waiting"),
+ QObject::tr("Please press OK once you're logged into steam."));
+
+ return true;
+}
+
+bool gameRequiresSteam(const QDir& gameDirectory, const Settings& settings)
+{
+ static const std::vector files = {
+ "steam_api.dll", "steam_api64.dll"
+ };
+
+ for (const auto& file : files) {
+ const QFileInfo fi(gameDirectory.absoluteFilePath(file));
+ if (fi.exists()) {
+ log::debug("found '{}'", fi.absoluteFilePath());
+ return true;
}
}
+
+ 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)
{
+ log::debug("checking steam");
+
if (!steamAppID.isEmpty()) {
- ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(steamAppID).c_str());
+ ::SetEnvironmentVariableW(L"SteamAPPId", steamAppID.toStdWString().c_str());
} else {
- ::SetEnvironmentVariableW(L"SteamAPPId",
- ToWString(settings.steam().appID()).c_str());
+ ::SetEnvironmentVariableW(L"SteamAPPId", settings.steam().appID().toStdWString().c_str());
}
- if ((QFileInfo(gameDirectory.absoluteFilePath("steam_api.dll")).exists() ||
- QFileInfo(gameDirectory.absoluteFilePath("steam_api64.dll")).exists())
- && (settings.game().loadMechanismType() == LoadMechanism::LOAD_MODORGANIZER)) {
+ if (!gameRequiresSteam(gameDirectory, settings)) {
+ log::debug("games doesn't seem to require steam");
+ return true;
+ }
- bool steamFound = true;
- bool steamAccess = true;
- if (!testForSteam(&steamFound, &steamAccess)) {
- log::error("unable to determine state of Steam");
- }
+ auto ss = getSteamStatus();
- if (!steamFound) {
- QDialogButtonBox::StandardButton result;
- 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?"),
- 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 (!ss.running) {
+ log::debug("steam isn't running, asking to start steam");
+ const auto c = confirmStartSteam(parent, sp);
+
+ if (c == QDialogButtonBox::Yes) {
+ log::debug("user wants to start steam");
+ startSteam(parent);
+
+ // double-check that Steam is started
+ ss = getSteamStatus();
+ if (!ss.running) {
+ log::error("could not start steam, continuing and hoping for the best");
+ return true;
}
+ } else if (c == QDialogButtonBox::No) {
+ log::debug("user declined to start steam");
+ return true;
+ } else {
+ log::debug("user cancelled");
+ return false;
}
+ }
+
+ if (ss.running && !ss.accessible) {
+ log::debug("steam is running but is not accessible, asking to restart MO");
+ const auto c = confirmRestartAsAdminForSteam(parent, sp);
- if (!steamAccess) {
- QDialogButtonBox::StandardButton result;
- 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 "
- "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;
- }
+ 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;
- } else if (result == QDialogButtonBox::Cancel) {
- return false;
}
+
+ log::error("unable to relaunch MO as admin");
+ return false;
+ } else if (c == QDialogButtonBox::No) {
+ log::debug("user declined to restart MO, continuing");
+ return true;
+ } else {
+ log::debug("user cancelled");
+ return false;
}
}
--
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/spawn.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 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.cpp')
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 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/spawn.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
From c1ab18b614aa6212f942d8d91afa0b191802f599 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 11:14:14 -0400
Subject: moved the content of checkService() to env::getService(), refactored
it
---
src/env.cpp | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/env.h | 58 +++++++++++++++
src/spawn.cpp | 92 ++++--------------------
3 files changed, 301 insertions(+), 77 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/env.cpp b/src/env.cpp
index 1aaaa8ef..78b5dc96 100644
--- a/src/env.cpp
+++ b/src/env.cpp
@@ -213,6 +213,234 @@ QString set(const QString& n, const QString& v)
}
+Service::Service(QString name)
+ : Service(std::move(name), StartType::None, Status::None)
+{
+}
+
+Service::Service(QString name, StartType st, Status s)
+ : m_name(std::move(name)), m_startType(st), m_status(s)
+{
+}
+
+const QString& Service::name() const
+{
+ return m_name;
+}
+
+bool Service::isValid() const
+{
+ return (m_startType != StartType::None) && (m_status != Status::None);
+}
+
+Service::StartType Service::startType() const
+{
+ return m_startType;
+}
+
+Service::Status Service::status() const
+{
+ return m_status;
+}
+
+QString Service::toString() const
+{
+ return QString("service '%1', start=%2, status=%3")
+ .arg(m_name)
+ .arg(env::toString(m_startType))
+ .arg(env::toString(m_status));
+}
+
+
+QString toString(Service::StartType st)
+{
+ using ST = Service::StartType;
+
+ switch (st)
+ {
+ case ST::None:
+ return "none";
+
+ case ST::Disabled:
+ return "disabled";
+
+ case ST::Enabled:
+ return "enabled";
+
+ default:
+ return QString("unknown %1").arg(static_cast(st));
+ }
+}
+
+QString toString(Service::Status st)
+{
+ using S = Service::Status;
+
+ switch (st)
+ {
+ case S::None:
+ return "none";
+
+ case S::Stopped:
+ return "stopped";
+
+ case S::Running:
+ return "running";
+
+ default:
+ return QString("unknown %1").arg(static_cast(st));
+ }
+}
+
+Service::StartType getServiceStartType(SC_HANDLE s, const QString& name)
+{
+ DWORD needed = 0;
+
+ if (!QueryServiceConfig(s, NULL, 0, &needed)) {
+ const auto e = GetLastError();
+
+ if (e != ERROR_INSUFFICIENT_BUFFER) {
+ log::error(
+ "QueryServiceConfig() for size for '{}' failed, {}",
+ name, GetLastError());
+
+ return Service::StartType::None;
+ }
+ }
+
+ const auto size = needed;
+ MallocPtr config(
+ static_cast(std::malloc(size)));
+
+ if (!QueryServiceConfig(s, config.get(), size, &needed)) {
+ const auto e = GetLastError();
+
+ log::error(
+ "QueryServiceConfig() for '{}' failed", name, formatSystemMessage(e));
+
+ return Service::StartType::None;
+ }
+
+
+ switch (config->dwStartType)
+ {
+ case SERVICE_AUTO_START: // fall-through
+ case SERVICE_BOOT_START:
+ case SERVICE_DEMAND_START:
+ case SERVICE_SYSTEM_START:
+ {
+ return Service::StartType::Enabled;
+ }
+
+ case SERVICE_DISABLED:
+ {
+ return Service::StartType::Disabled;
+ }
+
+ default:
+ {
+ log::error(
+ "unknown service start type {} for '{}'",
+ config->dwStartType, name);
+
+ return Service::StartType::None;
+ }
+ }
+}
+
+Service::Status getServiceStatus(SC_HANDLE s, const QString& name)
+{
+ DWORD needed = 0;
+
+ if (!QueryServiceStatusEx(s, SC_STATUS_PROCESS_INFO, NULL, 0, &needed)) {
+ const auto e = GetLastError();
+
+ if (e != ERROR_INSUFFICIENT_BUFFER) {
+ log::error(
+ "QueryServiceStatusEx() for size for '{}' failed, {}",
+ name, GetLastError());
+
+ return Service::Status::None;
+ }
+ }
+
+ const auto size = needed;
+ MallocPtr status(
+ static_cast(std::malloc(size)));
+
+ const auto r = QueryServiceStatusEx(
+ s, SC_STATUS_PROCESS_INFO, reinterpret_cast(status.get()),
+ size, &needed);
+
+ if (!r) {
+ const auto e = GetLastError();
+
+ log::error(
+ "QueryServiceStatusEx() failed for '{}', {}",
+ name, formatSystemMessage(e));
+
+ return Service::Status::None;
+ }
+
+
+ switch (status->dwCurrentState)
+ {
+ case SERVICE_START_PENDING: // fall-through
+ case SERVICE_CONTINUE_PENDING:
+ case SERVICE_RUNNING:
+ {
+ return Service::Status::Running;
+ }
+
+ case SERVICE_STOPPED: // fall-through
+ case SERVICE_STOP_PENDING:
+ case SERVICE_PAUSE_PENDING:
+ case SERVICE_PAUSED:
+ {
+ return Service::Status::Stopped;
+ }
+
+ default:
+ {
+ log::error(
+ "unknown service status {} for '{}'",
+ status->dwCurrentState, name);
+
+ return Service::Status::None;
+ }
+ }
+}
+
+Service getService(const QString& name)
+{
+ // service manager
+ const LocalPtr scm(OpenSCManager(
+ NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG));
+
+ if (!scm) {
+ const auto e = GetLastError();
+ log::error("OpenSCManager() failed, {}", formatSystemMessage(e));
+ return Service(name);
+ }
+
+ // service
+ const LocalPtr s(OpenService(
+ scm.get(), name.toStdWString().c_str(),
+ SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG));
+
+ if (!s) {
+ const auto e = GetLastError();
+ log::error("OpenService() failed for '{}', {}", name, formatSystemMessage(e));
+ return Service(name);
+ }
+
+ const auto startType = getServiceStartType(s.get(), name);
+ const auto status = getServiceStatus(s.get(), name);
+
+ return {name, startType, status};
+}
+
+
// 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 7bdc9b85..1760c7fe 100644
--- a/src/env.h
+++ b/src/env.h
@@ -93,6 +93,24 @@ struct MallocFreer
template
using MallocPtr = std::unique_ptr;
+
+// used by LocalPtr, calls LocalFree() as the deleter
+//
+template
+struct LocalFreer
+{
+ using pointer = T;
+
+ void operator()(T p)
+ {
+ ::LocalFree(p);
+ }
+};
+
+template
+using LocalPtr = std::unique_ptr>;
+
+
// creates a console in the constructor and destroys it in the destructor,
// also redirects standard streams
//
@@ -172,6 +190,46 @@ QString addPath(const QString& s);
QString setPath(const QString& s);
+class Service
+{
+public:
+ enum class StartType
+ {
+ None = 0,
+ Disabled,
+ Enabled
+ };
+
+ enum class Status
+ {
+ None = 0,
+ Stopped,
+ Running
+ };
+
+
+ explicit Service(QString name);
+ Service(QString name, StartType st, Status s);
+
+ bool isValid() const;
+
+ const QString& name() const;
+ StartType startType() const;
+ Status status() const;
+
+ QString toString() const;
+
+private:
+ QString m_name;
+ StartType m_startType;
+ Status m_status;
+};
+
+
+Service getService(const QString& name);
+QString toString(Service::StartType st);
+QString toString(Service::Status st);
+
enum class CoreDumpTypes
{
Mini = 1,
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 64766adf..551a5adb 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -428,7 +428,6 @@ void startBinaryAdmin(const SpawnParameters& sp)
restartAsAdmin();
}
-
bool checkBinary(QWidget* parent, const SpawnParameters& sp)
{
if (!sp.binary.exists()) {
@@ -557,9 +556,9 @@ bool checkSteam(
log::debug("checking steam");
if (!steamAppID.isEmpty()) {
- ::SetEnvironmentVariableW(L"SteamAPPId", steamAppID.toStdWString().c_str());
+ env::set("SteamAPPId", steamAppID);
} else {
- ::SetEnvironmentVariableW(L"SteamAPPId", settings.steam().appID().toStdWString().c_str());
+ env::set("SteamAPPId", settings.steam().appID());
}
if (!gameRequiresSteam(gameDirectory, settings)) {
@@ -584,7 +583,7 @@ bool checkSteam(
// double-check that Steam is started
ss = getSteamStatus();
if (!ss.running) {
- log::error("steam is still not running, continuing and hoping for the best");
+ log::error("steam is still not running, hoping for the best");
return true;
}
} else if (c == QDialogButtonBox::No) {
@@ -615,90 +614,29 @@ bool checkSteam(
return true;
}
-bool checkService()
+bool checkEventLogService()
{
- 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;
- }
+ const auto s = env::getService("EventLog");
- 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 (!s.isValid()) {
+ log::error("cannot determine the status of the EventLog, continuing");
+ return true;
}
- if (serviceStatus) {
- LocalFree(serviceStatus);
- }
- if (serviceConfig) {
- LocalFree(serviceConfig);
- }
- if (serviceHandle) {
- CloseServiceHandle(serviceHandle);
- }
- if (serviceManagerHandle) {
- CloseServiceHandle(serviceManagerHandle);
+ if (s.status() == env::Service::Status::Running) {
+ log::debug("{}", s.toString());
+ return true;
+ } else {
+ log::error("{}", s.toString());
+ return false;
}
-
- return serviceRunning;
}
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 (!checkEventLogService()) {
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"
--
cgit v1.3.1
From 8bc67a86d64c86cf7f1eeb2c656dd414c0716d0b Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 11:17:52 -0400
Subject: moved event log warning to dialogs
---
src/spawn.cpp | 45 ++++++++++++++++++++++-----------------------
1 file changed, 22 insertions(+), 23 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 551a5adb..c9025d98 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -330,7 +330,22 @@ QuestionBoxMemory::Button confirmRestartAsAdminForSteam(QWidget* parent, const S
QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
}
-} // namepsace
+bool eventLogNotRunning(
+ QWidget* parent, const env::Service& s, const SpawnParameters& sp)
+{
+ const auto r = 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(sp.binary.fileName()),
+ QDialogButtonBox::Yes | QDialogButtonBox::No);
+
+ return (r != QDialogButtonBox::No);
+}
+
+} // namespace
namespace spawn
@@ -614,8 +629,11 @@ bool checkSteam(
return true;
}
-bool checkEventLogService()
+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.
+
const auto s = env::getService("EventLog");
if (!s.isValid()) {
@@ -626,29 +644,10 @@ bool checkEventLogService()
if (s.status() == env::Service::Status::Running) {
log::debug("{}", s.toString());
return true;
- } else {
- log::error("{}", s.toString());
- return false;
- }
-}
-
-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 (!checkEventLogService()) {
- 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(sp.binary.fileName()),
- QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) {
- return false;
- }
}
- return true;
+ log::error("{}", s.toString());
+ return dialogs::eventLogNotRunning(parent, s, sp);
}
bool checkBlacklist(QWidget* parent, const SpawnParameters& sp, const Settings& settings)
--
cgit v1.3.1
From a5db7ed864ac58657bf9bfbbc292cdccbeeaa38b Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 11:23:21 -0400
Subject: added Settings::isExecutableBlacklisted() moved blacklisted
confirmation to dialogs
---
src/settings.cpp | 11 +++++++++++
src/settings.h | 1 +
src/spawn.cpp | 29 ++++++++++++++++-------------
3 files changed, 28 insertions(+), 13 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/settings.cpp b/src/settings.cpp
index 7fdda2bf..ae487c18 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -222,6 +222,17 @@ QString Settings::executablesBlacklist() const
return get(m_Settings, "Settings", "executable_blacklist", def);
}
+bool Settings::isExecutableBlacklisted(const QString& s) const
+{
+ for (auto exec : executablesBlacklist().split(";")) {
+ if (exec.compare(s, Qt::CaseInsensitive) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void Settings::setExecutablesBlacklist(const QString& s)
{
set(m_Settings, "Settings", "executable_blacklist", s);
diff --git a/src/settings.h b/src/settings.h
index 815ed160..cd478a5b 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -678,6 +678,7 @@ public:
// by MO but given to usvfs when starting an executable
//
QString executablesBlacklist() const;
+ bool isExecutableBlacklisted(const QString& s) const;
void setExecutablesBlacklist(const QString& s);
// ? looks obsolete, only used by dead code
diff --git a/src/spawn.cpp b/src/spawn.cpp
index c9025d98..45324d79 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -345,6 +345,20 @@ bool eventLogNotRunning(
return (r != QDialogButtonBox::No);
}
+bool confirmBlacklisted(QWidget* parent, const SpawnParameters& sp)
+{
+ const auto r = 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(sp.binary.fileName()),
+ QDialogButtonBox::Yes | QDialogButtonBox::No);
+
+ return (r != QDialogButtonBox::No);
+}
+
} // namespace
@@ -652,18 +666,8 @@ bool checkEnvironment(QWidget* parent, const SpawnParameters& sp)
bool checkBlacklist(QWidget* parent, const SpawnParameters& sp, const Settings& settings)
{
- for (auto exec : settings.executablesBlacklist().split(";")) {
- 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(sp.binary.fileName()),
- QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) {
- return false;
- }
- }
+ if (settings.isExecutableBlacklisted(sp.binary.fileName())) {
+ return dialogs::confirmBlacklisted(parent, sp);
}
return true;
@@ -779,7 +783,6 @@ bool backdateBSAs(const std::wstring &moPath, const std::wstring &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(
--
cgit v1.3.1
From 9bac57e3e864bd300fadccfaa194a6f3d28c9de2 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 15:03:03 -0400
Subject: steam confirmation now using TaskDialog fixed dialog choices not
remembering files
---
src/settings.cpp | 2 +-
src/settingsdialog.ui | 2 +-
src/spawn.cpp | 95 ++++++++++++++++++++++++++++++++++-----------------
3 files changed, 65 insertions(+), 34 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/settings.cpp b/src/settings.cpp
index ae487c18..7cea52fb 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -946,7 +946,7 @@ QuestionBoxMemory::Button WidgetSettings::questionButton(
if (!filename.isEmpty()) {
const auto fileSetting = windowName + "/" + filename;
- if (auto v=getOptional(m_Settings, sectionName, filename)) {
+ if (auto v=getOptional(m_Settings, sectionName, fileSetting)) {
return static_cast(*v);
}
}
diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui
index 1a3726fb..40079441 100644
--- a/src/settingsdialog.ui
+++ b/src/settingsdialog.ui
@@ -191,7 +191,7 @@ p, li { white-space: pre-wrap; }
This will make all dialogs show up again where you checked the "Remember selection"-box.
- Reset Dialogs
+ Reset Dialog Choices
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 45324d79..e18e6bb3 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -183,6 +183,7 @@ QMessageBox::StandardButton badSteamReg(
"The path to the Steam executable cannot be found. You might try "
"reinstalling Steam."))
.details(details)
+ .icon(QMessageBox::Critical)
.button({
QObject::tr("Continue without starting Steam"),
QObject::tr("The program may fail to launch."),
@@ -203,6 +204,7 @@ QMessageBox::StandardButton startSteamFailed(
.main(QObject::tr("Cannot start Steam"))
.content(makeContent(sp, e))
.details(details)
+ .icon(QMessageBox::Critical)
.button({
QObject::tr("Continue without starting Steam"),
QObject::tr("The program may fail to launch."),
@@ -232,6 +234,7 @@ void spawnFailed(const SpawnParameters& sp, DWORD code)
.main(mainText)
.content(makeContent(sp, code))
.details(details)
+ .icon(QMessageBox::Critical)
.exec();
}
@@ -261,6 +264,7 @@ void helperFailed(
.main(mainText)
.content(makeContent(sp, code))
.details(details)
+ .icon(QMessageBox::Critical)
.exec();
}
@@ -295,26 +299,45 @@ bool confirmRestartAsAdmin(const SpawnParameters& sp)
.main(mainText)
.content(content)
.details(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})
+ 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);
}
-QuestionBoxMemory::Button confirmStartSteam(QWidget* parent, const SpawnParameters& sp)
+QMessageBox::StandardButton confirmStartSteam(
+ QWidget* window, const SpawnParameters& sp, const QString& details)
{
- 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);
+ const auto title = QObject::tr("Launch Steam");
+ const auto mainText = QObject::tr("This program requires Steam");
+ const auto content = QObject::tr(
+ "Mod Organizer has detected that this program likely requires Steam to be "
+ "running to function properly.");
+
+ return MOBase::TaskDialog(window, title)
+ .main(mainText)
+ .content(content)
+ .details(details)
+ .icon(QMessageBox::Question)
+ .button({
+ QObject::tr("Start Steam"),
+ QMessageBox::Yes})
+ .button({
+ QObject::tr("Continue without starting Steam"),
+ QObject::tr("The program might fail to run."),
+ QMessageBox::No})
+ .button({
+ QObject::tr("Cancel"),
+ QMessageBox::Cancel})
+ .remember("steamQuery", sp.binary.fileName())
+ .exec();
}
QuestionBoxMemory::Button confirmRestartAsAdminForSteam(QWidget* parent, const SpawnParameters& sp)
@@ -561,27 +584,14 @@ bool startSteam(QWidget* parent)
return true;
}
-bool gameRequiresSteam(const QDir& gameDirectory, const Settings& settings)
-{
- static const std::vector files = {
- "steam_api.dll", "steam_api64.dll"
- };
-
- for (const auto& file : files) {
- const QFileInfo fi(gameDirectory.absoluteFilePath(file));
- if (fi.exists()) {
- log::debug("found '{}'", fi.absoluteFilePath());
- return true;
- }
- }
-
- return false;
-}
-
bool checkSteam(
QWidget* parent, const SpawnParameters& sp,
const QDir& gameDirectory, const QString &steamAppID, const Settings& settings)
{
+ static const std::vector steamFiles = {
+ "steam_api.dll", "steam_api64.dll"
+ };
+
log::debug("checking steam");
if (!steamAppID.isEmpty()) {
@@ -590,16 +600,37 @@ bool checkSteam(
env::set("SteamAPPId", settings.steam().appID());
}
- if (!gameRequiresSteam(gameDirectory, settings)) {
- log::debug("games doesn't seem to require steam");
+
+ bool steamRequired = false;
+ QString details;
+
+ for (const auto& file : steamFiles) {
+ const QFileInfo fi(gameDirectory.absoluteFilePath(file));
+ if (fi.exists()) {
+ details = QString(
+ "managed game is located at '%1' and file '%2' exists")
+ .arg(gameDirectory.absolutePath())
+ .arg(fi.absoluteFilePath());
+
+ log::debug("{}", details);
+ steamRequired = true;
+
+ break;
+ }
+ }
+
+ if (!steamRequired) {
+ log::debug("program doesn't seem to require steam");
return true;
}
+
auto ss = getSteamStatus();
if (!ss.running) {
log::debug("steam isn't running, asking to start steam");
- const auto c = dialogs::confirmStartSteam(parent, sp);
+
+ const auto c = dialogs::confirmStartSteam(parent, sp, details);
if (c == QDialogButtonBox::Yes) {
log::debug("user wants to start steam");
--
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.cpp')
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 cbfd3692ce95f43daa081c5f16a5c9160cb7c459 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 16:41:36 -0400
Subject: TaskDialog for event log not running
---
src/spawn.cpp | 46 +++++++++++++++++++++++++++++++---------------
1 file changed, 31 insertions(+), 15 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/spawn.cpp b/src/spawn.cpp
index a0cf9fb6..b7aa90c0 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -338,12 +338,11 @@ QMessageBox::StandardButton confirmRestartAsAdminForSteam(
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})
+ 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."),
@@ -358,16 +357,29 @@ QMessageBox::StandardButton confirmRestartAsAdminForSteam(
bool eventLogNotRunning(
QWidget* parent, const env::Service& s, const SpawnParameters& sp)
{
- const auto r = 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(sp.binary.fileName()),
- QDialogButtonBox::Yes | QDialogButtonBox::No);
+ const auto title = QObject::tr("Event Log not running");
+ const auto mainText = QObject::tr("The Event Log service is not running");
+ const auto content = QObject::tr(
+ "The Windows Event Log service is not running. This can prevent USVFS from "
+ "running properly and your mods may not be recognized by the program being "
+ "launched.");
- return (r != QDialogButtonBox::No);
+ const auto r = MOBase::TaskDialog(parent, title)
+ .main(mainText)
+ .content(content)
+ .details(s.toString())
+ .icon(QMessageBox::Question)
+ .remember("eventLogService", sp.binary.fileName())
+ .button({
+ QObject::tr("Continue"),
+ QObject::tr("Your mods might not work."),
+ QMessageBox::Yes})
+ .button({
+ QObject::tr("Cancel"),
+ QMessageBox::Cancel})
+ .exec();
+
+ return (r == QDialogButtonBox::Yes);
}
bool confirmBlacklisted(QWidget* parent, const SpawnParameters& sp)
@@ -681,11 +693,15 @@ 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.
+ const auto serviceName = "EventLog";
- const auto s = env::getService("EventLog");
+ const auto s = env::getService(serviceName);
if (!s.isValid()) {
- log::error("cannot determine the status of the EventLog, continuing");
+ log::error(
+ "cannot determine the status of the {} service, continuing",
+ serviceName);
+
return true;
}
--
cgit v1.3.1
From 94b0c4634290b41398915c6635982dc7b3928f60 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Sep 2019 17:10:30 -0400
Subject: TaskDialog for blacklisted, with button to change the blacklist
---
src/settingsdialogworkarounds.cpp | 51 +++++++++++++++++++++++--------
src/settingsdialogworkarounds.h | 15 ++++++++--
src/spawn.cpp | 63 +++++++++++++++++++++++++++++----------
3 files changed, 98 insertions(+), 31 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp
index 5e70e5a6..0e31fc4b 100644
--- a/src/settingsdialogworkarounds.cpp
+++ b/src/settingsdialogworkarounds.cpp
@@ -1,6 +1,7 @@
#include "settingsdialogworkarounds.h"
#include "ui_settingsdialog.h"
#include "spawn.h"
+#include "settings.h"
#include
WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d)
@@ -26,7 +27,7 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d)
ui->lockGUIBox->setChecked(settings().interface().lockGUI());
ui->enableArchiveParsingBox->setChecked(settings().archiveParsing());
- setExecutableBlacklist(settings().executablesBlacklist());
+ m_ExecutableBlacklist = settings().executablesBlacklist();
QObject::connect(ui->bsaDateBtn, &QPushButton::clicked, [&]{ on_bsaDateBtn_clicked(); });
QObject::connect(ui->execBlacklistBtn, &QPushButton::clicked, [&]{ on_execBlacklistBtn_clicked(); });
@@ -49,14 +50,29 @@ void WorkaroundsSettingsTab::update()
settings().interface().setDisplayForeign(ui->displayForeignBox->isChecked());
settings().interface().setLockGUI(ui->lockGUIBox->isChecked());
settings().setArchiveParsing(ui->enableArchiveParsingBox->isChecked());
- settings().setExecutablesBlacklist(getExecutableBlacklist());
+ settings().setExecutablesBlacklist(m_ExecutableBlacklist);
}
-void WorkaroundsSettingsTab::on_execBlacklistBtn_clicked()
+bool WorkaroundsSettingsTab::changeBlacklistNow(
+ QWidget* parent, Settings& settings)
+{
+ const auto current = settings.executablesBlacklist();
+
+ if (auto s=changeBlacklistLater(parent, current)) {
+ settings.setExecutablesBlacklist(*s);
+ return true;
+ }
+
+ return false;
+}
+
+std::optional WorkaroundsSettingsTab::changeBlacklistLater(
+ QWidget* parent, const QString& current)
{
bool ok = false;
+
QString result = QInputDialog::getMultiLineText(
- &dialog(),
+ parent,
QObject::tr("Executables Blacklist"),
QObject::tr("Enter one executable per line to be blacklisted from the virtual file system.\n"
"Mods and other virtualized files will not be visible to these executables and\n"
@@ -64,17 +80,28 @@ void WorkaroundsSettingsTab::on_execBlacklistBtn_clicked()
"Example:\n"
" Chrome.exe\n"
" Firefox.exe"),
- m_ExecutableBlacklist.split(";").join("\n"),
+ current.split(";").join("\n"),
&ok
);
- if (ok) {
- QStringList blacklist;
- for (auto exec : result.split("\n")) {
- if (exec.trimmed().endsWith(".exe", Qt::CaseInsensitive)) {
- blacklist << exec.trimmed();
- }
+
+ if (!ok) {
+ return {};
+ }
+
+ QStringList blacklist;
+ for (auto exec : result.split("\n")) {
+ if (exec.trimmed().endsWith(".exe", Qt::CaseInsensitive)) {
+ blacklist << exec.trimmed();
}
- m_ExecutableBlacklist = blacklist.join(";");
+ }
+
+ return blacklist.join(";");
+}
+
+void WorkaroundsSettingsTab::on_execBlacklistBtn_clicked()
+{
+ if (auto s=changeBlacklistLater(parentWidget(), m_ExecutableBlacklist)) {
+ m_ExecutableBlacklist = *s;
}
}
diff --git a/src/settingsdialogworkarounds.h b/src/settingsdialogworkarounds.h
index d5d6815f..cffc54a0 100644
--- a/src/settingsdialogworkarounds.h
+++ b/src/settingsdialogworkarounds.h
@@ -8,6 +8,18 @@ class WorkaroundsSettingsTab : public SettingsTab
{
public:
WorkaroundsSettingsTab(Settings& settings, SettingsDialog& dialog);
+
+ // shows the blacklist dialog from the given settings, and changes the
+ // settings when the user accepts it
+ //
+ static bool changeBlacklistNow(QWidget* parent, Settings& settings);
+
+ // shows the blacklist dialog from the given string and returns the new
+ // blacklist if the user accepted it
+ //
+ static std::optional changeBlacklistLater(
+ QWidget* parent, const QString& current);
+
void update();
private:
@@ -16,9 +28,6 @@ private:
void on_bsaDateBtn_clicked();
void on_execBlacklistBtn_clicked();
void on_resetGeometryBtn_clicked();
-
- QString getExecutableBlacklist() { return m_ExecutableBlacklist; }
- void setExecutableBlacklist(QString blacklist) { m_ExecutableBlacklist = blacklist; }
};
#endif // SETTINGSDIALOGWORKAROUNDS_H
diff --git a/src/spawn.cpp b/src/spawn.cpp
index b7aa90c0..ab9d90a0 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -26,6 +26,7 @@ along with Mod Organizer. If not, see .
#include "envsecurity.h"
#include "envmodule.h"
#include "settings.h"
+#include "settingsdialogworkarounds.h"
#include
#include
#include
@@ -379,21 +380,45 @@ bool eventLogNotRunning(
QMessageBox::Cancel})
.exec();
- return (r == QDialogButtonBox::Yes);
+ return (r == QMessageBox::Yes);
}
-bool confirmBlacklisted(QWidget* parent, const SpawnParameters& sp)
+QMessageBox::StandardButton confirmBlacklisted(
+ QWidget* parent, const SpawnParameters& sp)
{
- const auto r = 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(sp.binary.fileName()),
- QDialogButtonBox::Yes | QDialogButtonBox::No);
-
- return (r != QDialogButtonBox::No);
+ const auto title = QObject::tr("Blacklisted program");
+ const auto mainText = QObject::tr("The program %1 is blacklisted")
+ .arg(sp.binary.fileName());
+ const auto content = QObject::tr(
+ "The program you are attempting to launch is blacklisted in the virtual "
+ "filesystem. This will likely prevent it from seeing any mods, INI files "
+ "or any other virtualized files.");
+
+ auto r = MOBase::TaskDialog(parent, title)
+ .main(mainText)
+ .content(content)
+ .details("")
+ .icon(QMessageBox::Question)
+ .remember("blacklistedExecutable", sp.binary.fileName())
+ .button({
+ QObject::tr("Continue"),
+ QObject::tr("Your mods might not work"),
+ QMessageBox::Yes})
+ .button({
+ QObject::tr("Change the blacklist"),
+ QMessageBox::Retry})
+ .button({
+ QObject::tr("Cancel"),
+ QMessageBox::Cancel})
+ .exec();
+
+ if (r == QMessageBox::Retry) {
+ if (!WorkaroundsSettingsTab::changeBlacklistNow(parent, Settings::instance())) {
+ r = QMessageBox::Cancel;
+ }
+ }
+
+ return r;
}
} // namespace
@@ -716,11 +741,17 @@ bool checkEnvironment(QWidget* parent, const SpawnParameters& sp)
bool checkBlacklist(QWidget* parent, const SpawnParameters& sp, const Settings& settings)
{
- if (settings.isExecutableBlacklisted(sp.binary.fileName())) {
- return dialogs::confirmBlacklisted(parent, sp);
- }
+ for (;;) {
+ if (!settings.isExecutableBlacklisted(sp.binary.fileName())) {
+ return true;
+ }
- return true;
+ const auto r = dialogs::confirmBlacklisted(parent, sp);
+
+ if (r != QMessageBox::Retry) {
+ return (r == QMessageBox::Yes);
+ }
+ }
}
--
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.cpp')
diff --git a/src/spawn.cpp b/src/spawn.cpp
index ab9d90a0..d2c8ddd5 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -384,7 +384,7 @@ bool eventLogNotRunning(
}
QMessageBox::StandardButton confirmBlacklisted(
- QWidget* parent, const SpawnParameters& sp)
+ QWidget* parent, const SpawnParameters& sp, Settings& settings)
{
const auto title = QObject::tr("Blacklisted program");
const auto mainText = QObject::tr("The program %1 is blacklisted")
@@ -394,10 +394,14 @@ QMessageBox::StandardButton confirmBlacklisted(
"filesystem. This will likely prevent it from seeing any mods, INI files "
"or any other virtualized files.");
+ const auto details =
+ "Executable: " + sp.binary.fileName() + "\n"
+ "Current blacklist: " + settings.executablesBlacklist();
+
auto r = MOBase::TaskDialog(parent, title)
.main(mainText)
.content(content)
- .details("")
+ .details(details)
.icon(QMessageBox::Question)
.remember("blacklistedExecutable", sp.binary.fileName())
.button({
@@ -413,7 +417,7 @@ QMessageBox::StandardButton confirmBlacklisted(
.exec();
if (r == QMessageBox::Retry) {
- if (!WorkaroundsSettingsTab::changeBlacklistNow(parent, Settings::instance())) {
+ if (!WorkaroundsSettingsTab::changeBlacklistNow(parent, settings)) {
r = QMessageBox::Cancel;
}
}
@@ -739,14 +743,15 @@ bool checkEnvironment(QWidget* parent, const SpawnParameters& sp)
return dialogs::eventLogNotRunning(parent, s, sp);
}
-bool checkBlacklist(QWidget* parent, const SpawnParameters& sp, const Settings& settings)
+bool checkBlacklist(
+ QWidget* parent, const SpawnParameters& sp, Settings& settings)
{
for (;;) {
if (!settings.isExecutableBlacklisted(sp.binary.fileName())) {
return true;
}
- const auto r = dialogs::confirmBlacklisted(parent, sp);
+ const auto r = dialogs::confirmBlacklisted(parent, sp, settings);
if (r != QMessageBox::Retry) {
return (r == QMessageBox::Yes);
diff --git a/src/spawn.h b/src/spawn.h
index 9e1e2539..31b44739 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -61,7 +61,7 @@ bool checkSteam(
bool checkEnvironment(QWidget* parent, const SpawnParameters& sp);
bool checkBlacklist(
- QWidget* parent, const SpawnParameters& sp, const Settings& settings);
+ QWidget* parent, const SpawnParameters& sp, Settings& settings);
/**
* @brief spawn a binary with Mod Organizer injected
--
cgit v1.3.1
From b45565911487e91c11f641f5cca685c231f91faf Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Fri, 20 Sep 2019 00:45:22 -0400
Subject: registry details when steam fails to start typos
---
src/spawn.cpp | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
(limited to 'src/spawn.cpp')
diff --git a/src/spawn.cpp b/src/spawn.cpp
index d2c8ddd5..1b46c479 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -196,9 +196,17 @@ QMessageBox::StandardButton badSteamReg(
}
QMessageBox::StandardButton startSteamFailed(
- QWidget* parent, const SpawnParameters& sp, DWORD e)
+ QWidget* parent,
+ const QString& keyName, const QString& valueName, const QString& exe,
+ const SpawnParameters& sp, DWORD e)
{
- const auto details = makeDetails(sp, e);
+ auto details = QString(
+ "a steam install was found in the registry at '%1': '%2'\n\n")
+ .arg(keyName + "\\" + valueName)
+ .arg(exe);
+
+ details += makeDetails(sp, e);
+
log::error("{}", details);
return MOBase::TaskDialog(parent, QObject::tr("Cannot start Steam"))
@@ -332,7 +340,8 @@ QMessageBox::StandardButton confirmRestartAsAdminForSteam(
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"
+ "problems when Mod Organizer itself is not running as administrator."
+ "\r\n\r\n"
"You can restart Mod Organizer as administrator and try launching the "
"program again.");
@@ -618,7 +627,10 @@ bool startSteam(QWidget* parent)
(username.isEmpty() ? "" : "USERNAME"),
(password.isEmpty() ? "" : "PASSWORD"));
- return (dialogs::startSteamFailed(parent, sp, e) == QMessageBox::Yes);
+ const auto r = dialogs::startSteamFailed(
+ parent, keyName, valueName, exe, sp, e);
+
+ return (r == QMessageBox::Yes);
}
QMessageBox::information(
--
cgit v1.3.1
From 2feaed68b4bd2191aeaf02c33990a0bdf7f8738c Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Fri, 20 Sep 2019 01:19:01 -0400
Subject: missing period
---
src/spawn.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'src/spawn.cpp')
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 1b46c479..079677f4 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -415,7 +415,7 @@ QMessageBox::StandardButton confirmBlacklisted(
.remember("blacklistedExecutable", sp.binary.fileName())
.button({
QObject::tr("Continue"),
- QObject::tr("Your mods might not work"),
+ QObject::tr("Your mods might not work."),
QMessageBox::Yes})
.button({
QObject::tr("Change the blacklist"),
--
cgit v1.3.1