diff --git a/Include/Aurora/Processes/EStreamForward.hpp b/Include/Aurora/Processes/EStreamForward.hpp index 83e484f5..3dc70063 100644 --- a/Include/Aurora/Processes/EStreamForward.hpp +++ b/Include/Aurora/Processes/EStreamForward.hpp @@ -13,6 +13,7 @@ namespace Aurora::Processes eNull, eCurrentProcess, eAsyncPipe, - eNewConsoleWindow + eNewConsoleWindow, + eIOHandle )); } \ No newline at end of file diff --git a/Include/Aurora/Processes/IProcess.hpp b/Include/Aurora/Processes/IProcess.hpp index 4536ec2d..47782d33 100644 --- a/Include/Aurora/Processes/IProcess.hpp +++ b/Include/Aurora/Processes/IProcess.hpp @@ -45,7 +45,7 @@ namespace Aurora::Processes /** * @brief Creates the process, for the first and only time, given the input - * StartupParmaters provided to the relevant Spawn function + * StartupParameters provided to the relevant Spawn function * @return */ virtual bool Start() = 0; diff --git a/Include/Aurora/Processes/Processes.hpp b/Include/Aurora/Processes/Processes.hpp index d63adfa6..847163e8 100644 --- a/Include/Aurora/Processes/Processes.hpp +++ b/Include/Aurora/Processes/Processes.hpp @@ -11,7 +11,7 @@ #include "EStandardHandle.hpp" #include "EStreamForward.hpp" #include "IProcess.hpp" -#include "StartupParmaters.hpp" +#include "StartupParameters.hpp" #include "Spawn.hpp" #include "Open.hpp" \ No newline at end of file diff --git a/Include/Aurora/Processes/Spawn.hpp b/Include/Aurora/Processes/Spawn.hpp index f0de0395..1da241ef 100644 --- a/Include/Aurora/Processes/Spawn.hpp +++ b/Include/Aurora/Processes/Spawn.hpp @@ -9,5 +9,5 @@ namespace Aurora::Processes { - AUKN_SHARED_API(Spawn, IProcess, const StartupParmaters &startup); + AUKN_SHARED_API(Spawn, IProcess, StartupParameters &&startup); } \ No newline at end of file diff --git a/Include/Aurora/Processes/StartupParmaters.hpp b/Include/Aurora/Processes/StartupParameters.hpp similarity index 73% rename from Include/Aurora/Processes/StartupParmaters.hpp rename to Include/Aurora/Processes/StartupParameters.hpp index 0f52d60e..c05f6181 100644 --- a/Include/Aurora/Processes/StartupParmaters.hpp +++ b/Include/Aurora/Processes/StartupParameters.hpp @@ -1,7 +1,7 @@ /*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. - File: StartupParmaters.hpp + File: StartupParameters.hpp Date: 2022-1-29 Author: Reece ***/ @@ -9,10 +9,11 @@ namespace Aurora::Processes { - struct StartupParmaters + struct StartupParameters { - AU_COPY_MOVE_DEF(StartupParmaters); - + AU_DEF(StartupParameters); + AU_MOVE(StartupParameters); + /** * @brief Relative or absolute path to the executable binary */ @@ -54,5 +55,17 @@ namespace Aurora::Processes bool bInDebugMode {}; AuOptional workingDirectory; + + AuList> environmentVariables; + + bool bInheritEnvironmentVariables { true }; + + IO::IOHandle handleOutStream; + + IO::IOHandle handleErrorStream; + + IO::IOHandle handleInputStream; + + AuOptionalEx optAffinity; }; } \ No newline at end of file diff --git a/Source/Processes/AuProcess.NT.cpp b/Source/Processes/AuProcess.NT.cpp index 7e8fcd5e..a55e9503 100644 --- a/Source/Processes/AuProcess.NT.cpp +++ b/Source/Processes/AuProcess.NT.cpp @@ -40,7 +40,7 @@ namespace Aurora::Processes return AuLoop::ELoopSource::eProcessDead; } - ProcessImpl::ProcessImpl(const StartupParmaters ¶ms) : startup_(params) + ProcessImpl::ProcessImpl(StartupParameters &¶ms) : startup_(AuMove(params)) { AuIOFS::NormalizePath(this->startup_.process, this->startup_.process); if (this->startup_.workingDirectory) @@ -269,6 +269,28 @@ namespace Aurora::Processes return false; } } + else if (this->startup_.fwdOut == EStreamForward::eIOHandle) + { + auto optHandle = this->startup_.handleOutStream->GetOSHandleSafe(); + if (!optHandle) + { + return {}; + } + + HANDLE hTargetProcess = ::GetCurrentProcess(); + HANDLE hHandle { (HANDLE) optHandle.value() }; + + if (!::DuplicateHandle(hTargetProcess, + hHandle, + hTargetProcess, + &this->pipeStdOutWrite_, + GENERIC_READ | GENERIC_WRITE, + TRUE, + 0)) + { + return false; + } + } else if (this->startup_.fwdOut == EStreamForward::eCurrentProcess) { HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); @@ -299,6 +321,28 @@ namespace Aurora::Processes return false; } } + else if (this->startup_.fwdErr == EStreamForward::eIOHandle) + { + auto optHandle = this->startup_.handleErrorStream->GetOSHandleSafe(); + if (!optHandle) + { + return {}; + } + + HANDLE hTargetProcess = ::GetCurrentProcess(); + HANDLE hHandle { (HANDLE) optHandle.value() }; + + if (!::DuplicateHandle(hTargetProcess, + hHandle, + hTargetProcess, + &this->pipeStdErrWrite_, + GENERIC_READ | GENERIC_WRITE, + TRUE, + 0)) + { + return false; + } + } else if (this->startup_.fwdErr == EStreamForward::eCurrentProcess) { HANDLE handle = GetStdHandle(STD_ERROR_HANDLE); @@ -329,6 +373,28 @@ namespace Aurora::Processes return false; } } + else if (this->startup_.fwdIn == EStreamForward::eIOHandle) + { + auto optHandle = this->startup_.handleInputStream->GetOSHandleSafe(); + if (!optHandle) + { + return {}; + } + + HANDLE hTargetProcess = ::GetCurrentProcess(); + HANDLE hHandle { (HANDLE) optHandle.value() }; + + if (!::DuplicateHandle(hTargetProcess, + hHandle, + hTargetProcess, + &this->pipeStdInRead_, + GENERIC_READ, + TRUE, + 0)) + { + return false; + } + } else if (this->startup_.fwdIn == EStreamForward::eCurrentProcess) { HANDLE handle = GetStdHandle(STD_ERROR_HANDLE); @@ -474,7 +540,8 @@ namespace Aurora::Processes } } - if (this->startup_.bInDebugMode) + if (this->startup_.bInDebugMode || + this->startup_.optAffinity) { uCreateFlags |= CREATE_SUSPENDED; } @@ -485,11 +552,46 @@ namespace Aurora::Processes uCreateFlags |= CREATE_NO_WINDOW; } + AuList> envVars; + if (this->startup_.bInheritEnvironmentVariables) + { + if (this->startup_.environmentVariables.size()) + { + envVars = AuProcess::EnvironmentGetAll(); + + for (const auto pair : this->startup_.environmentVariables) + { + envVars.push_back(pair); + } + } + } + else + { + envVars = this->startup_.environmentVariables; + } + std::wstring envVarBlock; + + if (envVars.size() || !this->startup_.bInheritEnvironmentVariables) + { + for (const auto &[key, value] : envVars) + { + envVarBlock += AuLocale::ConvertFromUTF8(key); + envVarBlock += L'='; + envVarBlock += AuLocale::ConvertFromUTF8(value); + envVarBlock += L'\x00'; + } + + envVarBlock += L'\x00'; + + uCreateFlags |= CREATE_UNICODE_ENVIRONMENT; + } + auto result = CreateProcessW(Locale::ConvertFromUTF8(this->startup_.process).c_str(), Locale::ConvertFromUTF8(this->windowsCli_).data(), NULL, NULL, true, uCreateFlags, - NULL, wcwd.size() ? wcwd.data() : nullptr, &startupInfo, &processInfo); + envVarBlock.size() ? envVarBlock.data() : nullptr, + wcwd.size() ? wcwd.data() : nullptr, &startupInfo, &processInfo); if (!result) { @@ -503,6 +605,33 @@ namespace Aurora::Processes RelOtherHandles(); + if (this->startup_.optAffinity) + { + { + // This is good enough for now + // Minimum supported client: Windows XP [desktop apps | UWP apps] + auto primary = this->startup_.optAffinity.Value(); + auto secondary = this->startup_.optAffinity.Value(); + secondary.lower = 0; + if ((!bool(secondary)) && + (bool(primary))) + { + ::SetProcessAffinityMask(this->process_, primary.lower); + ::SetThreadAffinityMask(this->hthread_, primary.lower); + } + else + { + auto cpuSets = primary.ToCpuSets(); + // TODO: + } + } + + if (!this->startup_.bInDebugMode) + { + ::ResumeThread(this->hthread_); + } + } + if (this->type_ == ESpawnType::eSpawnChildProcessWorker) { #if defined(AURORA_PLATFORM_WIN32) @@ -569,11 +698,11 @@ namespace Aurora::Processes } } - AUKN_SYM IProcess *SpawnNew(const StartupParmaters ¶ms) + AUKN_SYM IProcess *SpawnNew(StartupParameters &¶ms) { try { - auto hi = _new ProcessImpl(params); + auto hi = _new ProcessImpl(AuMove(params)); if (!hi) { return {}; diff --git a/Source/Processes/AuProcess.NT.hpp b/Source/Processes/AuProcess.NT.hpp index 7abd65b3..c6fbaf39 100644 --- a/Source/Processes/AuProcess.NT.hpp +++ b/Source/Processes/AuProcess.NT.hpp @@ -13,7 +13,7 @@ namespace Aurora::Processes { struct ProcessImpl : IProcess { - ProcessImpl(const StartupParmaters ¶ms); + ProcessImpl(StartupParameters &¶ms); ~ProcessImpl(); bool TermWinEnumProcesses(); @@ -43,6 +43,8 @@ namespace Aurora::Processes void RelOtherHandles(); private: + HANDLE process_ {INVALID_HANDLE_VALUE}; + HANDLE hthread_ {INVALID_HANDLE_VALUE}; HANDLE pipeStdOutRead_ {INVALID_HANDLE_VALUE}; HANDLE pipeStdOutWrite_ {INVALID_HANDLE_VALUE}; @@ -65,7 +67,7 @@ namespace Aurora::Processes AuSPtr fsErrorHandle_; AuSPtr fsErrorStream_; - StartupParmaters startup_; + StartupParameters startup_; ESpawnType type_; AuList cargs_; @@ -73,8 +75,6 @@ namespace Aurora::Processes AuThreads::ThreadUnique_t thread_; HANDLE poke_ {INVALID_HANDLE_VALUE}; - HANDLE process_ {INVALID_HANDLE_VALUE}; - HANDLE hthread_ {INVALID_HANDLE_VALUE}; AuSInt exitCode_; }; } \ No newline at end of file