mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-02-17 13:14:31 +01:00
rx/Process: add spawn, waitProcess, suspendProcess & resumeProcess utils
This commit is contained in:
parent
d8fdf8209e
commit
8587801745
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rx {
|
||||
using ProcessId = std::uint32_t;
|
||||
|
|
@ -27,4 +29,21 @@ ProcessId getCurrentProcessId();
|
|||
ThreadId getCurrentThreadId();
|
||||
ProcessHandle getCurrentProcessHandle();
|
||||
ThreadHandle getCurrentThreadHandle();
|
||||
|
||||
// Spawn a new process with the given executable and arguments
|
||||
// Returns the process ID on success, or -1 on failure
|
||||
ProcessId spawn(const std::string &executable,
|
||||
const std::vector<std::string> &args = {});
|
||||
|
||||
// Wait for a process to exit and return its exit code
|
||||
// Returns true if the process exited normally, false otherwise
|
||||
bool waitProcess(ProcessId pid, int *exitCode = nullptr);
|
||||
|
||||
// Suspend a process
|
||||
// Returns true on success, false otherwise
|
||||
bool suspendProcess(ProcessId pid);
|
||||
|
||||
// Resume a suspended process
|
||||
// Returns true on success, false otherwise
|
||||
bool resumeProcess(ProcessId pid);
|
||||
} // namespace rx
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
#include <rx/Process.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
|
@ -48,3 +52,174 @@ rx::ThreadHandle rx::getCurrentThreadHandle() {
|
|||
return getCurrentThreadId();
|
||||
#endif
|
||||
}
|
||||
|
||||
rx::ProcessId rx::spawn(const std::string &executable,
|
||||
const std::vector<std::string> &args) {
|
||||
#ifdef _WIN32
|
||||
// Build command line
|
||||
std::string cmdLine = "\"" + executable + "\"";
|
||||
for (const auto &arg : args) {
|
||||
cmdLine += " \"" + arg + "\"";
|
||||
}
|
||||
|
||||
// Convert to wide string
|
||||
int wideSize =
|
||||
::MultiByteToWideChar(CP_UTF8, 0, cmdLine.c_str(), -1, nullptr, 0);
|
||||
if (wideSize <= 0) {
|
||||
return static_cast<ProcessId>(-1);
|
||||
}
|
||||
|
||||
std::vector<wchar_t> wideCmdLine(wideSize);
|
||||
::MultiByteToWideChar(CP_UTF8, 0, cmdLine.c_str(), -1, wideCmdLine.data(),
|
||||
wideSize);
|
||||
|
||||
STARTUPINFOW si{};
|
||||
si.cb = sizeof(si);
|
||||
PROCESS_INFORMATION pi{};
|
||||
|
||||
if (!::CreateProcessW(nullptr, wideCmdLine.data(), nullptr, nullptr, FALSE, 0,
|
||||
nullptr, nullptr, &si, &pi)) {
|
||||
return static_cast<ProcessId>(-1);
|
||||
}
|
||||
|
||||
ProcessId pid = ::GetProcessId(pi.hProcess);
|
||||
::CloseHandle(pi.hThread);
|
||||
::CloseHandle(pi.hProcess);
|
||||
|
||||
return pid;
|
||||
#else
|
||||
// fork + execv
|
||||
pid_t pid = ::fork();
|
||||
if (pid < 0) {
|
||||
return static_cast<ProcessId>(-1);
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
// child process
|
||||
std::vector<char *> argv;
|
||||
argv.reserve(args.size() + 2);
|
||||
|
||||
argv.push_back(const_cast<char *>(executable.c_str()));
|
||||
|
||||
// Add arguments
|
||||
for (const auto &arg : args) {
|
||||
argv.push_back(const_cast<char *>(arg.c_str()));
|
||||
}
|
||||
|
||||
argv.push_back(nullptr);
|
||||
|
||||
::execv(executable.c_str(), argv.data());
|
||||
// If execv returns, it failed
|
||||
::_exit(127);
|
||||
}
|
||||
|
||||
// parent process
|
||||
return static_cast<ProcessId>(pid);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool rx::waitProcess(ProcessId pid, int *exitCode) {
|
||||
#ifdef _WIN32
|
||||
HANDLE hProcess = ::OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
|
||||
FALSE, static_cast<DWORD>(pid));
|
||||
if (!hProcess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD result = ::WaitForSingleObject(hProcess, INFINITE);
|
||||
if (result != WAIT_OBJECT_0) {
|
||||
::CloseHandle(hProcess);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (exitCode) {
|
||||
DWORD code = 0;
|
||||
if (::GetExitCodeProcess(hProcess, &code)) {
|
||||
*exitCode = static_cast<int>(code);
|
||||
} else {
|
||||
*exitCode = -1;
|
||||
}
|
||||
}
|
||||
|
||||
::CloseHandle(hProcess);
|
||||
return true;
|
||||
#else
|
||||
int status = 0;
|
||||
pid_t result = ::waitpid(static_cast<pid_t>(pid), &status, 0);
|
||||
if (result == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (exitCode) {
|
||||
if (WIFEXITED(status)) {
|
||||
*exitCode = WEXITSTATUS(status);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
*exitCode = 128 + WTERMSIG(status);
|
||||
} else {
|
||||
*exitCode = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return WIFEXITED(status) || WIFSIGNALED(status);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool rx::suspendProcess(ProcessId pid) {
|
||||
#ifdef _WIN32
|
||||
// Use NtSuspendProcess from ntdll.dll
|
||||
using NtSuspendProcessFn = long(__stdcall *)(HANDLE);
|
||||
static NtSuspendProcessFn ntSuspend = [] {
|
||||
if (auto mod = ::GetModuleHandleW(L"ntdll.dll")) {
|
||||
return reinterpret_cast<NtSuspendProcessFn>(
|
||||
::GetProcAddress(mod, "NtSuspendProcess"));
|
||||
}
|
||||
return static_cast<NtSuspendProcessFn>(nullptr);
|
||||
}();
|
||||
|
||||
if (!ntSuspend) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE hProcess =
|
||||
::OpenProcess(PROCESS_SUSPEND_RESUME, FALSE, static_cast<DWORD>(pid));
|
||||
if (!hProcess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long status = ntSuspend(hProcess);
|
||||
::CloseHandle(hProcess);
|
||||
return status >= 0;
|
||||
#else
|
||||
return ::kill(static_cast<pid_t>(pid), SIGSTOP) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool rx::resumeProcess(ProcessId pid) {
|
||||
#ifdef _WIN32
|
||||
// Use NtResumeProcess from ntdll.dll
|
||||
using NtResumeProcessFn = long(__stdcall *)(HANDLE);
|
||||
static NtResumeProcessFn ntResume = [] {
|
||||
if (auto mod = ::GetModuleHandleW(L"ntdll.dll")) {
|
||||
return reinterpret_cast<NtResumeProcessFn>(
|
||||
::GetProcAddress(mod, "NtResumeProcess"));
|
||||
}
|
||||
return static_cast<NtResumeProcessFn>(nullptr);
|
||||
}();
|
||||
|
||||
if (!ntResume) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE hProcess =
|
||||
::OpenProcess(PROCESS_SUSPEND_RESUME, FALSE, static_cast<DWORD>(pid));
|
||||
if (!hProcess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long status = ntResume(hProcess);
|
||||
::CloseHandle(hProcess);
|
||||
return status >= 0;
|
||||
#else
|
||||
return ::kill(static_cast<pid_t>(pid), SIGCONT) == 0;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue