reading process stdout/stderr for Windows
This commit is contained in:
parent
c06402155e
commit
4c898662e0
|
|
@ -964,6 +964,9 @@ typedef enum {
|
||||||
// Process info
|
// Process info
|
||||||
typedef struct Process {
|
typedef struct Process {
|
||||||
int pid; // Process unique identifier
|
int pid; // Process unique identifier
|
||||||
|
|
||||||
|
void *pipeStdoutRead; // Process stdout and stderr read pipe [Windows only]
|
||||||
|
void *pipeStdinWrite; // Process stdin write pipe [Windows only]
|
||||||
} Process;
|
} Process;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
@ -1261,6 +1264,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t
|
||||||
RLAPI Process InitProcess(const char *command, char *const args[]); // Initialize a new process, returns a Process struct
|
RLAPI Process InitProcess(const char *command, char *const args[]); // Initialize a new process, returns a Process struct
|
||||||
RLAPI ProcessInfo CheckProcess(Process process); // Check if a process is still running
|
RLAPI ProcessInfo CheckProcess(Process process); // Check if a process is still running
|
||||||
RLAPI int WaitProcess(Process process); // Wait for process to finish, returns exit code, blocking
|
RLAPI int WaitProcess(Process process); // Wait for process to finish, returns exit code, blocking
|
||||||
|
RLAPI const char *ReadProcessOutput(Process process, int *length); // Read process output as raw buffer (not null-terminated), returns pointer to a static buffer, so data must be copied before next call to ReadProcessOutput()
|
||||||
RLAPI void PauseProcess(Process process); // Pause process execution
|
RLAPI void PauseProcess(Process process); // Pause process execution
|
||||||
RLAPI void ResumeProcess(Process process); // Resume paused process execution
|
RLAPI void ResumeProcess(Process process); // Resume paused process execution
|
||||||
RLAPI void CloseProcess(Process process); // Close process and free resources
|
RLAPI void CloseProcess(Process process); // Close process and free resources
|
||||||
|
|
|
||||||
107
src/rcore.c
107
src/rcore.c
|
|
@ -233,6 +233,12 @@
|
||||||
void *hStdError;
|
void *hStdError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct _SECURITY_ATTRIBUTES {
|
||||||
|
unsigned long nLength;
|
||||||
|
void *lpSecurityDescriptor;
|
||||||
|
int bInheritHandle;
|
||||||
|
};
|
||||||
|
|
||||||
struct _PROCESS_INFORMATION {
|
struct _PROCESS_INFORMATION {
|
||||||
void *hProcess;
|
void *hProcess;
|
||||||
void *hThread;
|
void *hThread;
|
||||||
|
|
@ -260,6 +266,17 @@
|
||||||
const char *lpCurrentDirectory,
|
const char *lpCurrentDirectory,
|
||||||
struct _STARTUPINFOA *lpStartupInfo,
|
struct _STARTUPINFOA *lpStartupInfo,
|
||||||
struct _PROCESS_INFORMATION *lpProcessInformation);
|
struct _PROCESS_INFORMATION *lpProcessInformation);
|
||||||
|
__declspec(dllimport) int __stdcall CreatePipe(
|
||||||
|
void **hReadPipe,
|
||||||
|
void **hWritePipe,
|
||||||
|
struct _SECURITY_ATTRIBUTES *lpPipeAttributes,
|
||||||
|
unsigned long nSize);
|
||||||
|
__declspec(dllimport) int __stdcall ReadFile(
|
||||||
|
void *hFile,
|
||||||
|
void *lpBuffer,
|
||||||
|
unsigned long nNumberOfBytesToRead,
|
||||||
|
unsigned long *lpNumberOfBytesRead,
|
||||||
|
void *lpOverlapped);
|
||||||
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
|
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
|
||||||
#include <sys/wait.h> // Required for: waitpid() [Used in CheckProcess()]
|
#include <sys/wait.h> // Required for: waitpid() [Used in CheckProcess()]
|
||||||
#include <signal.h> // Required for: kill() [Used in PauseProcess(), ResumeProcess(), CloseProcess()]
|
#include <signal.h> // Required for: kill() [Used in PauseProcess(), ResumeProcess(), CloseProcess()]
|
||||||
|
|
@ -4271,6 +4288,22 @@ int GetTouchPointCount(void)
|
||||||
// Module Functions Definition: Process Execution
|
// Module Functions Definition: Process Execution
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
static bool CreatePipeWindows(void** readPipe, void** writePipe) {
|
||||||
|
struct _SECURITY_ATTRIBUTES sa = { 0 };
|
||||||
|
sa.nLength = sizeof(struct _SECURITY_ATTRIBUTES);
|
||||||
|
sa.bInheritHandle = 1;
|
||||||
|
sa.lpSecurityDescriptor = NULL;
|
||||||
|
return CreatePipe(readPipe, writePipe, &sa, 0) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CloseAllPipesWindows(void** pipes, int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (pipes[i] != NULL) CloseHandle(pipes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Initialize a new process, returns a Process struct
|
// Initialize a new process, returns a Process struct
|
||||||
RLAPI Process InitProcess(const char *command, char *const args[])
|
RLAPI Process InitProcess(const char *command, char *const args[])
|
||||||
{
|
{
|
||||||
|
|
@ -4301,16 +4334,44 @@ RLAPI Process InitProcess(const char *command, char *const args[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For stdout/stderr, read handles reserved for Raylib side, write handles passed to child process
|
||||||
|
// For stdin, write handle reserved for Raylib side, read handle passed to child process
|
||||||
|
// NOTE: stdout and stderr are merged into a single pipe
|
||||||
|
void *pipeStdoutRead, *pipeStdoutWrite;
|
||||||
|
void *pipeStdinRead, *pipeStdinWrite;
|
||||||
|
|
||||||
|
if ((!CreatePipeWindows(&pipeStdoutRead, &pipeStdoutWrite)) ||
|
||||||
|
(!CreatePipeWindows(&pipeStdinRead, &pipeStdinWrite)))
|
||||||
|
{
|
||||||
|
TRACELOG(LOG_WARNING, "PROCESS: Failed to create pipes for process I/O redirection");
|
||||||
|
void *pipes[] = { pipeStdoutRead, pipeStdoutWrite, pipeStdinRead, pipeStdinWrite };
|
||||||
|
CloseAllPipesWindows(pipes, sizeof(pipes) / sizeof(pipes[0]));
|
||||||
|
return process;
|
||||||
|
}
|
||||||
|
|
||||||
struct _STARTUPINFOA si = { 0 };
|
struct _STARTUPINFOA si = { 0 };
|
||||||
si.cb = sizeof(struct _STARTUPINFOA);
|
si.cb = sizeof(struct _STARTUPINFOA);
|
||||||
|
// STARTF_USESTDHANDLES
|
||||||
|
si.dwFlags = 0x00000100;
|
||||||
|
si.hStdError = pipeStdoutWrite;
|
||||||
|
si.hStdOutput = pipeStdoutWrite;
|
||||||
|
si.hStdInput = pipeStdinRead;
|
||||||
struct _PROCESS_INFORMATION pi = { 0 };
|
struct _PROCESS_INFORMATION pi = { 0 };
|
||||||
|
|
||||||
// Application name is specified as the first command line argument, so pass NULL to CreateProcessA here
|
// Application name is specified as the first command line argument, so pass NULL to CreateProcessA here
|
||||||
if (CreateProcessA(NULL, cmdLine, NULL, NULL, 0, 0, NULL, NULL, &si, &pi))
|
// Flag bInheritHandles is set to TRUE, so child process will have access to the pipes
|
||||||
|
if (CreateProcessA(NULL, cmdLine, NULL, NULL, 1, 0, NULL, NULL, &si, &pi))
|
||||||
{
|
{
|
||||||
process.pid = (int)pi.dwProcessId;
|
process.pid = (int)pi.dwProcessId;
|
||||||
CloseHandle(pi.hThread);
|
CloseHandle(pi.hThread);
|
||||||
CloseHandle(pi.hProcess);
|
CloseHandle(pi.hProcess);
|
||||||
|
|
||||||
|
process.pipeStdoutRead = pipeStdoutRead;
|
||||||
|
process.pipeStdinWrite = pipeStdinWrite;
|
||||||
|
|
||||||
|
// Free parent process handles
|
||||||
|
CloseHandle(pipeStdoutWrite);
|
||||||
|
CloseHandle(pipeStdinRead);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -4337,11 +4398,12 @@ RLAPI Process InitProcess(const char *command, char *const args[])
|
||||||
{
|
{
|
||||||
process.pid = pid;
|
process.pid = pid;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
TRACELOG(LOG_WARNING, "PROCESS: Process management not supported on this platform");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return process;
|
return process;
|
||||||
|
#else
|
||||||
|
TRACELOG(LOG_WARNING, "PROCESS: Process management not supported on this platform");
|
||||||
|
return process;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a process is still running, updates Process struct
|
// Check if a process is still running, updates Process struct
|
||||||
|
|
@ -4446,6 +4508,7 @@ RLAPI int WaitProcess(Process process)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait indefinitely for process termination
|
// Wait indefinitely for process termination
|
||||||
|
// NOTE: calling WaitProcess while trying to read process output may cause deadlock
|
||||||
// INFINITE
|
// INFINITE
|
||||||
WaitForSingleObject(hProcess, 0xFFFFFFFF);
|
WaitForSingleObject(hProcess, 0xFFFFFFFF);
|
||||||
|
|
||||||
|
|
@ -4487,6 +4550,38 @@ RLAPI int WaitProcess(Process process)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RLAPI const char *ReadProcessOutput(Process process, int *length)
|
||||||
|
{
|
||||||
|
static char output[4096] = { 0 };
|
||||||
|
|
||||||
|
if (process.pid <= 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
// Read from output pipe
|
||||||
|
// NOTE: This will read whatever in the pipe buffer
|
||||||
|
|
||||||
|
unsigned long bytesRead = 0;
|
||||||
|
if (!ReadFile(process.pipeStdoutRead, output, sizeof(output), &bytesRead, NULL))
|
||||||
|
{
|
||||||
|
*length = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*length = (int)bytesRead;
|
||||||
|
return output;
|
||||||
|
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
|
||||||
|
TRACELOG(LOG_WARNING, "PROCESS: Reading process output not supported on this platform");
|
||||||
|
*length = 0;
|
||||||
|
return NULL;
|
||||||
|
#else
|
||||||
|
TRACELOG(LOG_WARNING, "PROCESS: Process management not supported on this platform");
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Pause process execution
|
// Pause process execution
|
||||||
RLAPI void PauseProcess(Process process)
|
RLAPI void PauseProcess(Process process)
|
||||||
{
|
{
|
||||||
|
|
@ -4608,6 +4703,10 @@ RLAPI void CloseProcess(Process process)
|
||||||
TerminateProcess(hProcess, 0);
|
TerminateProcess(hProcess, 0);
|
||||||
CloseHandle(hProcess);
|
CloseHandle(hProcess);
|
||||||
|
|
||||||
|
// Clean up pipes
|
||||||
|
void* pipes[] = { process.pipeStdoutRead, process.pipeStdinWrite };
|
||||||
|
CloseAllPipesWindows(pipes, sizeof(pipes) / sizeof(pipes[0]));
|
||||||
|
|
||||||
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
|
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
|
||||||
// If process is not running or not found, nothing to do
|
// If process is not running or not found, nothing to do
|
||||||
ProcessInfo info = CheckProcess(process);
|
ProcessInfo info = CheckProcess(process);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user