Capture Output of Spawned Process to String

Capture Output of Spawned Process to string

Eddy Luten's answer led me in a good direction, but the MSDN documentation (while elaborate) had some issues. Mainly, you need to ensure you close all handles you don't use. Also it just has code it expects the user to understand.

So instead, here's my wall of code I expect people to just understand :D

#include <string>
#include <iostream>
#include <windows.h>
#include <stdio.h>
#pragma warning( disable : 4800 ) // stupid warning about bool
#define BUFSIZE 4096
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_ERR_Rd = NULL;
HANDLE g_hChildStd_ERR_Wr = NULL;

PROCESS_INFORMATION CreateChildProcess(void);
void ReadFromPipe(PROCESS_INFORMATION);

int main(int argc, char *argv[]){
SECURITY_ATTRIBUTES sa;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDERR.
if ( ! CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0) ) {
exit(1);
}
// Ensure the read handle to the pipe for STDERR is not inherited.
if ( ! SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0) ){
exit(1);
}
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0) ) {
exit(1);
}
// Ensure the read handle to the pipe for STDOUT is not inherited
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ){
exit(1);
}
// Create the child process.
PROCESS_INFORMATION piProcInfo = CreateChildProcess();

// Read from pipe that is the standard output for child process.
printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
ReadFromPipe(piProcInfo);

printf("\n->End of parent execution.\n");

// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application,
// close handles explicitly.
return 0;
}

// Create a child process that uses the previously created pipes
// for STDERR and STDOUT.
PROCESS_INFORMATION CreateChildProcess(){
// Set the text I want to run
char szCmdline[]="test --log_level=all --report_level=detailed";
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
bool bSuccess = FALSE;

// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

// Set up members of the STARTUPINFO structure.
// This structure specifies the STDERR and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_ERR_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
CloseHandle(g_hChildStd_ERR_Wr);
CloseHandle(g_hChildStd_OUT_Wr);
// If an error occurs, exit the application.
if ( ! bSuccess ) {
exit(1);
}
return piProcInfo;
}

// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
void ReadFromPipe(PROCESS_INFORMATION piProcInfo) {
DWORD dwRead;
CHAR chBuf[BUFSIZE];
bool bSuccess = FALSE;
std::string out = "", err = "";
for (;;) {
bSuccess=ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;

std::string s(chBuf, dwRead);
out += s;
}
dwRead = 0;
for (;;) {
bSuccess=ReadFile( g_hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;

std::string s(chBuf, dwRead);
err += s;

}
std::cout << "stdout:" << out << std::endl;
std::cout << "stderr:" << err << std::endl;
}

How to capture a colored spawned process output and send to the browser via nodejs?

So it looks like I found an answer here. `${data}` was implicitly converting the the data returned from the spawned process (I believe by doing type conversion implicitly calling toString() but I could be wrong here).

So to correctly pass the data to ansi-to-html, you must just pass it directly

  process.stdout.on('data', (data) => {
self.terminalOutput += convert.toHtml(data);
});

process.stderr.on('data', (data) => {
self.terminalOutput += convert.toHtml(data);
});

How do I get each child process to output a string?

Below is a basic example of starting a child process and passing it an argument via the command line.

I'll leave the remainder of your homework for you to figure out.

Also note I'm using some C++17 features here, so if you can't compile either set the language standard in Visual Studio to C++17 or change the code to compile for the version of standard you're using.

Parent Process

#include <iostream>
#include <string>
#include <chrono>
#include <Windows.h>

int main( )
{
std::cout << "Opening processes\n";

for( int i = 0; i < 10; i++ )
{
STARTUPINFO startup_info{ };
PROCESS_INFORMATION process_info{ };

// To pass arguments to the child process replace 'test'
// with whatever argument you want to pass.
std::wstring args{ L"Child.exe test" };

// create child process
if( !CreateProcessW(
LR"(C:\Path\Child.exe)", // use command line
args.data( ),
nullptr, // don't inherit process handle
nullptr, // don't inherit thread handle
false, // disable handle inheritance
0, // no creation flags
nullptr, // use parent's environment block
nullptr, // use parent's existing directory
&startup_info,
&process_info ) )
{
std::error_code ec{ static_cast<int>(
GetLastError( ) ), std::system_category( ) };

std::cerr << "Failed to start process: " << ec.message( ) << '\n';
return -1;
}

std::cout << "Waiting for process to complete\n";

auto start{ std::chrono::high_resolution_clock::now( ) };

if( auto code{ WaitForSingleObject(
process_info.hProcess, INFINITE ) }; code != WAIT_OBJECT_0 )
{
std::error_code ec{ static_cast<int>(
GetLastError( ) ), std::system_category( ) };

std::cerr << "Failed to wait for process: " << ec.message( ) << '\n';
}

auto elapsed{ std::chrono::high_resolution_clock::now( ) - start };
auto duration{ std::chrono::duration_cast<std::chrono::microseconds>( elapsed ) };

std::cout << "Time: " << duration.count( ) << " us\n";

CloseHandle( process_info.hProcess );
CloseHandle( process_info.hThread );
}
}

Child Process

#include <iostream>

int main( int argc, char* argv[ ] )
{
if( argc > 1 )
{
// The argument we passed will be located at index
// 1 in argv.
auto arg{ argv[ 1 ] };
// Prints 'test'.
std::cout << "Argument: " << arg << '\n';
}
}

Redirecting stdout/stderr of spawn() to a string in Ruby

Why do you need spawn? Unless you are on Windows you can use popen*, e.g. popen4:

require "open4"

pid, p_i, p_o, p_e = Open4.popen4("ls")
p_i.close
o, e = [p_o, p_e].map { |p| begin p.read ensure p.close end }
s = Process::waitpid2(pid).last

How to get the output of a spawned child_process in Node.JS?

You can do it something like below.

    var spawn = require('child_process').spawn;
// Create a child process
var child = spawn('ls' , ['-l']);

child.stdout.on('data',
function (data) {
console.log('ls command output: ' + data);
});
child.stderr.on('data', function (data) {
//throw errors
console.log('stderr: ' + data);
});

child.on('close', function (code) {
console.log('child process exited with code ' + code);
});

Update: with spawnSync

    var spawn = require('child_process').spawnSync;
var child = spawn('ls' , ['-l','/usr']);
console.log('stdout here: \n' + child.stdout);

CreateProcess and capture stdout

The short answer is to create an anonymous pipe, setting the hStdOut/hStdErr and dwFlag members of the STARTUPINFO structure accordingly, and have CreateProcess() inherit the handle for the writing end of the pipe. Don't forget to close your writing handle of your pipe, then you can read from the reading handle of the pipe in a loop until it fails with an ERROR_BROKEN_PIPE error.

MSDN provides a detailed example of this:

Creating a Child Process with Redirected Input and Output

You are not the first person to do this, there should be plenty of example code and duplicate questions here on StackOverflow.

Node.js spawn child process and get terminal output live

I'm still getting my feet wet with Node.js, but I have a few ideas. first, I believe you need to use execFile instead of spawn; execFile is for when you have the path to a script, whereas spawn is for executing a well-known command that Node.js can resolve against your system path.

1. Provide a callback to process the buffered output:

var child = require('child_process').execFile('path/to/script', [ 
'arg1', 'arg2', 'arg3',
], function(err, stdout, stderr) {
// Node.js will invoke this callback when process terminates.
console.log(stdout);
});

2. Add a listener to the child process' stdout stream (9thport.net)

var child = require('child_process').execFile('path/to/script', [ 
'arg1', 'arg2', 'arg3' ]);
// use event hooks to provide a callback to execute when data are available:
child.stdout.on('data', function(data) {
console.log(data.toString());
});

Further, there appear to be options whereby you can detach the spawned process from Node's controlling terminal, which would allow it to run asynchronously. I haven't tested this yet, but there are examples in the API docs that go something like this:

child = require('child_process').execFile('path/to/script', [ 
'arg1', 'arg2', 'arg3',
], {
// detachment and ignored stdin are the key here:
detached: true,
stdio: [ 'ignore', 1, 2 ]
});
// and unref() somehow disentangles the child's event loop from the parent's:
child.unref();
child.stdout.on('data', function(data) {
console.log(data.toString());
});


Related Topics



Leave a reply



Submit