Core: Add initial process support (Process::SpawnDetached)
This commit is contained in:
parent
278e59934b
commit
ac1422c221
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_CORE_PROCESS_HPP
|
||||
#define NAZARA_CORE_PROCESS_HPP
|
||||
|
||||
#include <NazaraUtils/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Config.hpp>
|
||||
#include <NazaraUtils/Result.hpp>
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
using Pid = UInt32;
|
||||
|
||||
class NAZARA_CORE_API Process
|
||||
{
|
||||
public:
|
||||
Process() = default;
|
||||
Process(const Process&) = delete;
|
||||
Process(Process&&) = delete;
|
||||
~Process() = default;
|
||||
|
||||
Process& operator=(const Process&) = delete;
|
||||
Process& operator=(Process&&) = delete;
|
||||
|
||||
static Result<Pid, std::string> SpawnDetached(const std::filesystem::path& program, std::span<const std::string> arguments = {}, const std::filesystem::path& workingDirectory = {}); };
|
||||
}
|
||||
|
||||
#include <Nazara/Core/Process.inl>
|
||||
|
||||
#endif // NAZARA_CORE_PROCESS_HPP
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Core/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
#include <Nazara/Core/DebugOff.hpp>
|
||||
|
|
@ -36,6 +36,7 @@ namespace Nz
|
|||
inline bool IsNumber(std::string_view str);
|
||||
|
||||
NAZARA_CORE_API void IterateOnCodepoints(std::string_view str, FunctionRef<bool(std::u32string_view characters)> callback);
|
||||
NAZARA_CORE_API void IterateOnWideChars(std::string_view str, FunctionRef<bool(std::wstring_view characters)> callback);
|
||||
|
||||
NAZARA_CORE_API bool MatchPattern(std::string_view str, std::string_view pattern);
|
||||
|
||||
|
|
@ -43,7 +44,9 @@ namespace Nz
|
|||
|
||||
NAZARA_CORE_API std::string PointerToString(const void* ptr);
|
||||
|
||||
inline std::string& ReplaceStr(std::string& str, std::string_view from, std::string_view to);
|
||||
template<typename T> std::basic_string<T>& ReplaceStr(std::basic_string<T>& str, T from, T to);
|
||||
template<typename T> std::basic_string<T>& ReplaceStr(std::basic_string<T>& str, const T* from, const T* to);
|
||||
template<typename T> std::basic_string<T>& ReplaceStr(std::basic_string<T>& str, std::basic_string_view<T> from, std::basic_string_view<T> to);
|
||||
|
||||
inline bool StartsWith(std::string_view str, std::string_view s);
|
||||
NAZARA_CORE_API bool StartsWith(std::string_view lhs, std::string_view rhs, CaseIndependent);
|
||||
|
|
|
|||
|
|
@ -77,11 +77,28 @@ namespace Nz
|
|||
return str;
|
||||
}
|
||||
|
||||
inline std::string& ReplaceStr(std::string& str, std::string_view from, std::string_view to)
|
||||
template<typename T>
|
||||
std::basic_string<T>& ReplaceStr(std::basic_string<T>& str, T from, T to)
|
||||
{
|
||||
if (str.empty())
|
||||
return str;
|
||||
std::size_t startPos = 0;
|
||||
while ((startPos = str.find(from, startPos)) != std::string::npos)
|
||||
{
|
||||
str[startPos] = to;
|
||||
startPos++;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::basic_string<T>& ReplaceStr(std::basic_string<T>& str, const T* from, const T* to)
|
||||
{
|
||||
return ReplaceStr(str, std::basic_string_view<T>(from), std::basic_string_view<T>(to));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::basic_string<T>& ReplaceStr(std::basic_string<T>& str, std::basic_string_view<T> from, std::basic_string_view<T> to)
|
||||
{
|
||||
std::size_t startPos = 0;
|
||||
while ((startPos = str.find(from, startPos)) != std::string::npos)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Core/Posix/PosixUtils.hpp>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <utility>
|
||||
#include <fcntl.h>
|
||||
#include <Nazara/Core/Debug.hpp>
|
||||
|
||||
namespace Nz::PlatformImpl
|
||||
{
|
||||
int SafeClose(int fd)
|
||||
{
|
||||
#if defined(NAZARA_PLATFORM_LINUX) || defined(NAZARA_PLATFORM_ANDROID)
|
||||
// Retrying close on Linux is dangerous
|
||||
// https://android.googlesource.com/platform/bionic/+/master/docs/EINTR.md
|
||||
// https://lwn.net/Articles/576478/
|
||||
return ::close(fd);
|
||||
#else
|
||||
int ret;
|
||||
do
|
||||
{
|
||||
ret = ::close(fd);
|
||||
}
|
||||
while (ret != -1 || errno == EINTR);
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t SafeRead(int fd, void* buf, size_t count)
|
||||
{
|
||||
ssize_t ret;
|
||||
do
|
||||
{
|
||||
ret = ::read(fd, buf, count);
|
||||
}
|
||||
while (ret != -1 || errno == EINTR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t SafeWrite(int fd, const void* buf, size_t count)
|
||||
{
|
||||
ssize_t ret;
|
||||
do
|
||||
{
|
||||
ret = ::write(fd, buf, count);
|
||||
}
|
||||
while (ret != -1 || errno == EINTR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Pipe::Pipe(int flags) :
|
||||
m_readFd(-1),
|
||||
m_writeFd(-1)
|
||||
{
|
||||
int fds[2];
|
||||
if (::pipe2(fds, flags & O_CLOEXEC) != 0)
|
||||
return;
|
||||
|
||||
m_readFd = fds[0];
|
||||
m_writeFd = fds[1];
|
||||
}
|
||||
|
||||
Pipe::Pipe(Pipe&& other) noexcept :
|
||||
m_readFd(std::exchange(other.m_readFd, -1)),
|
||||
m_writeFd(std::exchange(other.m_writeFd, -1))
|
||||
{
|
||||
}
|
||||
|
||||
Pipe::~Pipe()
|
||||
{
|
||||
if (m_readFd != -1)
|
||||
{
|
||||
assert(m_writeFd != -1);
|
||||
SafeClose(m_readFd);
|
||||
SafeClose(m_writeFd);
|
||||
}
|
||||
}
|
||||
|
||||
Pipe& Pipe::operator=(Pipe&& other) noexcept
|
||||
{
|
||||
m_readFd = std::exchange(other.m_readFd, -1);
|
||||
m_writeFd = std::exchange(other.m_writeFd, -1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Pipe::operator bool() const
|
||||
{
|
||||
return m_readFd != -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_CORE_POSIX_POSIXUTILS_HPP
|
||||
#define NAZARA_CORE_POSIX_POSIXUTILS_HPP
|
||||
|
||||
#include <NazaraUtils/Prerequisites.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Nz::PlatformImpl
|
||||
{
|
||||
int SafeClose(int fd);
|
||||
ssize_t SafeRead(int fd, void* buf, size_t count);
|
||||
ssize_t SafeWrite(int fd, const void* buf, size_t count);
|
||||
|
||||
class Pipe
|
||||
{
|
||||
public:
|
||||
Pipe(int flags = 0);
|
||||
Pipe(const Pipe&) = delete;
|
||||
Pipe(Pipe&& other) noexcept;
|
||||
~Pipe();
|
||||
|
||||
inline int GetReadFd();
|
||||
inline int GetWriteFd();
|
||||
|
||||
inline ssize_t Read(void* buf, size_t count);
|
||||
inline ssize_t Write(const void* buf, size_t count);
|
||||
|
||||
Pipe& operator=(const Pipe&) = delete;
|
||||
Pipe& operator=(Pipe&& other) noexcept;
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
private:
|
||||
int m_readFd;
|
||||
int m_writeFd;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Core/Posix/PosixUtils.inl>
|
||||
|
||||
#endif // NAZARA_CORE_POSIX_POSIXUTILS_HPP
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
namespace Nz::PlatformImpl
|
||||
{
|
||||
inline int Pipe::GetReadFd()
|
||||
{
|
||||
return m_readFd;
|
||||
}
|
||||
|
||||
inline int Pipe::GetWriteFd()
|
||||
{
|
||||
return m_writeFd;
|
||||
}
|
||||
|
||||
inline ssize_t Pipe::Read(void* buf, size_t count)
|
||||
{
|
||||
return SafeRead(m_readFd, buf, count);
|
||||
}
|
||||
|
||||
inline ssize_t Pipe::Write(const void* buf, size_t count)
|
||||
{
|
||||
return SafeWrite(m_writeFd, buf, count);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Core/Posix/ProcessImpl.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Posix/PosixUtils.hpp>
|
||||
#include <NazaraUtils/Algorithm.hpp>
|
||||
#include <NazaraUtils/StackArray.hpp>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <Nazara/Core/Debug.hpp>
|
||||
|
||||
namespace Nz::PlatformImpl
|
||||
{
|
||||
Result<Pid, std::string> SpawnDetachedProcess(const std::filesystem::path& program, std::span<const std::string> arguments, const std::filesystem::path& workingDirectory)
|
||||
{
|
||||
struct PidOrErr
|
||||
{
|
||||
pid_t pid;
|
||||
int err;
|
||||
};
|
||||
|
||||
Pipe pipe;
|
||||
if (!pipe)
|
||||
return Err("failed to create pipe: " + Error::GetLastSystemError());
|
||||
|
||||
// Double fork (see https://0xjet.github.io/3OHA/2022/04/11/post.html)
|
||||
// We will create a child and a grand-child process, using a pipe to retrieve the grand-child pid
|
||||
pid_t childPid = ::fork();
|
||||
if (childPid == -1)
|
||||
return Err("failed to create child: " + Error::GetLastSystemError());
|
||||
|
||||
if (childPid == 0)
|
||||
{
|
||||
// Child process
|
||||
::setsid();
|
||||
|
||||
pid_t grandChildPid = ::vfork();
|
||||
if (grandChildPid == 0)
|
||||
{
|
||||
// Grand-child process
|
||||
StackArray<char*> argv = NazaraStackArrayNoInit(char*, arguments.size() + 2);
|
||||
|
||||
// It's safe to const_cast here as we're using a copy of the memory (from child) from the original process
|
||||
argv[0] = const_cast<char*>(program.c_str());
|
||||
for (std::size_t i = 0; i < arguments.size(); ++i)
|
||||
argv[i + 1] = const_cast<char*>(arguments[i].data());
|
||||
|
||||
argv[argv.size() - 1] = nullptr;
|
||||
|
||||
char* envs[] = { nullptr };
|
||||
|
||||
if (!workingDirectory.empty())
|
||||
::chdir(workingDirectory.c_str());
|
||||
|
||||
if (::execve(program.c_str(), argv.data(), envs) == -1)
|
||||
{
|
||||
PidOrErr err;
|
||||
err.pid = -1;
|
||||
err.err = errno;
|
||||
|
||||
pipe.Write(&err, sizeof(err));
|
||||
}
|
||||
}
|
||||
else if (grandChildPid != -1)
|
||||
{
|
||||
PidOrErr err;
|
||||
err.pid = grandChildPid;
|
||||
|
||||
pipe.Write(&err, sizeof(err));
|
||||
}
|
||||
else
|
||||
{
|
||||
PidOrErr err;
|
||||
err.pid = -1;
|
||||
err.err = errno;
|
||||
|
||||
pipe.Write(&err, sizeof(err));
|
||||
}
|
||||
|
||||
// Exits the child process, at this point the grand-child should have started
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
// Parent process
|
||||
|
||||
// Wait for and reap the child
|
||||
int childStatus;
|
||||
::waitpid(childPid, &childStatus, 0);
|
||||
|
||||
PidOrErr pidOrErr;
|
||||
if (pipe.Read(&pidOrErr, sizeof(pidOrErr) != sizeof(pidOrErr)))
|
||||
{
|
||||
// this should never happen
|
||||
return Err("failed to create child: couldn't retrieve status from pipe");
|
||||
}
|
||||
|
||||
if (pidOrErr.pid < 0)
|
||||
return Err(Error::GetLastSystemError(pidOrErr.err));
|
||||
|
||||
return SafeCast<Pid>(pidOrErr.pid);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_CORE_POSIX_PROCESSIMPL_HPP
|
||||
#define NAZARA_CORE_POSIX_PROCESSIMPL_HPP
|
||||
|
||||
#include <NazaraUtils/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Process.hpp>
|
||||
|
||||
namespace Nz::PlatformImpl
|
||||
{
|
||||
NAZARA_CORE_API Result<Pid, std::string> SpawnDetachedProcess(const std::filesystem::path& program, std::span<const std::string> arguments = {}, const std::filesystem::path& workingDirectory = {});
|
||||
}
|
||||
|
||||
#endif // NAZARA_CORE_POSIX_PROCESSIMPL_HPP
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Core/Process.hpp>
|
||||
|
||||
#if defined(NAZARA_PLATFORM_WINDOWS)
|
||||
#include <Nazara/Core/Win32/ProcessImpl.hpp>
|
||||
#elif defined(NAZARA_PLATFORM_POSIX)
|
||||
#include <Nazara/Core/Posix/ProcessImpl.hpp>
|
||||
#else
|
||||
#error OS not handled
|
||||
#endif
|
||||
|
||||
#include <Nazara/Core/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
Result<Pid, std::string> Process::SpawnDetached(const std::filesystem::path& program, std::span<const std::string> arguments, const std::filesystem::path& workingDirectory)
|
||||
{
|
||||
return PlatformImpl::SpawnDetachedProcess(program, arguments, workingDirectory);
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +56,14 @@ namespace Nz
|
|||
template<>
|
||||
struct WideConverter<2>
|
||||
{
|
||||
static constexpr std::size_t MaxCharacterPerCodepoint = 2;
|
||||
|
||||
// UTF-16 (Windows)
|
||||
static std::size_t Append(char32_t codepoint, wchar_t* output)
|
||||
{
|
||||
return utf8::append16(codepoint, output) - output;
|
||||
}
|
||||
|
||||
static std::string From(const wchar_t* wstr, std::size_t size)
|
||||
{
|
||||
return FromUtf16String(std::u16string_view(reinterpret_cast<const char16_t*>(wstr), size));
|
||||
|
|
@ -76,7 +83,15 @@ namespace Nz
|
|||
template<>
|
||||
struct WideConverter<4>
|
||||
{
|
||||
static constexpr std::size_t MaxCharacterPerCodepoint = 1;
|
||||
|
||||
// UTF-32 (POSIX)
|
||||
static std::size_t Append(char32_t codepoint, wchar_t* output)
|
||||
{
|
||||
*output = codepoint;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static std::string From(const wchar_t* wstr, std::size_t size)
|
||||
{
|
||||
return FromUtf32String(std::u32string_view(reinterpret_cast<const char32_t*>(wstr), size));
|
||||
|
|
@ -91,6 +106,8 @@ namespace Nz
|
|||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
using NativeWideConverter = WideConverter<sizeof(wchar_t)>;
|
||||
}
|
||||
|
||||
std::size_t ComputeCharacterCount(std::string_view str)
|
||||
|
|
@ -183,7 +200,7 @@ namespace Nz
|
|||
{
|
||||
NAZARA_USE_ANONYMOUS_NAMESPACE
|
||||
|
||||
return WideConverter<sizeof(wchar_t)>::From(wstr.data(), wstr.size());
|
||||
return NativeWideConverter::From(wstr.data(), wstr.size());
|
||||
}
|
||||
|
||||
std::size_t GetCharacterPosition(std::string_view str, std::size_t characterIndex)
|
||||
|
|
@ -283,6 +300,31 @@ namespace Nz
|
|||
callback(std::u32string_view(&buffer[0], charCount));
|
||||
}
|
||||
|
||||
void IterateOnWideChars(std::string_view str, FunctionRef<bool(std::wstring_view characters)> callback)
|
||||
{
|
||||
std::array<wchar_t, 128> buffer;
|
||||
std::size_t charCount = 0;
|
||||
|
||||
utf8::unchecked::iterator<const char*> it(str.data());
|
||||
utf8::unchecked::iterator<const char*> end(str.data() + str.size());
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
charCount += NativeWideConverter::Append(*it, &buffer[charCount]);
|
||||
|
||||
// Leave enough space for a full character (using up to MaxCharacterPerCodepoint)
|
||||
if (buffer.size() - charCount < NativeWideConverter::MaxCharacterPerCodepoint)
|
||||
{
|
||||
if (!callback(std::wstring_view(&buffer[0], charCount)))
|
||||
return;
|
||||
|
||||
charCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (charCount != 0)
|
||||
callback(std::wstring_view(&buffer[0], charCount));
|
||||
}
|
||||
|
||||
bool MatchPattern(std::string_view str, std::string_view pattern)
|
||||
{
|
||||
if (str.empty() || pattern.empty())
|
||||
|
|
@ -514,7 +556,7 @@ namespace Nz
|
|||
{
|
||||
NAZARA_USE_ANONYMOUS_NAMESPACE
|
||||
|
||||
return WideConverter<sizeof(wchar_t)>::To(str);
|
||||
return NativeWideConverter::To(str);
|
||||
}
|
||||
|
||||
std::string_view TrimLeft(std::string_view str)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Core/Win32/ProcessImpl.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/StringExt.hpp>
|
||||
#include <Nazara/Core/Win32/Win32Utils.hpp>
|
||||
#include <Windows.h>
|
||||
#include <Nazara/Core/Debug.hpp>
|
||||
|
||||
namespace Nz::PlatformImpl
|
||||
{
|
||||
std::wstring BuildCommandLine(const std::filesystem::path& program, std::span<const std::string> arguments)
|
||||
{
|
||||
std::wstring commandLine;
|
||||
|
||||
auto AddProgramName = [&commandLine](const WidePathHolder& program)
|
||||
{
|
||||
if (!program.starts_with(L'"') && !program.ends_with(L'"') && program.find(L' ') != program.npos)
|
||||
{
|
||||
commandLine += L'"';
|
||||
commandLine += program;
|
||||
commandLine += L'"';
|
||||
}
|
||||
else
|
||||
commandLine += program;
|
||||
|
||||
ReplaceStr(commandLine, L'/', L'\\');
|
||||
commandLine += L' ';
|
||||
};
|
||||
|
||||
// Use a lambda to keep WidePathHolder alive
|
||||
AddProgramName(PathToWideTemp(program));
|
||||
|
||||
for (std::string_view arg : arguments)
|
||||
{
|
||||
commandLine += L' ';
|
||||
|
||||
if (arg.empty())
|
||||
{
|
||||
// Empty argument (ensures quotes)
|
||||
commandLine += LR"("")";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Characters requiring quotes from cmd /?
|
||||
constexpr std::string_view specialChars = "\t &()[]{}^=;!'+,`~%|<>";
|
||||
|
||||
bool requiresQuote = arg.find_first_of(specialChars) != arg.npos;
|
||||
|
||||
if (requiresQuote)
|
||||
commandLine += L'"';
|
||||
|
||||
std::size_t backslashCount = 0;
|
||||
IterateOnWideChars(arg, [&](std::wstring_view characters)
|
||||
{
|
||||
for (wchar_t character : characters)
|
||||
{
|
||||
if (character != L'\\')
|
||||
{
|
||||
// Escape quotes and double their preceding backslashes ('\\\"' => '\\\\\\\"')
|
||||
if (character == L'"')
|
||||
commandLine.append(backslashCount + 1, L'\\');
|
||||
|
||||
backslashCount = 0;
|
||||
}
|
||||
else
|
||||
backslashCount++;
|
||||
|
||||
commandLine.push_back(character);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (requiresQuote)
|
||||
{
|
||||
commandLine.append(backslashCount, L'\\');
|
||||
commandLine += L'"';
|
||||
}
|
||||
}
|
||||
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
Result<Pid, std::string> SpawnDetachedProcess(const std::filesystem::path& program, std::span<const std::string> arguments, const std::filesystem::path& workingDirectory)
|
||||
{
|
||||
DWORD creationFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS;
|
||||
|
||||
std::wstring commandLine = BuildCommandLine(program, arguments);
|
||||
|
||||
STARTUPINFOW startupInfo = {
|
||||
.cb = sizeof(startupInfo),
|
||||
.dwX = DWORD(CW_USEDEFAULT),
|
||||
.dwY = DWORD(CW_USEDEFAULT),
|
||||
.dwXSize = DWORD(CW_USEDEFAULT),
|
||||
.dwYSize = DWORD(CW_USEDEFAULT)
|
||||
};
|
||||
|
||||
PROCESS_INFORMATION processInfo;
|
||||
BOOL success = CreateProcessW(
|
||||
nullptr, // Application name
|
||||
commandLine.data(), // Command line
|
||||
nullptr, // Process attributes
|
||||
nullptr, // Thread attributes
|
||||
false, // Inherit handles
|
||||
creationFlags, // Creation flags
|
||||
nullptr, // Environment
|
||||
(!workingDirectory.empty()) ? PathToWideTemp(workingDirectory).data() : nullptr, // Current directory
|
||||
&startupInfo, // Startup info
|
||||
&processInfo // Process information
|
||||
);
|
||||
|
||||
if (!success)
|
||||
return Err(Error::GetLastSystemError());
|
||||
|
||||
CloseHandle(processInfo.hThread);
|
||||
CloseHandle(processInfo.hProcess);
|
||||
|
||||
return Pid(processInfo.dwProcessId);
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Core/AntiWindows.hpp>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Core module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_CORE_WIN32_PROCESSIMPL_HPP
|
||||
#define NAZARA_CORE_WIN32_PROCESSIMPL_HPP
|
||||
|
||||
#include <NazaraUtils/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Process.hpp>
|
||||
|
||||
namespace Nz::PlatformImpl
|
||||
{
|
||||
NAZARA_CORE_API Result<Pid, std::string> SpawnDetachedProcess(const std::filesystem::path& program, std::span<const std::string> arguments = {}, const std::filesystem::path& workingDirectory = {});
|
||||
}
|
||||
|
||||
#endif // NAZARA_CORE_WIN32_PROCESSIMPL_HPP
|
||||
Loading…
Reference in New Issue