Browse Source

Implemented streams for Windows. Tested isDone() and terminate(). Started testing streams.

git-svn-id: https://svn.microneil.com/svn/CodeDweller/branches/adeniz_1@31 d34b734f-a00e-4b39-a726-e4eeb87269ab
adeniz_1
adeniz 10 years ago
parent
commit
f5c9822cd4
2 changed files with 379 additions and 45 deletions
  1. 244
    28
      child.cpp
  2. 135
    17
      child.hpp

+ 244
- 28
child.cpp View File

//============================================================================== //==============================================================================
#include <iostream> // Temporary. #include <iostream> // Temporary.
#include <stdexcept> #include <stdexcept>
#include "child.hpp" #include "child.hpp"
namespace CodeDweller { namespace CodeDweller {
Child::Child(std::vector<std::string> args) {
Child::Child(std::vector<std::string> args, size_t bufSize) :
readStreambuf(bufSize),
writeStreambuf(bufSize),
reader(&readStreambuf),
writer(&writeStreambuf) {
init(); init();
cmdline += args.back(); // Append last command-line argument. cmdline += args.back(); // Append last command-line argument.
} }
Child::Child(std::string childpath) :
Child::Child(std::string childpath, size_t bufSize) :
readStreambuf(bufSize),
writeStreambuf(bufSize),
reader(&readStreambuf),
writer(&writeStreambuf),
cmdline(childpath) { cmdline(childpath) {
init(); init();
} }
Child::~Child() { Child::~Child() {
// Close handles.
} }
void void
Child::init() { Child::init() {
childStarted = false;
exitCodeObtainedFlag = false; exitCodeObtainedFlag = false;
exitCode = 0; exitCode = 0;
} }
std::istream *
Child::reader() {
return new std::istream(std::cin.rdbuf());
}
std::ostream *
Child::writer() {
return new std::ostream(std::cout.rdbuf());
}
void void
Child::run() { Child::run() {
if (childStarted) {
throw std::logic_error("Child process was active when "
"run() was called");
}
// Set the bInheritHandle flag so pipe handles are inherited. // Set the bInheritHandle flag so pipe handles are inherited.
SECURITY_ATTRIBUTES securityAttributes; SECURITY_ATTRIBUTES securityAttributes;
// Create a pipe for the child process's STDOUT. // Create a pipe for the child process's STDOUT.
HANDLE childStdOutAtChild; HANDLE childStdOutAtChild;
HANDLE childStdOutAtParent;
HANDLE childStdInAtChild; HANDLE childStdInAtChild;
HANDLE childStdInAtParent;
int bufferSize = 0; int bufferSize = 0;
if (!CreatePipe(&childStdOutAtParent, if (!CreatePipe(&childStdOutAtParent,
startInfo.dwFlags |= STARTF_USESTDHANDLES; startInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process. // Create the child process.
bool createSuccess;
bool status;
createSuccess = CreateProcess(NULL,
(char *) cmdline.c_str(), // 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
&startInfo, // STARTUPINFO pointer
&processInfo); // receives PROCESS_INFORMATION
status = CreateProcess(NULL,
(char *) cmdline.c_str(), // 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
&startInfo, // STARTUPINFO pointer
&processInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application. // If an error occurs, exit the application.
if (!createSuccess ) {
if (!status ) {
throw std::runtime_error("Error from CreateProcess with " throw std::runtime_error("Error from CreateProcess with "
"command line \"" + cmdline + "\": " + "command line \"" + cmdline + "\": " +
getErrorText()); getErrorText());
} }
// Provide the stream buffers with the handles for communicating
// with the child process.
readStreambuf.setInputHandle(childStdOutAtParent);
writeStreambuf.setOutputHandle(childStdInAtParent);
// Save the handles to the child process and its primary thread. // Save the handles to the child process and its primary thread.
childProcess = processInfo.hProcess; childProcess = processInfo.hProcess;
childThread = processInfo.hThread; childThread = processInfo.hThread;
childStarted = true;
// Close the child's end of the pipes. // Close the child's end of the pipes.
CloseHandle(childStdOutAtChild);
CloseHandle(childStdInAtChild);
if (!CloseHandle(childStdOutAtChild)) {
throw std::runtime_error("Error closing the child process stdout handle: " +
getErrorText());
}
if (!CloseHandle(childStdInAtChild)) {
throw std::runtime_error("Error closing the child process stdin handle: " +
getErrorText());
}
} }
void void
Child::terminate() { Child::terminate() {
if (isDone()) {
return;
}
if (!TerminateProcess(childProcess, terminateExitCode)) { if (!TerminateProcess(childProcess, terminateExitCode)) {
throw std::runtime_error("Error terminating the child process: " + throw std::runtime_error("Error terminating the child process: " +
getErrorText()); getErrorText());
} }
bool
Child::isDone() {
if (exitCodeObtainedFlag) {
return true;
}
if (!childStarted) {
throw std::logic_error("Child process was not started "
"when isDone() was called");
}
int result;
if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
throw std::runtime_error("Error checking status of child process: " +
getErrorText());
}
if (STILL_ACTIVE == result) {
return false;
}
// Child process has exited. Save the exit code.
exitCode = result;
exitCodeObtainedFlag = true;
return true;
}
int32_t int32_t
Child::result() { Child::result() {
if (exitCodeObtainedFlag) {
return exitCode;
}
if (!childStarted) {
throw std::logic_error("Child process was not started "
"when result() was called");
}
int result; int result;
if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) { if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
getErrorText()); getErrorText());
} }
// Error if the process has not exited.
if (STILL_ACTIVE == result) {
throw std::logic_error("Child process was active when "
"result() was called");
}
// Child process has exited. Save the exit code.
exitCode = result;
exitCodeObtainedFlag = true;
return result; return result;
} }
return errMsg; return errMsg;
} }
Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
inputHandle(0),
buffer(bufferSize + 1) {
char *end = &(buffer.front()) + buffer.size();
// Indicate to underflow that underflow has not been called.
setg(end, end, end);
}
void
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
inputHandle = inHandle;
}
std::streambuf::int_type
Child::ReadStreambuf::underflow() {
// Check for empty buffer.
if (gptr() < egptr()) {
// Not empty.
return traits_type::to_int_type(*gptr());
}
// Need to fill the buffer.
char *base = &(buffer.front());
char *start = base;
// Check whether this is the first fill.
if (eback() == base) {
// Not the first fill. Copy putback characters.
}
// start points to the start of the buffer. Fill buffer.
DWORD nBytesRead;
if (!ReadFile(inputHandle,
start,
buffer.size() - (start - base),
&nBytesRead,
NULL)) {
return traits_type::eof();
}
// Check for EOF.
if (0 == nBytesRead) {
return traits_type::eof();
}
// Update buffer pointers.
setg(base, start, start + nBytesRead);
return traits_type::to_int_type(*gptr());
}
Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
outputHandle(0),
buffer(bufferSize + 1) {
char *base = &(buffer.front());
// Indicate to overflow that overflow has not been called.
setp(base, base + buffer.size() - 1);
}
void
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
outputHandle = outHandle;
}
void
Child::WriteStreambuf::flushBuffer() {
// Write.
std::ptrdiff_t nBytes = pptr() - pbase();
DWORD nBytesWritten;
if (!WriteFile(outputHandle,
pbase(),
nBytes,
&nBytesWritten,
NULL)) {
throw std::runtime_error("Error writing to child process: " +
getErrorText());
}
if (nBytes != nBytesWritten) {
throw std::runtime_error("Not all data was written to to child process: " +
getErrorText());
}
pbump(-nBytes);
return;
}
std::streambuf::int_type
Child::WriteStreambuf::overflow(int_type ch) {
// Check whether we're writing EOF.
if (traits_type::eof() != ch) {
// Not writing EOF.
*(pptr()) = ch;
pbump(1);
}
// Write.
flushBuffer();
// Success.
return traits_type::not_eof('a');
}
int
Child::WriteStreambuf::sync() {
flushBuffer();
return 1; // Success.
}
} }

