Skip to content

Instantly share code, notes, and snippets.

@hoyhoy
Last active April 15, 2024 16:49
Show Gist options
  • Select an option

  • Save hoyhoy/092ed17c7f3f2585a712606ae6ccff88 to your computer and use it in GitHub Desktop.

Select an option

Save hoyhoy/092ed17c7f3f2585a712606ae6ccff88 to your computer and use it in GitHub Desktop.
// ******************************************************************
// # @hoyhoy - 04/12/2024
// ******************************************************************
// ******************************************************************
// # CMakeLists.txt
// ******************************************************************
// cmake_minimum_required(VERSION 3.29)
// project(stdout_failure)
// enable_testing()
// add_executable(stdout_failure stdout_failure.cpp)
// add_test(NAME stdout_failure COMMAND stdout_failure)
// ******************************************************************
#include <windows.h>
#include <iostream>
std::string GetLastErrorAsString() {
DWORD errorMessageID = GetLastError();
if (errorMessageID == 0)
return std::string(); // No error message has been recorded
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
// Free the buffer
LocalFree(messageBuffer);
return message;
}
int main() {
std::wstring executablePath = L"C:\\Windows\\System32\\whoami.exe";
// CreateProcess variables
STARTUPINFOW si;
PROCESS_INFORMATION pi;
HANDLE hChildStdOutRd = INVALID_HANDLE_VALUE;
HANDLE hChildStdOutRdDup = INVALID_HANDLE_VALUE;
HANDLE hChildStdOutWr = INVALID_HANDLE_VALUE;
HANDLE hChildStdErrorRd = INVALID_HANDLE_VALUE;
HANDLE hChildStdErrorRdDup = INVALID_HANDLE_VALUE;
HANDLE hChildStdErrorWr = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES sa;
CHAR buffer[4096] = {0};
DWORD bytesRead = 0;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Initialize STARTUPINFOW
// Initialize STARTINFOW for a detached console process
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
// ******************************************************************
// Running this binary with ctest causes stdout and stderr to be
// to error with "broken pipe" if GetStartupInfoW() is called
// even if STARTF_USESTDHANDLES is OR'd into dwFlags
// ******************************************************************
// ******************************************************************
// DO NOT do the following...
// ******************************************************************
// * GetStartupInfoW(&si);
// * si.dwFlags |= STARTF_USESTDHANDLES;
// ******************************************************************
ZeroMemory(&pi, sizeof(pi));
if (!CreatePipe(&hChildStdOutRd, &hChildStdOutWr, &sa, 0)) {
std::cerr << "CreatePipe failed for hChildStdOutRd (" << GetLastErrorAsString() << ").\n";
return EXIT_FAILURE;
}
if (!SetHandleInformation(hChildStdOutRd, HANDLE_FLAG_INHERIT, 0)) {
std::cerr << "SetHandleInformation failed for hChildStdoutRd (" << GetLastErrorAsString() << ").\n";
return EXIT_FAILURE;
}
if (!DuplicateHandle(GetCurrentProcess(), hChildStdOutRd, GetCurrentProcess(), &hChildStdOutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
std::cerr << "DuplicateHandle failed for hChildStdOutRd (" << GetLastErrorAsString() << ").\n";
return EXIT_FAILURE;
}
if (!CreatePipe(&hChildStdErrorRd, &hChildStdErrorWr, &sa, 0)) {
std::cerr << "CreatePipe failed for hChildStdErrorRd (" << GetLastErrorAsString() << ").\n";
return EXIT_FAILURE;
}
if (!SetHandleInformation(hChildStdErrorRd, HANDLE_FLAG_INHERIT, 0)) {
std::cerr << "SetHandleInformation failed for hChildStdErrorRd (" << GetLastErrorAsString() << ").\n";
return EXIT_FAILURE;
}
if (!DuplicateHandle(GetCurrentProcess(), hChildStdErrorRd, GetCurrentProcess(), &hChildStdErrorRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
std::cerr << "DuplicateHandle failed for hChildStdErrorRd (" << GetLastErrorAsString() << ").\n";
return EXIT_FAILURE;
}
si.hStdOutput = hChildStdOutWr; // Redirect stdout to the write end of the pipe
si.hStdError = hChildStdErrorWr; // Redirect stderr to the write end of the pipe
// Create the process
DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
if (!CreateProcessW(
NULL, // No module name (use command line)
(LPWSTR) executablePath.c_str(), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
TRUE, // Set handle inheritance to TRUE
dwCreationFlags, // Creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFOW structure
&pi // Pointer to PROCESS_INFORMATION structure
)) {
std::cerr << "CreateProcess failed (" << GetLastErrorAsString() << ").\n";
return EXITFAILURE;
}
// Wait until child process exits.
CloseHandle(hChildStdOutWr);
CloseHandle(hChildStdErrorWr);
CloseHandle(hChildStdOutRd);
CloseHandle(hChildStdErrorRd);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
bool read_ok = ReadFile(hChildStdOutRdDup, buffer, sizeof(buffer), &bytesRead, NULL);
if (!read_ok) {
std::cout << "error: " << GetLastErrorAsString() << '\n';
}
if (bytesRead > 0) {
std::string output(buffer, bytesRead);
std::cout << "read: " << output << '\n';
} else {
std::cout << "could not read bytes" << '\n';
}
CloseHandle(hChildStdOutRdDup);
CloseHandle(hChildStdOutRdDup);
// Close process and thread handles.
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment