From 9ec158dceea37c9d783a8439d3c1253186efbbda Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Tue, 19 Feb 2019 03:24:52 -0600 Subject: Fix for Steam being run as administrator Previously, when Steam was run as administrator, MO couldn't access the process therefore couldn't detect it. Now, an alternative method for listing processes is now being used in order to determine if Steam is running. Additionally, MO will detect if Steam is admin and MO is not and prompt the user if they want to restart MO as admin. --- src/organizercore.cpp | 153 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 104 insertions(+), 49 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index bfe36b24..2f75439d 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -34,6 +34,7 @@ #include "lockeddialog.h" #include "instancemanager.h" #include +#include "helper.h" #include #include @@ -438,58 +439,65 @@ void OrganizerCore::storeSettings() } } -bool OrganizerCore::testForSteam() +bool OrganizerCore::testForSteam(bool *found, bool *access) { - size_t currentSize = 1024; - std::unique_ptr processIDs; - DWORD bytesReturned; - bool success = false; - while (!success) { - processIDs.reset(new DWORD[currentSize]); - if (!::EnumProcesses(processIDs.get(), - static_cast(currentSize) * sizeof(DWORD), - &bytesReturned)) { - qWarning("failed to determine if steam is running"); - return true; - } - if (bytesReturned == (currentSize * sizeof(DWORD))) { - // maximum size used, list probably truncated - currentSize *= 2; - } else { - success = true; - } - } - TCHAR processName[MAX_PATH]; - for (unsigned int i = 0; i < bytesReturned / sizeof(DWORD); ++i) { - memset(processName, '\0', sizeof(TCHAR) * MAX_PATH); - if (processIDs[i] != 0) { - HANDLE process = ::OpenProcess( - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]); - - if (process != nullptr) { - - ON_BLOCK_EXIT([&]() { - if (process != INVALID_HANDLE_VALUE) - ::CloseHandle(process); - }); - - HMODULE module; - DWORD ignore; - - // first module in a process is always the binary - if (EnumProcessModules(process, &module, sizeof(HMODULE) * 1, - &ignore)) { - ::GetModuleBaseName(process, module, processName, MAX_PATH); - if ((_tcsicmp(processName, TEXT("steam.exe")) == 0) - || (_tcsicmp(processName, TEXT("steamservice.exe")) == 0)) { - return true; - } + 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(); + qCritical("unable to get snapshot of processes (error %d)", lastError); + return false; + } + + // Retrieve information about the first process, + // and exit if unsuccessful + pe32.dwSize = sizeof(PROCESSENTRY32); + if (!Process32First(hProcessSnap, &pe32)) { + lastError = GetLastError(); + qCritical("unable to get first process (error %d)", 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; } - } - return false; +} while(Process32Next(hProcessSnap, &pe32)); + +CloseHandle(hProcessSnap); +return true; + } void OrganizerCore::updateExecutablesList(QSettings &settings) @@ -1316,7 +1324,14 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, "steam_api64.dll")) .exists()) && (m_Settings.getLoadMechanism() == LoadMechanism::LOAD_MODORGANIZER)) { - if (!testForSteam()) { + + bool steamFound = true; + bool steamAccess = true; + if (!testForSteam(&steamFound, &steamAccess)) { + qCritical("unable to determine state of Steam"); + } + + if (!steamFound) { QDialogButtonBox::StandardButton result; result = QuestionBoxMemory::query(window, "steamQuery", binary.fileName(), tr("Start Steam?"), @@ -1325,7 +1340,47 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); if (result == QDialogButtonBox::Yes) { startSteam(window); - } else if(result == QDialogButtonBox::Cancel) { + + // double-check that Steam is started and MO has access + steamFound = true; + steamAccess = true; + if (!testForSteam(&steamFound, &steamAccess)) { + qCritical("unable to determine state of Steam"); + } else if (!steamFound) { + qCritical("could not find Steam"); + } + + } else if (result == QDialogButtonBox::Cancel) { + 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 neccessary.\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)) { + qCritical("unable to get current directory (error %d)", GetLastError()); + cwd[0] = L'\0'; + } + if (!Helper::adminLaunch( + qApp->applicationDirPath().toStdWString(), + qApp->applicationFilePath().toStdWString(), + std::wstring(cwd))) { + qCritical("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; } } -- cgit v1.3.1