|
|
@@ -63,6 +63,8 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
void
|
|
|
|
Child::init() {
|
|
|
|
reader.exceptions(std::istream::failbit | std::istream::badbit);
|
|
|
|
writer.exceptions(std::ostream::failbit | std::ostream::badbit);
|
|
|
|
childStarted = false;
|
|
|
|
exitCodeObtainedFlag = false;
|
|
|
|
exitCode = 0;
|
|
|
@@ -77,112 +79,112 @@ namespace CodeDweller { |
|
|
|
}
|
|
|
|
|
|
|
|
// Set the bInheritHandle flag so pipe handles are inherited.
|
|
|
|
SECURITY_ATTRIBUTES securityAttributes;
|
|
|
|
|
|
|
|
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
securityAttributes.bInheritHandle = true;
|
|
|
|
securityAttributes.lpSecurityDescriptor = NULL;
|
|
|
|
|
|
|
|
// Create a pipe for the child process's STDOUT.
|
|
|
|
HANDLE childStdOutAtChild;
|
|
|
|
HANDLE childStdOutAtParent;
|
|
|
|
HANDLE childStdInAtChild;
|
|
|
|
HANDLE childStdInAtParent;
|
|
|
|
int bufferSize = 0;
|
|
|
|
|
|
|
|
if (!CreatePipe(&childStdOutAtParent,
|
|
|
|
&childStdOutAtChild,
|
|
|
|
&securityAttributes,
|
|
|
|
bufferSize)) {
|
|
|
|
throw std::runtime_error("Error from CreatePipe for stdout: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the read handle to the pipe for STDOUT is not inherited.
|
|
|
|
int inheritFlag = 0;
|
|
|
|
if (!SetHandleInformation(childStdOutAtParent,
|
|
|
|
HANDLE_FLAG_INHERIT,
|
|
|
|
inheritFlag) ) {
|
|
|
|
throw std::runtime_error("Error from GetHandleInformation for stdout: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a pipe for the child process's STDIN.
|
|
|
|
if (! CreatePipe(&childStdInAtChild,
|
|
|
|
&childStdInAtParent,
|
|
|
|
SECURITY_ATTRIBUTES securityAttributes;
|
|
|
|
|
|
|
|
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
securityAttributes.bInheritHandle = true;
|
|
|
|
securityAttributes.lpSecurityDescriptor = NULL;
|
|
|
|
|
|
|
|
// Create a pipe for the child process's STDOUT.
|
|
|
|
HANDLE childStdOutAtChild;
|
|
|
|
HANDLE childStdOutAtParent;
|
|
|
|
HANDLE childStdInAtChild;
|
|
|
|
HANDLE childStdInAtParent;
|
|
|
|
int bufferSize = 0;
|
|
|
|
|
|
|
|
if (!CreatePipe(&childStdOutAtParent,
|
|
|
|
&childStdOutAtChild,
|
|
|
|
&securityAttributes,
|
|
|
|
bufferSize)) {
|
|
|
|
throw std::runtime_error("Error from CreatePipe for stdin: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the write handle to the pipe for STDIN is not inherited.
|
|
|
|
if (!SetHandleInformation(childStdInAtParent,
|
|
|
|
HANDLE_FLAG_INHERIT,
|
|
|
|
inheritFlag)) {
|
|
|
|
throw std::runtime_error("Error from GetHandleInformation for stdin: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
throw std::runtime_error("Error from CreatePipe for stdout: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the read handle to the pipe for STDOUT is not inherited.
|
|
|
|
int inheritFlag = 0;
|
|
|
|
if (!SetHandleInformation(childStdOutAtParent,
|
|
|
|
HANDLE_FLAG_INHERIT,
|
|
|
|
inheritFlag) ) {
|
|
|
|
throw std::runtime_error("Error from GetHandleInformation for stdout: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a pipe for the child process's STDIN.
|
|
|
|
if (! CreatePipe(&childStdInAtChild,
|
|
|
|
&childStdInAtParent,
|
|
|
|
&securityAttributes,
|
|
|
|
bufferSize)) {
|
|
|
|
throw std::runtime_error("Error from CreatePipe for stdin: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the write handle to the pipe for STDIN is not inherited.
|
|
|
|
if (!SetHandleInformation(childStdInAtParent,
|
|
|
|
HANDLE_FLAG_INHERIT,
|
|
|
|
inheritFlag)) {
|
|
|
|
throw std::runtime_error("Error from GetHandleInformation for stdin: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up members of the PROCESS_INFORMATION structure.
|
|
|
|
PROCESS_INFORMATION processInfo;
|
|
|
|
// Set up members of the PROCESS_INFORMATION structure.
|
|
|
|
PROCESS_INFORMATION processInfo;
|
|
|
|
|
|
|
|
std::fill((char *) &processInfo,
|
|
|
|
((char *) &processInfo) + sizeof(PROCESS_INFORMATION),
|
|
|
|
0);
|
|
|
|
std::fill((char *) &processInfo,
|
|
|
|
((char *) &processInfo) + sizeof(PROCESS_INFORMATION),
|
|
|
|
0);
|
|
|
|
|
|
|
|
// Set up members of the STARTUPINFO structure. This structure
|
|
|
|
// specifies the STDIN and STDOUT handles for redirection.
|
|
|
|
STARTUPINFO startInfo;
|
|
|
|
|
|
|
|
std::fill((char *) &startInfo,
|
|
|
|
((char *) &startInfo) + sizeof(STARTUPINFO),
|
|
|
|
0);
|
|
|
|
startInfo.cb = sizeof(STARTUPINFO);
|
|
|
|
startInfo.hStdError = childStdOutAtChild;
|
|
|
|
startInfo.hStdOutput = childStdOutAtChild;
|
|
|
|
startInfo.hStdInput = childStdInAtChild;
|
|
|
|
startInfo.dwFlags |= STARTF_USESTDHANDLES;
|
|
|
|
// Set up members of the STARTUPINFO structure. This structure
|
|
|
|
// specifies the STDIN and STDOUT handles for redirection.
|
|
|
|
STARTUPINFO startInfo;
|
|
|
|
|
|
|
|
std::fill((char *) &startInfo,
|
|
|
|
((char *) &startInfo) + sizeof(STARTUPINFO),
|
|
|
|
0);
|
|
|
|
startInfo.cb = sizeof(STARTUPINFO);
|
|
|
|
startInfo.hStdError = childStdOutAtChild;
|
|
|
|
startInfo.hStdOutput = childStdOutAtChild;
|
|
|
|
startInfo.hStdInput = childStdInAtChild;
|
|
|
|
startInfo.dwFlags |= STARTF_USESTDHANDLES;
|
|
|
|
|
|
|
|
// Create the child process.
|
|
|
|
bool status;
|
|
|
|
// Create the child process.
|
|
|
|
bool status;
|
|
|
|
|
|
|
|
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 (!status ) {
|
|
|
|
throw std::runtime_error("Error from CreateProcess with "
|
|
|
|
"command line \"" + cmdline + "\": " +
|
|
|
|
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.
|
|
|
|
childProcess = processInfo.hProcess;
|
|
|
|
childThread = processInfo.hThread;
|
|
|
|
|
|
|
|
childStarted = true;
|
|
|
|
|
|
|
|
// Close the child's end of the pipes.
|
|
|
|
if (!CloseHandle(childStdOutAtChild)) {
|
|
|
|
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 (!status ) {
|
|
|
|
throw std::runtime_error("Error from CreateProcess with "
|
|
|
|
"command line \"" + cmdline + "\": " +
|
|
|
|
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.
|
|
|
|
childProcess = processInfo.hProcess;
|
|
|
|
childThread = processInfo.hThread;
|
|
|
|
|
|
|
|
childStarted = true;
|
|
|
|
|
|
|
|
// Close the child's end of the pipes.
|
|
|
|
if (!CloseHandle(childStdOutAtChild)) {
|
|
|
|
throw std::runtime_error("Error closing the child process stdout handle: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CloseHandle(childStdInAtChild)) {
|
|
|
|
if (!CloseHandle(childStdInAtChild)) {
|
|
|
|
throw std::runtime_error("Error closing the child process stdin handle: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
@@ -199,38 +201,38 @@ namespace CodeDweller { |
|
|
|
}
|
|
|
|
|
|
|
|
if (!TerminateProcess(childProcess, terminateExitCode)) {
|
|
|
|
throw std::runtime_error("Error terminating the child process: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
throw std::runtime_error("Error terminating the child process: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
Child::isDone() {
|
|
|
|
bool
|
|
|
|
Child::isDone() {
|
|
|
|
|
|
|
|
if (exitCodeObtainedFlag) {
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!childStarted) {
|
|
|
|
throw std::logic_error("Child process was not started "
|
|
|
|
"when isDone() was called");
|
|
|
|
}
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
throw std::runtime_error("Error checking status of child process: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STILL_ACTIVE == result) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Child process has exited. Save the exit code.
|
|
|
|
exitCode = result;
|
|
|
@@ -240,32 +242,32 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
Child::result() {
|
|
|
|
int32_t
|
|
|
|
Child::result() {
|
|
|
|
|
|
|
|
if (exitCodeObtainedFlag) {
|
|
|
|
|
|
|
|
return exitCode;
|
|
|
|
return exitCode;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!childStarted) {
|
|
|
|
throw std::logic_error("Child process was not started "
|
|
|
|
"when result() was called");
|
|
|
|
}
|
|
|
|
throw std::logic_error("Child process was not started "
|
|
|
|
"when result() was called");
|
|
|
|
}
|
|
|
|
|
|
|
|
int result;
|
|
|
|
|
|
|
|
if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
|
|
|
|
throw std::runtime_error("Error getting child process exit code: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
throw std::runtime_error("Error getting child process exit code: " +
|
|
|
|
getErrorText());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error if the process has not exited.
|
|
|
|
if (STILL_ACTIVE == result) {
|
|
|
|
throw std::logic_error("Child process was active when "
|
|
|
|
"result() was called");
|
|
|
|
}
|
|
|
|
throw std::logic_error("Child process was active when "
|
|
|
|
"result() was called");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Child process has exited. Save the exit code.
|
|
|
|
exitCode = result;
|
|
|
@@ -274,21 +276,21 @@ namespace CodeDweller { |
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
Child::getErrorText() {
|
|
|
|
std::string
|
|
|
|
Child::getErrorText() {
|
|
|
|
|
|
|
|
LPVOID winMsgBuf;
|
|
|
|
DWORD lastError = GetLastError();
|
|
|
|
|
|
|
|
FormatMessage(
|
|
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
NULL,
|
|
|
|
lastError,
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
(char *) &winMsgBuf,
|
|
|
|
0, NULL );
|
|
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
NULL,
|
|
|
|
lastError,
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
(char *) &winMsgBuf,
|
|
|
|
0, NULL );
|
|
|
|
|
|
|
|
std::string errMsg((char *) winMsgBuf);
|
|
|
|
|
|
|
@@ -296,9 +298,9 @@ namespace CodeDweller { |
|
|
|
return errMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
|
|
|
|
inputHandle(0),
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
|
|
|
|
inputHandle(0),
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
char *end = &(buffer.front()) + buffer.size();
|
|
|
|
|
|
|
@@ -307,23 +309,23 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
|
|
|
|
void
|
|
|
|
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
|
|
|
|
|
|
|
|
inputHandle = inHandle;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::streambuf::int_type
|
|
|
|
Child::ReadStreambuf::underflow() {
|
|
|
|
std::streambuf::int_type
|
|
|
|
Child::ReadStreambuf::underflow() {
|
|
|
|
|
|
|
|
// Check for empty buffer.
|
|
|
|
if (gptr() < egptr()) {
|
|
|
|
|
|
|
|
// Not empty.
|
|
|
|
return traits_type::to_int_type(*gptr());
|
|
|
|
// Not empty.
|
|
|
|
return traits_type::to_int_type(*gptr());
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Need to fill the buffer.
|
|
|
|
char *base = &(buffer.front());
|
|
|
@@ -332,25 +334,26 @@ namespace CodeDweller { |
|
|
|
// Check whether this is the first fill.
|
|
|
|
if (eback() == base) {
|
|
|
|
|
|
|
|
// Not the first fill. Copy putback characters.
|
|
|
|
|
|
|
|
}
|
|
|
|
// Not the first fill. Copy one putback character.
|
|
|
|
*(eback()) = *(egptr() - 1);
|
|
|
|
start++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
start,
|
|
|
|
buffer.size() - (start - base),
|
|
|
|
&nBytesRead,
|
|
|
|
NULL)) {
|
|
|
|
return traits_type::eof();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for EOF.
|
|
|
|
if (0 == nBytesRead) {
|
|
|
|
return traits_type::eof();
|
|
|
|
}
|
|
|
|
return traits_type::eof();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update buffer pointers.
|
|
|
|
setg(base, start, start + nBytesRead);
|
|
|
@@ -359,9 +362,9 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
|
|
|
|
outputHandle(0),
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
|
|
|
|
outputHandle(0),
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
char *base = &(buffer.front());
|
|
|
|
|
|
|
@@ -370,63 +373,69 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
|
|
|
|
void
|
|
|
|
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
|
|
|
|
|
|
|
|
outputHandle = outHandle;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Child::WriteStreambuf::flushBuffer() {
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
pbase(),
|
|
|
|
nBytes,
|
|
|
|
&nBytesWritten,
|
|
|
|
NULL)) {
|
|
|
|
pbump(epptr() - pptr()); // Indicate failure.
|
|
|
|
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(epptr() - pptr()); // Indicate failure.
|
|
|
|
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) {
|
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
// Not writing EOF.
|
|
|
|
*(pptr()) = ch;
|
|
|
|
pbump(1);
|
|
|
|
|
|
|
|
// Write.
|
|
|
|
flushBuffer();
|
|
|
|
|
|
|
|
// Success.
|
|
|
|
return traits_type::not_eof('a');
|
|
|
|
return ch;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Child::WriteStreambuf::sync() {
|
|
|
|
return traits_type::eof();
|
|
|
|
|
|
|
|
flushBuffer();
|
|
|
|
return 1; // Success.
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Child::WriteStreambuf::sync() {
|
|
|
|
|
|
|
|
flushBuffer(); // Throws exception on failure.
|
|
|
|
|
|
|
|
// Success.
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|