void ChildProcess::WaitForCompletion(bool printOutput) { if (hProcess_) { // This looks like a busy loop, but it isn't. IsStillRunning() // waits until the process exits or sends more output, so this // is actually an idle loop. while (IsStillRunning()) { if (printOutput) { std::wstring output = RemoveOutputText(); outputPrintf(L"%s", output.c_str()); } } // This isn't technically needed, but removing it would make // me nervous. WaitForSingleObject(hProcess_, INFINITE); } // Once the process is finished we have to close the stderr/stdout // handles so that the listener thread will exit. We also have to // close these if the process never started. if (hStdError_ != INVALID_HANDLE_VALUE) { CloseHandle(hStdError_); hStdError_ = INVALID_HANDLE_VALUE; } if (hStdOutput_ != INVALID_HANDLE_VALUE) { CloseHandle(hStdOutput_); hStdOutput_ = INVALID_HANDLE_VALUE; } // Wait for the listener thread to exit. if (hChildThread_) { WaitForSingleObject(hChildThread_, INFINITE); CloseHandle(hChildThread_); hChildThread_ = 0; } // Clean up. if (hPipe_ != INVALID_HANDLE_VALUE) { CloseHandle(hPipe_); hPipe_ = INVALID_HANDLE_VALUE; } if (printOutput) { // Now that the child thread has exited we can finally read // the last of the child-process output. std::wstring output = RemoveOutputText(); if (!output.empty()) outputPrintf(L"%s", output.c_str()); } }
// Load a file and convert to a string. If the file contains // an embedded NUL then the resulting string will be truncated. std::wstring LoadFileAsText(const std::wstring& fileName) { if (!::PathFileExistsW(fileName.c_str())) return L""; std::ifstream f; f.open(fileName, std::ios_base::binary); if (!f) { outputPrintf(L"Failed to load file `%s` as text - f.open failed!\n", fileName.c_str()); return L""; } // Find the file length. f.seekg(0, std::ios_base::end); const std::streamoff len_int = f.tellg(); if (len_int == -1) { outputPrintf(L"Failed to load file `%s` as text - f.tellg failed!\n", fileName.c_str()); return L""; } const size_t length = static_cast<size_t>(len_int); f.seekg(0, std::ios_base::beg); // Allocate a buffer and read the file. std::vector<char> data(length + 2); f.read(&data[0], length); if (!f) { outputPrintf(L"Failed to load file `%s` as text - f.read failed!\n", fileName.c_str()); return L""; } // Add a multi-byte null terminator. data[length] = 0; data[length+1] = 0; const wchar_t bom = 0xFEFF; UIETWASSERT(data.size() > sizeof(bom)); if (memcmp(&bom, &data[0], sizeof(bom)) == 0) { // Assume UTF-16, strip bom, and return. return reinterpret_cast<const wchar_t*>(&data[sizeof(bom)]); } // If not-UTF-16 then convert from ANSI to wstring and return return AnsiToUnicode(&data[0]); }
bool ChildProcess::Run(bool showCommand, std::wstring args) { UIETWASSERT(!hProcess_); if (showCommand) outputPrintf(L"%s\n", args.c_str()); SECURITY_ATTRIBUTES security = { sizeof(security), 0, TRUE }; hStdOutput_ = CreateFile(kPipeName, GENERIC_WRITE, 0, &security, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (hStdOutput_ == INVALID_HANDLE_VALUE) return false; if (!DuplicateHandle(GetCurrentProcess(), hStdOutput_, GetCurrentProcess(), &hStdError_, 0, TRUE, DUPLICATE_SAME_ACCESS)) return false; STARTUPINFO startupInfo = {}; startupInfo.hStdOutput = hStdOutput_; startupInfo.hStdError = hStdError_; startupInfo.hStdInput = INVALID_HANDLE_VALUE; startupInfo.dwFlags = STARTF_USESTDHANDLES; PROCESS_INFORMATION processInfo = {}; DWORD flags = CREATE_NO_WINDOW; // Wacky CreateProcess rules say args has to be writable! std::vector<wchar_t> argsCopy(args.size() + 1); wcscpy_s(&argsCopy[0], argsCopy.size(), args.c_str()); BOOL success = CreateProcess(exePath_.c_str(), &argsCopy[0], NULL, NULL, TRUE, flags, NULL, NULL, &startupInfo, &processInfo); if (success) { CloseHandle(processInfo.hThread); hProcess_ = processInfo.hProcess; return true; } else { outputPrintf(L"Error %d starting %s, %s\n", (int)GetLastError(), exePath_.c_str(), args.c_str()); } return false; }
void outputLastError(const DWORD lastErr) { const DWORD errMsgSize = 1024u; wchar_t errBuff[errMsgSize] = {}; const DWORD ret = ::FormatMessageW( (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS), NULL, lastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errBuff, errMsgSize, NULL); if (ret == 0) return; // FormatMessageW failed. outputPrintf(errBuff); }
ChildProcess::~ChildProcess() { if (hProcess_) { DWORD exitCode = GetExitCode(); if (exitCode) outputPrintf(L"Process exit code was %08x (%lu)\n", exitCode, exitCode); CloseHandle(hProcess_); } if (hOutputAvailable_) { CloseHandle(hOutputAvailable_); } }