Process polling api

This commit is contained in:
Krzosa Karol
2024-08-07 07:31:37 +02:00
parent fe20d05e13
commit 7b8b8c751d
4 changed files with 204 additions and 60 deletions

View File

@@ -35,57 +35,6 @@ bool VDecommit(void *p, size_t size) {
}
// Basic end
Process RunEx(String args) {
Scratch scratch;
wchar_t *application_name = NULL;
wchar_t *cmd = ToWidechar(scratch, args);
BOOL inherit_handles = FALSE;
DWORD creation_flags = 0;
void *enviroment = NULL;
wchar_t *working_dir = NULL;
STARTUPINFOW startup_info = {};
startup_info.cb = sizeof(STARTUPINFOW);
Process result = {};
Assert(sizeof(result.platform) >= sizeof(PROCESS_INFORMATION));
PROCESS_INFORMATION *process_info = (PROCESS_INFORMATION *)result.platform;
BOOL success = CreateProcessW(application_name, cmd, NULL, NULL, inherit_handles, creation_flags, enviroment, working_dir, &startup_info, process_info);
result.is_valid = true;
if (!success) {
result.is_valid = false;
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
char *buff = (char *)lpMsgBuf;
size_t buffLen = strlen((const char *)buff);
if (buffLen > 0 && buff[buffLen - 1] == '\n') {
buff[buffLen - 1] = 0;
}
// @warning: leak! but we don't care
result.error_message = Format(GetSystemAllocator(), "Failed to create process | message: %s | cmd: %.*s", (char *)lpMsgBuf, FmtString(args));
LocalFree(lpMsgBuf);
}
return result;
}
int Wait(Process *process) {
Assert(process->is_valid);
PROCESS_INFORMATION *pi = (PROCESS_INFORMATION *)process->platform;
WaitForSingleObject(pi->hProcess, INFINITE);
DWORD exit_code;
BOOL err = GetExitCodeProcess(pi->hProcess, &exit_code);
Assert(err != 0);
CloseHandle(pi->hProcess);
CloseHandle(pi->hThread);
process[0] = {};
return (int)exit_code;
}
bool InitOS() {
SetConsoleOutputCP(CP_UTF8);
@@ -324,3 +273,179 @@ bool DeleteFile(String path) {
if (success == 0) result = false;
return result;
}
struct Win32Process {
HANDLE handle;
HANDLE child_stdout_read;
HANDLE child_stdout_write;
HANDLE child_stdin_read;
HANDLE child_stdin_write;
};
static_assert(sizeof(Win32Process) < sizeof(Process::platform));
static String Win32GetPlatformError(String cmd) {
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
char *buff = (char *)lpMsgBuf;
size_t buffLen = strlen((const char *)buff);
if (buffLen > 0 && buff[buffLen - 1] == '\n') {
buff[buffLen - 1] = 0;
}
// @warning: leak! but we don't care
String r = Format(GetSystemAllocator(), "Failed to create process using cmd: %.*s! %s", FmtString(cmd), (char *)lpMsgBuf);
LocalFree(lpMsgBuf);
return r;
}
static void Win32CloseProcess(Process *process) {
Win32Process *p = (Win32Process *)process->platform;
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
process->is_valid = false;
*p = {};
}
static void Win32ProcessError(Process *process, String cmd) {
Win32Process *p = (Win32Process *)process->platform;
process->error_message = Win32GetPlatformError(cmd);
Win32CloseProcess(process);
}
Process RunCmd(String command_line, String working_dir) {
Process process = {};
Win32Process *p = (Win32Process *)process.platform;
Scratch scratch;
command_line = Format(scratch, "/C %.*s", FmtString(command_line));
p->handle = INVALID_HANDLE_VALUE;
p->child_stdout_write = INVALID_HANDLE_VALUE;
p->child_stdout_read = INVALID_HANDLE_VALUE;
p->child_stdin_read = INVALID_HANDLE_VALUE;
p->child_stdin_write = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES security_atrb = {};
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
security_atrb.bInheritHandle = TRUE;
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
Win32ProcessError(&process, command_line);
return process;
}
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
Win32ProcessError(&process, command_line);
return process;
}
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
Win32ProcessError(&process, command_line);
return process;
}
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
Win32ProcessError(&process, command_line);
return process;
}
STARTUPINFOW startup = {};
startup.cb = sizeof(STARTUPINFO);
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
startup.hStdInput = p->child_stdin_read;
startup.hStdOutput = p->child_stdout_write;
startup.hStdError = p->child_stdout_write;
startup.wShowWindow = SW_HIDE;
String16 cwd = ToString16(scratch, working_dir);
String16 cmd = ToString16(scratch, command_line);
void *env = NULL;
PROCESS_INFORMATION info = {};
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", cmd.data, 0, 0, TRUE, 0, env, cwd.data, &startup, &info)) {
Win32ProcessError(&process, command_line);
return process;
}
// Close handles to the stdin and stdout pipes no longer needed by the child process.
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
CloseHandle(info.hThread);
CloseHandle(p->child_stdin_read);
CloseHandle(p->child_stdout_write);
p->child_stdin_read = INVALID_HANDLE_VALUE;
p->child_stdout_write = INVALID_HANDLE_VALUE;
p->handle = info.hProcess;
process.is_valid = true;
return process;
}
bool WaitForExit(Process *process) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
if (WaitForSingleObject(p->handle, INFINITE) != WAIT_OBJECT_0) {
Assert(!"Invalid codepath");
return false;
}
DWORD exit_code;
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
if (get_exit_code_failed) {
exit_code = -1;
}
process->exit_code = (int)exit_code;
Win32CloseProcess(process);
return true;
}
bool PollExitCode(Process *process) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
return false;
}
DWORD exit_code;
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
if (get_exit_code_failed) {
exit_code = -1;
}
process->exit_code = (int)exit_code;
Win32CloseProcess(process);
return true;
}
void KillProcess(Process *process) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
if (terminate_process_error) {
Assert(0);
}
Win32CloseProcess(process);
process->exit_code = -1;
}
StdoutPollInfo PollStdout(Process *process, char *buffer, int64_t buffer_size) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
DWORD bytes_read = 0;
DWORD bytes_avail = 0;
bool peek_error = PeekNamedPipe(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, &bytes_avail, NULL) == 0;
StdoutPollInfo result = {bytes_read, bytes_avail};
return result;
}