+ 135
- 17
child.hpp View File

#include <windows.h> #include <windows.h>


#include <cstdint> #include <cstdint>
#include <streambuf>
#include <istream> #include <istream>
#include <ostream> #include <ostream>
#include <string> #include <string>


class Child { class Child {


private:

/// Streambuf class for reading the standard output of the child
/// process.
class ReadStreambuf : public std::streambuf {

public:

/// Reader streambuf constructor.
//
// \param[in] bufferSize is the size in bytes of the input
// buffer.
//
explicit ReadStreambuf(std::size_t bufferSize = 4096);

/// Set the handle to read the standard output of the child
/// process.
//
// \param[in] inHandle is the input handle for the standard
// output of the child process.
//
void setInputHandle(HANDLE inHandle);

private:

/// Override streambuf::underflow().
int_type underflow();

/// Copy constructor not implemented.
ReadStreambuf(const ReadStreambuf &);

/// Copy constructor not implemented.
ReadStreambuf &operator=(const ReadStreambuf &);

/// Input handle.
HANDLE inputHandle;

/// Read buffer.
std::vector<char> buffer;

};

/// Streambuf class for writing to the standard input of the child
/// process.
class WriteStreambuf : public std::streambuf {

public:

/// Writeer streambuf constructor.
//
// \param[in] bufferSize is the size in bytes of the input
// buffer.
//
explicit WriteStreambuf(std::size_t bufferSize = 4096);

/// Set the handle to write the standard input of the child
/// process.
//
// \param[in] outHandle is the output handle for the standard
// input of the child process.
//
void setOutputHandle(HANDLE outHandle);

private:

/// Flush the output buffer.
void flushBuffer();

/// Override streambuf::overflow().
int_type overflow(int_type ch);

/// Override streambuf::sync().
int sync();

/// Copy constructor not implemented.
WriteStreambuf(const WriteStreambuf &);

/// Copy constructor not implemented.
WriteStreambuf &operator=(const WriteStreambuf &);

/// Output handle.
HANDLE outputHandle;

/// Write buffer.
std::vector<char> buffer;

};

/// Stream buffer for reading to the stdout of the child process;
ReadStreambuf readStreambuf;

/// Stream buffer for writing to the stdin of the child process;
WriteStreambuf writeStreambuf;

public: public:


/** Constructor for spawning with command-line parameters. /** Constructor for spawning with command-line parameters.
executable, and args[1] thru args[n] are the command-line executable, and args[1] thru args[n] are the command-line
parameters. parameters.


\param[in] bufSize is the buffer size of the reader and writer
streams used to communicate with the child process.

*/ */
Child(std::vector<std::string> args);
Child(std::vector<std::string> args, size_t bufSize = 4096);


/** Constructor for spawning without command-line parameters. /** Constructor for spawning without command-line parameters.




\param[in] childpath contains the child executable file name. \param[in] childpath contains the child executable file name.


\param[in] bufSize is the buffer size of the reader and writer
streams used to communicate with the child process.

*/ */
Child(std::string childpath);
Child(std::string childpath, size_t bufSize = 4096);


/** Destructor terminates the child process. */ /** Destructor terminates the child process. */
~Child(); ~Child();


/// Return a stream that is seen by the child as standard output.
std::istream *reader();
/// Input stream to read data from the child's standard output.
std::istream reader;


/// Return a stream that is seen by the child as standard input.
std::ostream *writer();
/// Output stream to write data to the child's standard input.
std::ostream writer;


/** Spawn the child process. /** Spawn the child process.


If an error occurs, an exception is thrown.
\throws runtime_error if an error occurs.


*/ */
void run(); void run();


/** Terminite the child process. /** Terminite the child process.


If an error occurs, an exception is thrown.
\throws runtime_error if an error occurs.

\throws logic_error if the child process is not running.


*/ */
void terminate(); void terminate();


/** Check whether the child process has exited.

\returns True if the child process has exited, false
otherwise.

\throws runtime_error if an error occurs.

\throws logic_error if the child process is not running.

*/
bool isDone();

/** Get the exit value of the child process. /** Get the exit value of the child process.


\returns The exit value of the child process if the child \returns The exit value of the child process if the child
process has exited. If the child process has not exited, an
exception is thrown.
process has exited.

\throws runtime_error if an error occurs.

\throws logic_error if the child process has not exited.

\throws logic_error if the child process is not running.


*/ */
int32_t result(); int32_t result();
/// Exit code to use when terminating the child process. /// Exit code to use when terminating the child process.
static const uint32_t terminateExitCode = 0; static const uint32_t terminateExitCode = 0;


/// True if the child process was successfully started.
bool childStarted;

/// Initialize data members. /// Initialize data members.
void init(); void init();


/// Child executable path and command-line parameters. /// Child executable path and command-line parameters.
std::string cmdline; std::string cmdline;


/// Parent's read handle for child process standard output.
HANDLE childStdOutAtParent;

/// Parent's write handle for child process standard input.
HANDLE childStdInAtParent;

/// Child's process handle. /// Child's process handle.
HANDLE childProcess; HANDLE childProcess;


// //
// \returns Human-readable description of the most recent error. // \returns Human-readable description of the most recent error.
// //
std::string getErrorText();
static std::string getErrorText();

}; };


} }

Loading…
Cancel
Save