Browse Source

Completed unit tests on Windows.

git-svn-id: https://svn.microneil.com/svn/CodeDweller/branches/adeniz_1@32 d34b734f-a00e-4b39-a726-e4eeb87269ab
adeniz_1
adeniz 10 years ago
parent
commit
6fc2c7c921
1 changed files with 192 additions and 183 deletions
  1. 192
    183
      child.cpp

+ 192
- 183
child.cpp View File

void void
Child::init() { Child::init() {
reader.exceptions(std::istream::failbit | std::istream::badbit);
writer.exceptions(std::ostream::failbit | std::ostream::badbit);
childStarted = false; childStarted = false;
exitCodeObtainedFlag = false; exitCodeObtainedFlag = false;
exitCode = 0; exitCode = 0;
} }
// Set the bInheritHandle flag so pipe handles are inherited. // 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, &securityAttributes,
bufferSize)) { 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: " + throw std::runtime_error("Error closing the child process stdout handle: " +
getErrorText()); getErrorText());
} }
if (!CloseHandle(childStdInAtChild)) {
if (!CloseHandle(childStdInAtChild)) {
throw std::runtime_error("Error closing the child process stdin handle: " + throw std::runtime_error("Error closing the child process stdin handle: " +
getErrorText()); getErrorText());
} }
} }
if (!TerminateProcess(childProcess, terminateExitCode)) { 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) { if (exitCodeObtainedFlag) {
return true;
return true;
}
}
if (!childStarted) { 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; int result;
if (!GetExitCodeProcess(childProcess, (LPDWORD) &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) { if (STILL_ACTIVE == result) {
return false;
return false;
}
}
// Child process has exited. Save the exit code. // Child process has exited. Save the exit code.
exitCode = result; exitCode = result;
} }
int32_t
Child::result() {
int32_t
Child::result() {
if (exitCodeObtainedFlag) { if (exitCodeObtainedFlag) {
return exitCode;
return exitCode;
}
}
if (!childStarted) { 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; int result;
if (!GetExitCodeProcess(childProcess, (LPDWORD) &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. // Error if the process has not exited.
if (STILL_ACTIVE == result) { 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. // Child process has exited. Save the exit code.
exitCode = result; exitCode = result;
return result; return result;
} }
std::string
Child::getErrorText() {
std::string
Child::getErrorText() {
LPVOID winMsgBuf; LPVOID winMsgBuf;
DWORD lastError = GetLastError(); DWORD lastError = GetLastError();
FormatMessage( 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); std::string errMsg((char *) winMsgBuf);
return errMsg; 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(); char *end = &(buffer.front()) + buffer.size();
} }
void
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
void
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
inputHandle = inHandle; inputHandle = inHandle;
} }
std::streambuf::int_type
Child::ReadStreambuf::underflow() {
std::streambuf::int_type
Child::ReadStreambuf::underflow() {
// Check for empty buffer. // Check for empty buffer.
if (gptr() < egptr()) { 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. // Need to fill the buffer.
char *base = &(buffer.front()); char *base = &(buffer.front());
// Check whether this is the first fill. // Check whether this is the first fill.
if (eback() == base) { 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. // start points to the start of the buffer. Fill buffer.
DWORD nBytesRead; DWORD nBytesRead;
if (!ReadFile(inputHandle, 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. // Check for EOF.
if (0 == nBytesRead) { if (0 == nBytesRead) {
return traits_type::eof();
}
return traits_type::eof();
}
// Update buffer pointers. // Update buffer pointers.
setg(base, start, start + nBytesRead); setg(base, start, start + nBytesRead);
} }
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()); char *base = &(buffer.front());
} }
void
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
void
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
outputHandle = outHandle; outputHandle = outHandle;
} }
void
Child::WriteStreambuf::flushBuffer() {
void
Child::WriteStreambuf::flushBuffer() {
// Write. // Write.
std::ptrdiff_t nBytes = pptr() - pbase(); std::ptrdiff_t nBytes = pptr() - pbase();
DWORD nBytesWritten; DWORD nBytesWritten;
if (!WriteFile(outputHandle, 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) { 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); pbump(-nBytes);
return; 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. // Check whether we're writing EOF.
if (traits_type::eof() != ch) { if (traits_type::eof() != ch) {
// Not writing EOF.
*(pptr()) = ch;
pbump(1);
}
// Not writing EOF.
*(pptr()) = ch;
pbump(1);
// Write. // Write.
flushBuffer(); flushBuffer();
// Success. // 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;
} }

Loading…
Cancel
Save