summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/env.h17
-rw-r--r--src/envmodule.cpp107
-rw-r--r--src/envmodule.h35
-rw-r--r--src/envsecurity.cpp1
-rw-r--r--src/envwindows.cpp1
-rw-r--r--src/ilockedwaitingforprocess.h1
-rw-r--r--src/lockeddialogbase.cpp7
-rw-r--r--src/lockeddialogbase.h4
-rw-r--r--src/organizercore.cpp128
-rw-r--r--src/spawn.cpp29
-rw-r--r--src/spawn.h7
11 files changed, 246 insertions, 91 deletions
diff --git a/src/env.h b/src/env.h
index 1760c7fe..f95d1013 100644
--- a/src/env.h
+++ b/src/env.h
@@ -13,23 +13,6 @@ class WindowsInfo;
class Metrics;
-// used by HandlePtr, calls CloseHandle() as the deleter
-//
-struct HandleCloser
-{
- using pointer = HANDLE;
-
- void operator()(HANDLE h)
- {
- if (h != INVALID_HANDLE_VALUE) {
- ::CloseHandle(h);
- }
- }
-};
-
-using HandlePtr = std::unique_ptr<HANDLE, HandleCloser>;
-
-
// used by DesktopDCPtr, calls ReleaseDC(0, dc) as the deleter
//
struct DesktopDCReleaser
diff --git a/src/envmodule.cpp b/src/envmodule.cpp
index 160a54fa..0e2e8ec7 100644
--- a/src/envmodule.cpp
+++ b/src/envmodule.cpp
@@ -320,19 +320,61 @@ QString Module::getMD5() const
}
-Process::Process(DWORD pid, QString name)
- : m_pid(pid), m_name(std::move(name))
+Process::Process()
+ : Process(0, 0, {})
{
}
+Process::Process(HANDLE h)
+ : Process(::GetProcessId(h), 0, {})
+{
+}
+
+Process::Process(DWORD pid, DWORD ppid, QString name)
+ : m_pid(pid), m_ppid(ppid), m_name(std::move(name))
+{
+}
+
+bool Process::isValid() const
+{
+ return (m_pid != 0);
+}
+
DWORD Process::pid() const
{
return m_pid;
}
+DWORD Process::ppid() const
+{
+ if (!m_ppid) {
+ m_ppid = getProcessParentID(m_pid);
+ }
+
+ return *m_ppid;
+}
+
const QString& Process::name() const
{
- return m_name;
+ if (!m_name) {
+ m_name = getProcessName(m_pid);
+ }
+
+ return *m_name;
+}
+
+HandlePtr Process::openHandleForWait() const
+{
+ HandlePtr h(OpenProcess(
+ PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, m_pid));
+
+ if (!h) {
+ const auto e = GetLastError();
+ log::error("can't get name of process {}, {}", m_pid, formatSystemMessage(e));
+ return {};
+ }
+
+ return h;
}
// whether this process can be accessed; fails if the current process doesn't
@@ -353,6 +395,16 @@ bool Process::canAccess() const
return true;
}
+void Process::addChild(Process p)
+{
+ m_children.push_back(p);
+}
+
+std::vector<Process>& Process::children()
+{
+ return m_children;
+}
+
std::vector<Module> getLoadedModules()
{
@@ -458,6 +510,7 @@ std::vector<Process> getRunningProcesses()
forEachRunningProcess([&](auto&& entry) {
v.push_back(Process(
entry.th32ProcessID,
+ entry.th32ParentProcessID,
QString::fromStdWString(entry.szExeFile)));
return true;
@@ -466,6 +519,54 @@ std::vector<Process> getRunningProcesses()
return v;
}
+void findChildren(Process& parent, const std::vector<Process>& processes)
+{
+ for (auto&& p : processes) {
+ if (p.ppid() == parent.pid()) {
+ Process child = p;
+ findChildren(child, processes);
+
+ parent.addChild(child);
+ }
+ }
+}
+
+Process getProcessTree(HANDLE parent)
+{
+ const auto parentPID = ::GetProcessId(parent);
+ const auto v = getRunningProcesses();
+
+ Process root;
+ for (auto&& p : v) {
+ if (p.pid() == parentPID) {
+ root = p;
+ break;
+ }
+ }
+
+ if (root.pid() == 0) {
+ log::error("process {} is not running", parentPID);
+ return {};
+ }
+
+ findChildren(root, v);
+
+ return root;
+}
+
+QString getProcessName(DWORD pid)
+{
+ HandlePtr h(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
+
+ if (!h) {
+ const auto e = GetLastError();
+ log::error("can't get name of process {}, {}", pid, formatSystemMessage(e));
+ return {};
+ }
+
+ return getProcessName(h.get());
+}
+
QString getProcessName(HANDLE process)
{
const QString badName = "unknown";
diff --git a/src/envmodule.h b/src/envmodule.h
index 6c0a028d..d152b840 100644
--- a/src/envmodule.h
+++ b/src/envmodule.h
@@ -7,6 +7,23 @@
namespace env
{
+// used by HandlePtr, calls CloseHandle() as the deleter
+//
+struct HandleCloser
+{
+ using pointer = HANDLE;
+
+ void operator()(HANDLE h)
+ {
+ if (h != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(h);
+ }
+ }
+};
+
+using HandlePtr = std::unique_ptr<HANDLE, HandleCloser>;
+
+
// represents one module
//
class Module
@@ -101,25 +118,39 @@ private:
class Process
{
public:
- Process(DWORD pid, QString name);
+ Process();
+ explicit Process(HANDLE h);
+ Process(DWORD pid, DWORD ppid, QString name);
+ bool isValid() const;
DWORD pid() const;
+ DWORD ppid() const;
const QString& name() const;
+ HandlePtr openHandleForWait() const;
+
// whether this process can be accessed; fails if the current process doesn't
// have the proper permissions
//
bool canAccess() const;
+ void addChild(Process p);
+ std::vector<Process>& children();
+
private:
DWORD m_pid;
- QString m_name;
+ mutable std::optional<DWORD> m_ppid;
+ mutable std::optional<QString> m_name;
+ std::vector<Process> m_children;
};
std::vector<Process> getRunningProcesses();
std::vector<Module> getLoadedModules();
+Process getProcessTree(HANDLE parent);
+
+QString getProcessName(DWORD pid);
QString getProcessName(HANDLE process);
DWORD getProcessParentID(DWORD pid);
diff --git a/src/envsecurity.cpp b/src/envsecurity.cpp
index 786291c6..6d62728b 100644
--- a/src/envsecurity.cpp
+++ b/src/envsecurity.cpp
@@ -1,5 +1,6 @@
#include "envsecurity.h"
#include "env.h"
+#include "envmodule.h"
#include <utility.h>
#include <log.h>
diff --git a/src/envwindows.cpp b/src/envwindows.cpp
index 3932a9b5..98e78a3e 100644
--- a/src/envwindows.cpp
+++ b/src/envwindows.cpp
@@ -1,5 +1,6 @@
#include "envwindows.h"
#include "env.h"
+#include "envmodule.h"
#include <utility.h>
#include <log.h>
diff --git a/src/ilockedwaitingforprocess.h b/src/ilockedwaitingforprocess.h
index 9475ddb9..4d1e786f 100644
--- a/src/ilockedwaitingforprocess.h
+++ b/src/ilockedwaitingforprocess.h
@@ -8,6 +8,7 @@ class ILockedWaitingForProcess
public:
virtual bool unlockForced() const = 0;
virtual void setProcessName(QString const &) = 0;
+ virtual void setProcessInformation(DWORD pid, const QString& name) = 0;
};
#endif // ILOCKEDWAITINGFORPROCESS_H
diff --git a/src/lockeddialogbase.cpp b/src/lockeddialogbase.cpp
index b18f7429..0876a511 100644
--- a/src/lockeddialogbase.cpp
+++ b/src/lockeddialogbase.cpp
@@ -18,7 +18,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lockeddialogbase.h"
-
+#include "envmodule.h"
#include <QPoint>
#include <QResizeEvent>
#include <QWidget>
@@ -63,6 +63,11 @@ bool LockedDialogBase::canceled() const {
return m_Canceled;
}
+void LockedDialogBase::setProcessInformation(DWORD pid, const QString& name)
+{
+ setProcessName(QString("%1 (%2)").arg(name).arg(pid));
+}
+
void LockedDialogBase::unlock() {
m_Unlocked = true;
}
diff --git a/src/lockeddialogbase.h b/src/lockeddialogbase.h
index 3c974a38..4ebad4c7 100644
--- a/src/lockeddialogbase.h
+++ b/src/lockeddialogbase.h
@@ -30,7 +30,7 @@ class QWidget;
/**
* a small borderless dialog displayed while the Mod Organizer UI is locked
* The dialog contains only a label and a button to force the UI to be unlocked
- *
+ *
* The UI gets locked while running external applications since they may modify the
* data on which Mod Organizer works. After the UI is unlocked (manually or after the
* external application closed) MO will refresh all of its data sources
@@ -46,6 +46,8 @@ public:
virtual bool canceled() const;
+ void setProcessInformation(DWORD pid, const QString& name) override;
+
protected:
virtual void resizeEvent(QResizeEvent *event);
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 2a97b998..eda64c1d 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -34,6 +34,7 @@
#include "instancemanager.h"
#include <scriptextender.h>
#include "previewdialog.h"
+#include "env.h"
#include "envmodule.h"
#include <QApplication>
@@ -85,6 +86,45 @@ QStringList toStringList(InputIterator current, InputIterator end)
return result;
}
+env::Process* getInterestingProcess(std::vector<env::Process>& processes)
+{
+ if (processes.empty()) {
+ return nullptr;
+ }
+
+ // Certain process names we wish to "hide" for aesthetic reason:
+ const std::vector<QString> hiddenList = {
+ QFileInfo(QCoreApplication::applicationFilePath()).fileName()
+ };
+
+ auto isHidden = [&](auto&& p) {
+ for (auto h : hiddenList) {
+ if (p.name().contains(h, Qt::CaseInsensitive)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+
+ for (auto&& root : processes) {
+ if (!isHidden(root)) {
+ return &root;
+ }
+
+ for (auto&& child : root.children()) {
+ if (!isHidden(child)) {
+ return &child;
+ }
+ }
+ }
+
+
+ // everything is hidden, just pick the first one
+ return &processes[0];
+}
+
OrganizerCore::OrganizerCore(Settings &settings)
: m_MainWindow(nullptr)
@@ -1367,12 +1407,31 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
bool OrganizerCore::waitForProcessCompletion(
HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock)
{
- const auto r = spawn::waitForProcess(handle, exitCode, uilock);
+ const auto tree = env::getProcessTree(handle);
+ std::vector<env::Process> processes = {tree};
+
+ const auto* interesting = getInterestingProcess(processes);
+ if (!interesting) {
+ return true;
+ }
+
+ if (uilock) {
+ uilock->setProcessInformation(interesting->pid(), interesting->name());
+ }
+
+ auto interestingHandle = interesting->openHandleForWait();
+ if (!interestingHandle) {
+ return true;
+ }
+
+ auto progress = [&]{ return uilock->unlockForced(); };
+ const auto r = spawn::waitForProcess(
+ interestingHandle.get(), exitCode, progress);
switch (r)
{
case spawn::WaitResults::Completed: // fall-through
- case spawn::WaitResults::Unlocked:
+ case spawn::WaitResults::Cancelled:
return true;
case spawn::WaitResults::Error: // fall-through
@@ -1395,60 +1454,39 @@ bool OrganizerCore::waitForAllUSVFSProcessesWithLock()
return r;
}
-HANDLE getInterestingProcess(
- const std::vector<HANDLE>& handles,
- const std::vector<QString>& hidden, DWORD preferedParentPid)
-{
- HANDLE best_match = INVALID_HANDLE_VALUE;
- bool best_match_hidden = true;
-
- for (auto handle : handles) {
- const QString pname = env::getProcessName(handle);
-
- bool phidden = false;
- for (auto h : hidden)
- if (pname.contains(h, Qt::CaseInsensitive))
- phidden = true;
-
- bool pprefered = preferedParentPid && env::getProcessParentID(handle) == preferedParentPid;
-
- if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) {
- best_match = handle;
- best_match_hidden = phidden;
- }
-
- if (!phidden && pprefered)
- return best_match;
- }
-
- return best_match;
-}
-
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;
}
- const auto interesting = getInterestingProcess(
- handles, hiddenList, GetCurrentProcessId());
+ std::vector<env::Process> processes;
+ for (auto&& h : handles) {
+ auto p = env::getProcessTree(h);
+ if (p.isValid()) {
+ processes.emplace_back(std::move(p));
+ }
+ }
+
+ const auto* interesting = getInterestingProcess(processes);
+ if (!interesting) {
+ break;
+ }
if (uilock) {
- const DWORD pid = ::GetProcessId(interesting);
- const QString processName = QString("%1 (%2)")
- .arg(env::getProcessName(interesting))
- .arg(pid);
+ uilock->setProcessInformation(interesting->pid(), interesting->name());
+ }
- uilock->setProcessName(processName);
+ auto interestingHandle = interesting->openHandleForWait();
+ if (!interestingHandle) {
+ break;
}
- const auto r = spawn::waitForProcess(interesting, nullptr, uilock);
+ auto progress = [&]{ return uilock->unlockForced(); };
+ const auto r = spawn::waitForProcess(
+ interestingHandle.get(), nullptr, progress);
switch (r)
{
@@ -1456,7 +1494,7 @@ bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
// this process is completed, check for others
break;
- case spawn::WaitResults::Unlocked:
+ case spawn::WaitResults::Cancelled:
// force unlocked
log::debug("waiting for process completion aborted by UI");
return true;
@@ -1468,7 +1506,7 @@ bool OrganizerCore::waitForAllUSVFSProcesses(ILockedWaitingForProcess* uilock)
}
}
- log::debug("Waiting for process completion successful");
+ log::debug("waiting for process completion successful");
return true;
}
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 1003024f..0ea60641 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -1095,32 +1095,25 @@ FileExecutionContext getFileExecutionContext(
}
WaitResults waitForProcess(
- HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock)
+ HANDLE handle, DWORD* exitCode, std::function<bool ()> progress)
{
if (handle == INVALID_HANDLE_VALUE) {
return WaitResults::Error;
}
- const DWORD pid = ::GetProcessId(handle);
- const QString processName = QString("%1 (%2)")
- .arg(env::getProcessName(handle))
- .arg(pid);
-
- if (uilock)
- uilock->setProcessName(processName);
-
- log::debug(
- "waiting for process completion '{}' ({})",
- processName, pid);
+ log::debug("waiting for completion on pid {}", ::GetProcessId(handle));
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];
+ const auto r = waitForProcesses(handles, exitCodes, progress);
+
+ if (r == WaitResults::Completed) {
+ if (exitCode && !exitCodes.empty()) {
+ *exitCode = exitCodes[0];
+ }
}
return r;
@@ -1128,7 +1121,7 @@ WaitResults waitForProcess(
WaitResults waitForProcesses(
const std::vector<HANDLE>& handles, std::vector<DWORD>& exitCodes,
- ILockedWaitingForProcess* uilock)
+ std::function<bool ()> progress)
{
if (handles.empty()) {
return WaitResults::Completed;
@@ -1175,8 +1168,8 @@ WaitResults waitForProcesses(
QCoreApplication::sendPostedEvents();
QCoreApplication::processEvents();
- if (uilock && uilock->unlockForced()) {
- return WaitResults::Unlocked;
+ if (progress && progress()) {
+ return WaitResults::Cancelled;
}
}
}
diff --git a/src/spawn.h b/src/spawn.h
index 6b947f2f..ad50bde6 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -27,7 +27,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QDir>
class Settings;
-class ILockedWaitingForProcess;
namespace MOBase { class IPluginGame; }
@@ -130,15 +129,15 @@ enum class WaitResults
{
Completed = 1,
Error,
- Unlocked
+ Cancelled
};
WaitResults waitForProcess(
- HANDLE handle, DWORD* exitCode, ILockedWaitingForProcess* uilock);
+ HANDLE handle, DWORD* exitCode, std::function<bool ()> progress);
WaitResults waitForProcesses(
const std::vector<HANDLE>& handles, std::vector<DWORD>& exitCodes,
- ILockedWaitingForProcess* uilock);
+ std::function<bool ()> progress);
} // namespace