diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mainwindow.cpp | 4 | ||||
| -rw-r--r-- | src/organizercore.cpp | 192 | ||||
| -rw-r--r-- | src/organizercore.h | 9 | ||||
| -rw-r--r-- | src/plugincontainer.cpp | 1 | ||||
| -rw-r--r-- | src/spawn.cpp | 131 | ||||
| -rw-r--r-- | src/spawn.h | 34 |
6 files changed, 230 insertions, 141 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 12ed40b3..b5b9ab0d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -411,7 +411,7 @@ MainWindow::MainWindow(Settings &settings m_Tutorial.expose("modList", m_OrganizerCore.modList()); m_Tutorial.expose("espList", m_OrganizerCore.pluginList()); - m_OrganizerCore.setUserInterface(this, this); + m_OrganizerCore.setUserInterface(this); for (const QString &fileName : m_PluginContainer.pluginFileNames()) { installTranslator(QFileInfo(fileName).baseName()); } @@ -595,7 +595,7 @@ MainWindow::~MainWindow() cleanup(); m_PluginContainer.setUserInterface(nullptr, nullptr); - m_OrganizerCore.setUserInterface(nullptr, nullptr); + m_OrganizerCore.setUserInterface(nullptr); m_IntegratedBrowser.close(); delete ui; } catch (std::exception &e) { diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 5613e8ce..3c986004 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1,5 +1,5 @@ #include "organizercore.h" - +#include "mainwindow.h" #include "delayedfilewriter.h" #include "guessedvalue.h" #include "imodinterface.h" @@ -127,7 +127,7 @@ QStringList toStringList(InputIterator current, InputIterator end) OrganizerCore::OrganizerCore(Settings &settings) - : m_UserInterface(nullptr) + : m_MainWindow(nullptr) , m_PluginContainer(nullptr) , m_GameName() , m_CurrentProfile(nullptr) @@ -249,44 +249,43 @@ void OrganizerCore::updateExecutablesList() m_PluginContainer, m_Settings.interface().displayForeign(), managedGame()); } -void OrganizerCore::setUserInterface(IUserInterface *userInterface, - QWidget *widget) +void OrganizerCore::setUserInterface(MainWindow* mainWindow) { storeSettings(); - m_UserInterface = userInterface; + m_MainWindow = mainWindow; - if (widget != nullptr) { - connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), widget, + if (m_MainWindow != nullptr) { + connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), m_MainWindow, SLOT(modlistChanged(QModelIndex, int))); - connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), widget, + connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), m_MainWindow, SLOT(modlistChanged(QModelIndexList, int))); - connect(&m_ModList, SIGNAL(showMessage(QString)), widget, + connect(&m_ModList, SIGNAL(showMessage(QString)), m_MainWindow, SLOT(showMessage(QString))); - connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), widget, + connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), m_MainWindow, SLOT(modRenamed(QString, QString))); - connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget, + connect(&m_ModList, SIGNAL(modUninstalled(QString)), m_MainWindow, SLOT(modRemoved(QString))); - connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, + connect(&m_ModList, SIGNAL(removeSelectedMods()), m_MainWindow, SLOT(removeMod_clicked())); - connect(&m_ModList, SIGNAL(clearOverwrite()), widget, + connect(&m_ModList, SIGNAL(clearOverwrite()), m_MainWindow, SLOT(clearOverwrite())); - connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, + connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), m_MainWindow, SLOT(displayColumnSelection(QPoint))); - connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, + connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), m_MainWindow, SLOT(fileMoved(QString, QString, QString))); - connect(&m_ModList, SIGNAL(modorder_changed()), widget, + connect(&m_ModList, SIGNAL(modorder_changed()), m_MainWindow, SLOT(modorder_changed())); - connect(&m_PluginList, SIGNAL(writePluginsList()), widget, + connect(&m_PluginList, SIGNAL(writePluginsList()), m_MainWindow, SLOT(esplist_changed())); - connect(&m_PluginList, SIGNAL(esplist_changed()), widget, + connect(&m_PluginList, SIGNAL(esplist_changed()), m_MainWindow, SLOT(esplist_changed())); - connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget, + connect(&m_DownloadManager, SIGNAL(showMessage(QString)), m_MainWindow, SLOT(showMessage(QString))); } - m_InstallationManager.setParentWidget(widget); - m_Updater.setUserInterface(widget); + m_InstallationManager.setParentWidget(m_MainWindow); + m_Updater.setUserInterface(m_MainWindow); checkForUpdates(); } @@ -295,7 +294,7 @@ void OrganizerCore::checkForUpdates() { // this currently wouldn't work reliably if the ui isn't initialized yet to // display the result - if (m_UserInterface != nullptr) { + if (m_MainWindow != nullptr) { m_Updater.testForUpdate(m_Settings); } } @@ -755,13 +754,13 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, int modIndex = ModInfo::getIndex(modName); if (modIndex != UINT_MAX) { ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - if (hasIniTweaks && (m_UserInterface != nullptr) + if (hasIniTweaks && (m_MainWindow != nullptr) && (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"), tr("This mod contains ini tweaks. Do you " "want to configure them now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - m_UserInterface->displayModInformation( + m_MainWindow->displayModInformation( modInfo, modIndex, ModInfoTabIDs::IniFiles); } m_ModInstalled(modName); @@ -822,13 +821,13 @@ void OrganizerCore::installDownload(int index) ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); modInfo->addInstalledFile(modID, fileID); - if (hasIniTweaks && m_UserInterface != nullptr + if (hasIniTweaks && m_MainWindow != nullptr && (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"), tr("This mod contains ini tweaks. Do you " "want to configure them now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - m_UserInterface->displayModInformation( + m_MainWindow->displayModInformation( modInfo, modIndex, ModInfoTabIDs::IniFiles); } @@ -1250,11 +1249,11 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, std::unique_ptr<LockedDialog> dlg; ILockedWaitingForProcess* uilock = nullptr; - if (m_UserInterface != nullptr) { - uilock = m_UserInterface->lock(); + if (m_MainWindow != nullptr) { + uilock = m_MainWindow->lock(); } else { - // i.e. when running command line shortcuts there is no m_UserInterface + // i.e. when running command line shortcuts there is no user interface dlg.reset(new LockedDialog); dlg->show(); dlg->setEnabled(true); @@ -1262,8 +1261,8 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, } ON_BLOCK_EXIT([&]() { - if (m_UserInterface != nullptr) { - m_UserInterface->unlock(); + if (m_MainWindow != nullptr) { + m_MainWindow->unlock(); } }); DWORD ignoreExitCode; @@ -1275,35 +1274,21 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, } -HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, - const QString &arguments, - const QString &profileName, - const QDir ¤tDirectory, - const QString &steamAppID, - const QString &customOverwrite, - const QList<MOBase::ExecutableForcedLoadSetting> &forcedLibraries) +HANDLE OrganizerCore::spawnBinaryProcess( + const QFileInfo &binary, const QString &arguments, const QString &profileName, + const QDir ¤tDirectory, const QString &steamAppID, + const QString &customOverwrite, + const QList<MOBase::ExecutableForcedLoadSetting> &forcedLibraries) { spawn::SpawnParameters sp; sp.binary = binary; sp.arguments = arguments; sp.currentDirectory = currentDirectory; + sp.steamAppID = steamAppID; sp.hooked = true; prepareStart(); - QWidget *window = qApp->activeWindow(); - if ((window != nullptr) && (!window->isVisible())) { - window = nullptr; - } - - if (!spawn::checkBinary(window, sp)) { - return INVALID_HANDLE_VALUE; - } - - if (!spawn::checkSteam(window, sp, managedGame()->gameDirectory(), steamAppID, m_Settings)) { - return INVALID_HANDLE_VALUE; - } - while (m_DirectoryUpdate) { ::Sleep(100); QCoreApplication::processEvents(); @@ -1315,72 +1300,25 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, } // TODO: should also pass arguments - if (m_AboutToRun(binary.absoluteFilePath())) { - try { - m_USVFS.updateMapping(fileMapping(profileName, customOverwrite)); - m_USVFS.updateForcedLibraries(forcedLibraries); - - } catch (const UsvfsConnectorException &e) { - log::debug(e.what()); - return INVALID_HANDLE_VALUE; - } catch (const std::exception &e) { - QMessageBox::warning(window, tr("Error"), e.what()); - return INVALID_HANDLE_VALUE; - } - - if (!spawn::checkEnvironment(window, sp)) { - return INVALID_HANDLE_VALUE; - } - - if (!spawn::checkBlacklist(window, sp, m_Settings)) { - return INVALID_HANDLE_VALUE; - } - - QString modsPath = settings().paths().mods(); - - // Check if this a request with either an executable or a working directory under our mods folder - // then will start the process in a virtualized "environment" with the appropriate paths fixed: - // (i.e. mods\FNIS\path\exe => game\data\path\exe) - QString cwdPath = currentDirectory.absolutePath(); - bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive); - QString binPath = binary.absoluteFilePath(); - bool virtualizedBin = binPath.startsWith(modsPath, Qt::CaseInsensitive); - if (virtualizedCwd || virtualizedBin) { - if (virtualizedCwd) { - int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1); - QString adjustedCwd = cwdPath.mid(cwdOffset, -1); - cwdPath = m_GamePlugin->dataDirectory().absolutePath(); - if (cwdOffset >= 0) - cwdPath += adjustedCwd; - - } - - if (virtualizedBin) { - int binOffset = binPath.indexOf('/', modsPath.length() + 1); - QString adjustedBin = binPath.mid(binOffset, -1); - binPath = m_GamePlugin->dataDirectory().absolutePath(); - if (binOffset >= 0) - binPath += adjustedBin; - } - - QString cmdline - = QString("launch \"%1\" \"%2\" %3") - .arg(QDir::toNativeSeparators(cwdPath), - QDir::toNativeSeparators(binPath), arguments); + if (!m_AboutToRun(binary.absoluteFilePath())) { + log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath()); + return INVALID_HANDLE_VALUE; + } - sp.binary = QFileInfo(QCoreApplication::applicationFilePath()); - sp.arguments = cmdline; - sp.currentDirectory.setPath(QCoreApplication::applicationDirPath()); + try { + m_USVFS.updateMapping(fileMapping(profileName, customOverwrite)); + m_USVFS.updateForcedLibraries(forcedLibraries); - return spawn::startBinary(window, sp); - } else { - log::debug("Spawning direct process <{}, {}, {}>", binPath, arguments, cwdPath); - return spawn::startBinary(window, sp); - } - } else { - log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath()); + } catch (const UsvfsConnectorException &e) { + log::debug(e.what()); + return INVALID_HANDLE_VALUE; + } catch (const std::exception &e) { + QMessageBox::warning(m_MainWindow, tr("Error"), e.what()); return INVALID_HANDLE_VALUE; } + + auto process = spawn::Spawner().spawn(m_MainWindow, m_GamePlugin, sp, m_Settings); + return process.releaseHandle(); } HANDLE OrganizerCore::runShortcut(const MOShortcut& shortcut) @@ -1498,13 +1436,13 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) return true; ILockedWaitingForProcess* uilock = nullptr; - if (m_UserInterface != nullptr) { - uilock = m_UserInterface->lock(); + if (m_MainWindow != nullptr) { + uilock = m_MainWindow->lock(); } ON_BLOCK_EXIT([&] () { - if (m_UserInterface != nullptr) { - m_UserInterface->unlock(); + if (m_MainWindow != nullptr) { + m_MainWindow->unlock(); } }); return waitForProcessCompletion(handle, exitCode, uilock); } @@ -1762,8 +1700,8 @@ void OrganizerCore::refreshBSAList() m_ActiveArchives = m_DefaultArchives; } - if (m_UserInterface != nullptr) { - m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); + if (m_MainWindow != nullptr) { + m_MainWindow->updateBSAList(m_DefaultArchives, m_ActiveArchives); } m_ArchivesInit = true; @@ -1879,8 +1817,8 @@ void OrganizerCore::updateModsInDirectoryStructure(QMap<unsigned int, ModInfo::P // now we need to refresh the bsa list and save it so there is no confusion // about what archives are available and active refreshBSAList(); - if (m_UserInterface != nullptr) { - m_UserInterface->archivesWriter().writeImmediately(false); + if (m_MainWindow != nullptr) { + m_MainWindow->archivesWriter().writeImmediately(false); } std::vector<QString> archives = enabledArchives(); @@ -2064,8 +2002,8 @@ void OrganizerCore::modStatusChanged(unsigned int index) = m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())); origin.enable(false); } - if (m_UserInterface != nullptr) { - m_UserInterface->archivesWriter().write(); + if (m_MainWindow != nullptr) { + m_MainWindow->archivesWriter().write(); } } modInfo->clearCaches(); @@ -2114,8 +2052,8 @@ void OrganizerCore::modStatusChanged(QList<unsigned int> index) { origin.enable(false); } } - if (m_UserInterface != nullptr) { - m_UserInterface->archivesWriter().write(); + if (m_MainWindow != nullptr) { + m_MainWindow->archivesWriter().write(); } } @@ -2290,8 +2228,8 @@ bool OrganizerCore::saveCurrentLists() try { savePluginList(); - if (m_UserInterface != nullptr) { - m_UserInterface->archivesWriter().write(); + if (m_MainWindow != nullptr) { + m_MainWindow->archivesWriter().write(); } } catch (const std::exception &e) { reportError(tr("failed to save load order: %1").arg(e.what())); diff --git a/src/organizercore.h b/src/organizercore.h index 0d0a092c..04c96ba6 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -3,7 +3,7 @@ #include "selfupdater.h"
-#include "iuserinterface.h" //should be class IUserInterface;
+#include "ilockedwaitingforprocess.h"
#include "settings.h"
#include "modlist.h"
#include "modinfo.h"
@@ -26,6 +26,8 @@ class ModListSortProxy;
class PluginListSortProxy;
class Profile;
+class MainWindow;
+
namespace MOBase {
template <typename T> class GuessedValue;
class IModInterface;
@@ -101,7 +103,7 @@ public: ~OrganizerCore();
- void setUserInterface(IUserInterface *userInterface, QWidget *widget);
+ void setUserInterface(MainWindow* mainWindow);
void connectPlugins(PluginContainer *container);
void disconnectPlugins();
@@ -328,8 +330,7 @@ private: static const unsigned int PROBLEM_MO1SCRIPTEXTENDERWORKAROUND = 1;
private:
-
- IUserInterface *m_UserInterface;
+ MainWindow* m_MainWindow;
PluginContainer *m_PluginContainer;
QString m_GameName;
MOBase::IPluginGame *m_GamePlugin;
diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index c0706ba8..767d3eb8 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -3,6 +3,7 @@ #include "organizerproxy.h"
#include "report.h"
#include <ipluginproxy.h>
+#include <iuserinterface.h>
#include <idownloadmanager.h>
#include <appconfig.h>
#include <QAction>
diff --git a/src/spawn.cpp b/src/spawn.cpp index a34230b2..3c7d64ce 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "envmodule.h"
#include "settings.h"
#include "settingsdialogworkarounds.h"
+#include <iplugingame.h>
#include <errorcodes.h>
#include <report.h>
#include <log.h>
@@ -440,7 +441,7 @@ QMessageBox::StandardButton confirmBlacklisted( namespace spawn
{
-DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHandle)
+DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle)
{
BOOL inheritHandles = FALSE;
@@ -494,7 +495,7 @@ DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHand }
processHandle = pi.hProcess;
- threadHandle = pi.hThread;
+ ::CloseHandle(pi.hThread);
return ERROR_SUCCESS;
}
@@ -618,8 +619,7 @@ bool startSteam(QWidget* parent) (password.isEmpty() ? "no" : "yes"));
HANDLE ph = INVALID_HANDLE_VALUE;
- HANDLE th = INVALID_HANDLE_VALUE;
- const auto e = spawn(sp, ph, th);
+ const auto e = spawn(sp, ph);
if (e != ERROR_SUCCESS) {
// make sure username and passwords are not shown
@@ -772,17 +772,59 @@ bool checkBlacklist( }
+void adjustForVirtualized(
+ const IPluginGame* game, SpawnParameters& sp, const Settings& settings)
+{
+ const QString modsPath = settings.paths().mods();
+
+ // Check if this a request with either an executable or a working directory
+ // under our mods folder then will start the process in a virtualized
+ // "environment" with the appropriate paths fixed:
+ // (i.e. mods\FNIS\path\exe => game\data\path\exe)
+ QString cwdPath = sp.currentDirectory.absolutePath();
+ bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive);
+ QString binPath = sp.binary.absoluteFilePath();
+ bool virtualizedBin = binPath.startsWith(modsPath, Qt::CaseInsensitive);
+ if (virtualizedCwd || virtualizedBin) {
+ if (virtualizedCwd) {
+ int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1);
+ QString adjustedCwd = cwdPath.mid(cwdOffset, -1);
+ cwdPath = game->dataDirectory().absolutePath();
+ if (cwdOffset >= 0)
+ cwdPath += adjustedCwd;
+
+ }
+
+ if (virtualizedBin) {
+ int binOffset = binPath.indexOf('/', modsPath.length() + 1);
+ QString adjustedBin = binPath.mid(binOffset, -1);
+ binPath = game->dataDirectory().absolutePath();
+ if (binOffset >= 0)
+ binPath += adjustedBin;
+ }
+
+ QString cmdline
+ = QString("launch \"%1\" \"%2\" %3")
+ .arg(QDir::toNativeSeparators(cwdPath),
+ QDir::toNativeSeparators(binPath), sp.arguments);
+
+ sp.binary = QFileInfo(QCoreApplication::applicationFilePath());
+ sp.arguments = cmdline;
+ sp.currentDirectory.setPath(QCoreApplication::applicationDirPath());
+ }
+}
+
+
HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
{
- HANDLE processHandle, threadHandle;
- const auto e = spawn(sp, processHandle, threadHandle);
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ const auto e = spawn::spawn(sp, handle);
switch (e)
{
case ERROR_SUCCESS:
{
- ::CloseHandle(threadHandle);
- return processHandle;
+ return handle;
}
case ERROR_ELEVATION_REQUIRED:
@@ -799,6 +841,79 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp) }
}
+
+
+SpawnedProcess::SpawnedProcess(HANDLE handle, SpawnParameters sp)
+ : m_handle(handle), m_parameters(std::move(sp))
+{
+}
+
+SpawnedProcess::SpawnedProcess(SpawnedProcess&& other)
+ : m_handle(other.m_handle), m_parameters(std::move(other.m_parameters))
+{
+ other.m_handle = INVALID_HANDLE_VALUE;
+}
+
+SpawnedProcess& SpawnedProcess::operator=(SpawnedProcess&& other)
+{
+ if (this != &other) {
+ destroy();
+
+ m_handle = other.m_handle;
+ other.m_handle = INVALID_HANDLE_VALUE;
+
+ m_parameters = std::move(other.m_parameters);
+ }
+
+ return *this;
+}
+
+SpawnedProcess::~SpawnedProcess()
+{
+ destroy();
+}
+
+HANDLE SpawnedProcess::releaseHandle()
+{
+ const auto h = m_handle;
+ m_handle = INVALID_HANDLE_VALUE;
+ return h;
+}
+
+void SpawnedProcess::destroy()
+{
+ if (m_handle != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(m_handle);
+ m_handle = INVALID_HANDLE_VALUE;
+ }
+}
+
+
+SpawnedProcess Spawner::spawn(
+ QWidget* parent, const IPluginGame* game,
+ SpawnParameters sp, Settings& settings)
+{
+ if (!checkBinary(parent, sp)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!checkSteam(parent, sp, game->gameDirectory(), sp.steamAppID, settings)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!spawn::checkEnvironment(parent, sp)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!spawn::checkBlacklist(parent, sp, settings)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ adjustForVirtualized(game, sp, settings);
+
+ return {startBinary(parent, sp), sp};
+}
+
} // namespace
diff --git a/src/spawn.h b/src/spawn.h index 31b44739..d2853cd5 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QDir>
class Settings;
+namespace MOBase { class IPluginGame; }
namespace spawn
{
@@ -46,6 +47,7 @@ struct SpawnParameters QFileInfo binary;
QString arguments;
QDir currentDirectory;
+ QString steamAppID;
bool hooked = false;
HANDLE stdOut = INVALID_HANDLE_VALUE;
HANDLE stdErr = INVALID_HANDLE_VALUE;
@@ -69,6 +71,38 @@ bool checkBlacklist( **/
HANDLE startBinary(QWidget* parent, const SpawnParameters& sp);
+
+class SpawnedProcess
+{
+public:
+ SpawnedProcess(HANDLE handle, SpawnParameters sp);
+
+ SpawnedProcess(const SpawnedProcess&) = delete;
+ SpawnedProcess& operator=(const SpawnedProcess&) = delete;
+ SpawnedProcess(SpawnedProcess&& other);
+ SpawnedProcess& operator=(SpawnedProcess&& other);
+ ~SpawnedProcess();
+
+ HANDLE releaseHandle();
+
+private:
+ HANDLE m_handle;
+ SpawnParameters m_parameters;
+
+ void destroy();
+};
+
+
+class Spawner
+{
+public:
+ SpawnedProcess spawn(
+ QWidget* parent, const MOBase::IPluginGame* game,
+ SpawnParameters sp, Settings& settings);
+
+private:
+};
+
} // namespace
|
