Last active
April 15, 2024 16:49
-
-
Save hoyhoy/092ed17c7f3f2585a712606ae6ccff88 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ****************************************************************** | |
| // # @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