summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/envmodule.cpp6
-rw-r--r--src/envmodule.h2
-rw-r--r--src/organizercore.cpp220
-rw-r--r--src/spawn.cpp53
-rw-r--r--src/spawn.h4
-rw-r--r--src/usvfsconnector.cpp47
-rw-r--r--src/usvfsconnector.h2
7 files changed, 161 insertions, 173 deletions
diff --git a/src/envmodule.cpp b/src/envmodule.cpp
index abbe02e5..160a54fa 100644
--- a/src/envmodule.cpp
+++ b/src/envmodule.cpp
@@ -511,4 +511,10 @@ DWORD getProcessParentID(DWORD pid)
return ppid;
}
+
+DWORD getProcessParentID(HANDLE handle)
+{
+ return getProcessParentID(GetProcessId(handle));
+}
+
} // namespace
diff --git a/src/envmodule.h b/src/envmodule.h
index 212f6f7b..6c0a028d 100644
--- a/src/envmodule.h
+++ b/src/envmodule.h
@@ -121,7 +121,9 @@ std::vector<Process> getRunningProcesses();
std::vector<Module> getLoadedModules();
QString getProcessName(HANDLE process);
+
DWORD getProcessParentID(DWORD pid);
+DWORD getProcessParentID(HANDLE handle);
} // namespace env
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 2a228fda..2a97b998 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -1355,17 +1355,13 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
if (!Settings::instance().interface().lockGUI())
return true;
- ILockedWaitingForProcess* uilock = nullptr;
- if (m_MainWindow != nullptr) {
- uilock = m_MainWindow->lock();
- }
+ bool r = false;
- ON_BLOCK_EXIT([&] () {
- if (m_MainWindow != nullptr) {
- m_MainWindow->unlock();
- } });
+ withLock([&](auto* uilock) {
+ r = waitForProcessCompletion(handle, exitCode, uilock);
+ });
- return waitForProcessCompletion(handle, exitCode, uilock);
+ return r;
}
bool OrganizerCore::waitForProcessCompletion(
@@ -1387,6 +1383,9 @@ bool OrganizerCore::waitForProcessCompletion(
bool OrganizerCore::waitForAllUSVFSProcessesWithLock()
{
+ if (!Settings::instance().interface().lockGUI())
+ return true;
+
bool r = false;
withLock([&](auto* uilock) {
@@ -1396,178 +1395,81 @@ bool OrganizerCore::waitForAllUSVFSProcessesWithLock()
return r;
}
-bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
+HANDLE getInterestingProcess(
+ const std::vector<HANDLE>& handles,
+ const std::vector<QString>& hidden, DWORD preferedParentPid)
{
- // Certain process names we wish to "hide" for aesthetic reason:
- std::vector<QString> hiddenList;
- hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
-
- bool originalHandle = true;
- bool newHandle = true;
- bool uiunlocked = false;
-
- HANDLE handle = findAndOpenAUSVFSProcess(hiddenList, GetCurrentProcessId());
- DWORD* exitCode = nullptr;
- DWORD currentPID = 0;
- QString processName;
-
- auto waitForChildUntil = GetTickCount64();
- if (handle != INVALID_HANDLE_VALUE) {
- currentPID = GetProcessId(handle);
- processName = env::getProcessName(handle);
- }
-
- bool waitingOnHidden = false;
- for (QString hide : hiddenList)
- if (processName.contains(hide, Qt::CaseInsensitive))
- waitingOnHidden = true;
+ HANDLE best_match = INVALID_HANDLE_VALUE;
+ bool best_match_hidden = true;
- // The main reason for adding the hidden list is to hide the MO proxy we use to spawn virtualized processes.
- // On the one hand we want to display the real executable without it feeling laggy, on the other we don't want
- // to requery processes all the time if for some reason we are waiting on hidden processes and find no "unhidden"
- // process. For this reason we use exponential backoff and also start with a delibrately low value to improve
- // the responsiveness of the initial update
- DWORD64 nextHiddenCheck = GetTickCount64();
- DWORD64 nextHiddenCheckDelay = 50;
+ for (auto handle : handles) {
+ const QString pname = env::getProcessName(handle);
- constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1;
- DWORD res = WAIT_TIMEOUT;
- while (handle != INVALID_HANDLE_VALUE && (newHandle || res == WAIT_TIMEOUT || res == INPUT_EVENT))
- {
- if (newHandle) {
- processName += QString(" (%1)").arg(currentPID);
- if (uilock)
- uilock->setProcessName(processName);
+ bool phidden = false;
+ for (auto h : hidden)
+ if (pname.contains(h, Qt::CaseInsensitive))
+ phidden = true;
- log::debug(
- "Waiting for {} process completion: {}",
- (originalHandle ? "spawned" : "usvfs"), processName);
+ bool pprefered = preferedParentPid && env::getProcessParentID(handle) == preferedParentPid;
- newHandle = false;
+ if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) {
+ best_match = handle;
+ best_match_hidden = phidden;
}
- // Wait for a an event on the handle, a key press, mouse click or timeout
- res = MsgWaitForMultipleObjects(1, &handle, FALSE, 200, QS_KEY | QS_MOUSEBUTTON);
- if (res == WAIT_FAILED) {
- log::warn("Failed waiting for process completion : MsgWaitForMultipleObjects WAIT_FAILED {}", GetLastError());
- break;
- }
+ if (!phidden && pprefered)
+ return best_match;
+ }
- // keep processing events so the app doesn't appear dead
- QCoreApplication::sendPostedEvents();
- QCoreApplication::processEvents();
+ return best_match;
+}
- if (uilock && uilock->unlockForced()) {
- uiunlocked = true;
+bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
+{
+ // Certain process names we wish to "hide" for aesthetic reason:
+ std::vector<QString> hiddenList;
+ hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
+
+ for (;;) {
+ const auto handles = getRunningUSVFSProcesses();
+ if (handles.empty()) {
break;
}
- if (res == WAIT_OBJECT_0) {
- // process we were waiting on has completed
- if (originalHandle && exitCode && !::GetExitCodeProcess(handle, exitCode))
- log::warn("Failed getting exit code of complete process: {}", GetLastError());
- CloseHandle(handle);
- handle = INVALID_HANDLE_VALUE;
- originalHandle = false;
- // if the previous process spawned a child process and immediately exits we may miss it if we check immediately
- waitForChildUntil = GetTickCount64() + 800;
- }
+ const auto interesting = getInterestingProcess(
+ handles, hiddenList, GetCurrentProcessId());
- // search for another process to wait on if either:
- // 1. we just completed waiting for a process and need to find/wait for an inject child
- // 2. we are currently waiting on a hidden process so periodically check if there is a non-hidden process to wait on
- bool firstIteration = true;
- while ((handle == INVALID_HANDLE_VALUE && GetTickCount64() <= waitForChildUntil)
- || (waitingOnHidden && GetTickCount64() >= nextHiddenCheck))
- {
- if (firstIteration)
- firstIteration = false;
- else {
- QThread::msleep(200);
- QCoreApplication::sendPostedEvents();
- QCoreApplication::processEvents();
- }
+ if (uilock) {
+ const DWORD pid = ::GetProcessId(interesting);
+ const QString processName = QString("%1 (%2)")
+ .arg(env::getProcessName(interesting))
+ .arg(pid);
- // search if there is another usvfs process active
- handle = findAndOpenAUSVFSProcess(hiddenList, currentPID);
- waitingOnHidden = false;
- newHandle = handle != INVALID_HANDLE_VALUE;
- if (newHandle) {
- currentPID = GetProcessId(handle);
- processName = env::getProcessName(handle);
- for (QString hide : hiddenList)
- if (processName.contains(hide, Qt::CaseInsensitive))
- waitingOnHidden = true;
- }
- if (waitingOnHidden) {
- nextHiddenCheck = GetTickCount64() + nextHiddenCheckDelay;
- nextHiddenCheckDelay = std::min(nextHiddenCheckDelay * 2, (DWORD64) 2000);
- }
- else {
- nextHiddenCheck = GetTickCount64();
- nextHiddenCheckDelay = 200;
- }
+ uilock->setProcessName(processName);
}
- }
- if (res == WAIT_OBJECT_0)
- log::debug("Waiting for process completion successfull");
- else if (uiunlocked)
- log::debug("Waiting for process completion aborted by UI");
- else
- log::debug("Waiting for process completion not successfull: {}", res);
+ const auto r = spawn::waitForProcess(interesting, nullptr, uilock);
- if (handle != INVALID_HANDLE_VALUE)
- ::CloseHandle(handle);
-
- return res == WAIT_OBJECT_0;
-}
-
-HANDLE OrganizerCore::findAndOpenAUSVFSProcess(const std::vector<QString>& hiddenList, DWORD preferedParentPid) {
- // for practical reasons a querySize of 1 is probably enough, we use a larger query as a heuristics
- // to find a more "aesthetic injected processes (attempting to comply to hiddenList and preferedParentPid)
- constexpr size_t querySize = 100;
- DWORD pids[querySize];
- size_t found = querySize;
- if (!::GetVFSProcessList(&found, pids)) {
- log::warn("Failed seeking USVFS processes : GetVFSProcessList failed?!");
- return INVALID_HANDLE_VALUE;
- }
-
- HANDLE best_match = INVALID_HANDLE_VALUE;
- bool best_match_hidden = true;
- for (size_t i = 0; i < found; ++i) {
- if (pids[i] == GetCurrentProcessId())
- continue; // obviously don't wait for MO process
-
- HANDLE handle = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pids[i]);
- if (handle == INVALID_HANDLE_VALUE) {
- log::warn("Failed opening USVFS process {}: OpenProcess failed {}", pids[i], GetLastError());
- continue;
- }
-
- QString pname = env::getProcessName(handle);
- bool phidden = false;
- for (auto hide : hiddenList)
- if (pname.contains(hide, Qt::CaseInsensitive))
- phidden = true;
+ switch (r)
+ {
+ case spawn::WaitResults::Completed:
+ // this process is completed, check for others
+ break;
- bool pprefered = preferedParentPid && env::getProcessParentID(pids[i]) == preferedParentPid;
+ case spawn::WaitResults::Unlocked:
+ // force unlocked
+ log::debug("waiting for process completion aborted by UI");
+ return true;
- if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) {
- if (best_match != INVALID_HANDLE_VALUE)
- CloseHandle(best_match);
- best_match = handle;
- best_match_hidden = phidden;
+ case spawn::WaitResults::Error: // fall-through
+ default:
+ log::debug("waiting for process completion not successful");
+ return false;
}
- else
- CloseHandle(handle);
-
- if (!phidden && pprefered)
- return best_match;
}
- return best_match;
+ log::debug("Waiting for process completion successful");
+ return true;
}
bool OrganizerCore::onAboutToRun(
diff --git a/src/spawn.cpp b/src/spawn.cpp
index f0b3b2c7..1003024f 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -1094,7 +1094,8 @@ FileExecutionContext getFileExecutionContext(
return {{}, {}, FileExecutionTypes::Other};
}
-WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
+WaitResults waitForProcess(
+ HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
{
if (handle == INVALID_HANDLE_VALUE) {
return WaitResults::Error;
@@ -1108,37 +1109,62 @@ WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProc
if (uilock)
uilock->setProcessName(processName);
- constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1;
- DWORD res = WAIT_TIMEOUT;
-
log::debug(
"waiting for process completion '{}' ({})",
processName, pid);
+ std::vector<HANDLE> handles;
+ handles.push_back(handle);
+
+ std::vector<DWORD> exitCodes;
+
+ const auto r = waitForProcesses(handles, exitCodes, uilock);
+ if (exitCode && !exitCodes.empty()) {
+ *exitCode = exitCodes[0];
+ }
+
+ return r;
+}
+
+WaitResults waitForProcesses(
+ const std::vector<HANDLE>& handles, std::vector<DWORD>& exitCodes,
+ ILockedWaitingForProcess* uilock)
+{
+ if (handles.empty()) {
+ return WaitResults::Completed;
+ }
+
+ const auto WAIT_OBJECT_N = static_cast<DWORD>(WAIT_OBJECT_0 + handles.size());
+
for (;;) {
// Wait for a an event on the handle, a key press, mouse click or timeout
const auto res = MsgWaitForMultipleObjects(
- 1, &handle, FALSE, 50, QS_KEY | QS_MOUSEBUTTON);
+ static_cast<DWORD>(handles.size()), &handles[0],
+ TRUE, 50, QS_KEY | QS_MOUSEBUTTON);
if (res == WAIT_FAILED) {
// error
const auto e = ::GetLastError();
log::error(
- "failed waiting for process completion '{}' ({}), {}",
- processName, pid, formatSystemMessage(e));
+ "failed waiting for process completion, {}", formatSystemMessage(e));
return WaitResults::Error;
- } else if (res == WAIT_OBJECT_0) {
+ } else if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_N) {
// completed
- log::debug("process '{}' ({}) completed", processName, pid);
+ exitCodes.resize(handles.size());
+ std::fill(exitCodes.begin(), exitCodes.end(), 0);
+
+ for (std::size_t i=0; i<handles.size(); ++i) {
+ DWORD exitCode = 0;
- if (exitCode) {
- if (!::GetExitCodeProcess(handle, exitCode)) {
+ if (::GetExitCodeProcess(handles[i], &exitCode)) {
+ exitCodes[i] = exitCode;
+ } else {
const auto e = ::GetLastError();
log::warn(
- "failed to get exit code of process '{}' ({}): {}",
- processName, pid, formatSystemMessage(e));
+ "failed to get exit code of process, {}",
+ formatSystemMessage(e));
}
}
@@ -1150,7 +1176,6 @@ WaitResults waitForProcess(HANDLE handle, DWORD* exitCode, ILockedWaitingForProc
QCoreApplication::processEvents();
if (uilock && uilock->unlockForced()) {
- log::debug("waiting for process '{}' ({}) aborted by UI", processName, pid);
return WaitResults::Unlocked;
}
}
diff --git a/src/spawn.h b/src/spawn.h
index 441cad2c..6b947f2f 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -136,6 +136,10 @@ enum class WaitResults
WaitResults waitForProcess(
HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock);
+WaitResults waitForProcesses(
+ const std::vector<HANDLE>& handles, std::vector<DWORD>& exitCodes,
+ ILockedWaitingForProcess* uilock);
+
} // namespace
diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp
index 311c6dd3..3dba3efc 100644
--- a/src/usvfsconnector.cpp
+++ b/src/usvfsconnector.cpp
@@ -20,6 +20,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "usvfsconnector.h"
#include "settings.h"
#include "organizercore.h"
+#include "envmodule.h"
#include "shared/util.h"
#include <memory>
#include <sstream>
@@ -256,3 +257,49 @@ void UsvfsConnector::updateForcedLibraries(const QList<MOBase::ExecutableForcedL
}
}
}
+
+
+std::vector<HANDLE> getRunningUSVFSProcesses()
+{
+ std::vector<DWORD> pids;
+
+ {
+ size_t count = 0;
+ DWORD* buffer = nullptr;
+ if (!::GetVFSProcessList2(&count, &buffer)) {
+ log::error("failed to get usvfs process list");
+ return {};
+ }
+
+ if (buffer) {
+ pids.assign(buffer, buffer + count);
+ std::free(buffer);
+ }
+ }
+
+ const auto thisPid = GetCurrentProcessId();
+ std::vector<HANDLE> v;
+
+ for (auto&& pid : pids) {
+ if (pid == thisPid) {
+ continue; // obviously don't wait for MO process
+ }
+
+ HANDLE handle = ::OpenProcess(
+ PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pid);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ const auto e = GetLastError();
+
+ log::warn(
+ "failed to open usvfs process {}: {}",
+ pid, formatSystemMessage(e));
+
+ continue;
+ }
+
+ v.push_back(handle);
+ }
+
+ return v;
+}
diff --git a/src/usvfsconnector.h b/src/usvfsconnector.h
index d0071678..5982778b 100644
--- a/src/usvfsconnector.h
+++ b/src/usvfsconnector.h
@@ -103,4 +103,6 @@ private:
CrashDumpsType crashDumpsType(int type);
+std::vector<HANDLE> getRunningUSVFSProcesses();
+
#endif // USVFSCONNECTOR_H