// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // This is a utility program for testing that Dart binary correctly handles // situations when stdout and stderr handles are the same. // // This can happen in certain terminal emulators, e.g. one used by GitBash // see https://github.com/dart-lang/sdk/issues/61981 for an example. #include "platform/globals.h" #if defined(DART_HOST_OS_WINDOWS) #include #include #include #include struct StdioHandles { HANDLE in; HANDLE out; HANDLE err; }; static void ReportErrorAndAbort( std::source_location location = std::source_location::current()) { const auto code = GetLastError(); wchar_t buffer[512]; auto message_size = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, ARRAY_SIZE(buffer), nullptr); if (message_size == 0) { _snwprintf(buffer, ARRAY_SIZE(buffer), L"OS Error %d", code); } fprintf(stderr, "error at %s:%d: %ls", location.file_name(), location.line(), buffer); abort(); } static void LaunchProcessWith(wchar_t* command_line, const StdioHandles& stdio_handles) { fprintf(stderr, "LAUNCHING %ls\n", command_line); // Setup info STARTUPINFOEXW startup_info; ZeroMemory(&startup_info, sizeof(startup_info)); startup_info.StartupInfo.cb = sizeof(startup_info); // Setup the handles to inherit. We only want to inherit the three // handles for stdin, stdout and stderr. startup_info.StartupInfo.hStdInput = stdio_handles.in; startup_info.StartupInfo.hStdOutput = stdio_handles.out; startup_info.StartupInfo.hStdError = stdio_handles.err; startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES; SIZE_T size = 0; // The call to determine the size of an attribute list always fails with // ERROR_INSUFFICIENT_BUFFER and that error should be ignored. if (!InitializeProcThreadAttributeList(nullptr, 1, 0, &size) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) { return ReportErrorAndAbort(); } auto attribute_list = reinterpret_cast(malloc(size)); ZeroMemory(attribute_list, size); if (!InitializeProcThreadAttributeList(attribute_list, 1, 0, &size)) { return ReportErrorAndAbort(); } std::vector inherited_handles = {stdio_handles.in}; if (stdio_handles.out != stdio_handles.in) { inherited_handles.push_back(stdio_handles.out); } if (stdio_handles.err != stdio_handles.out && stdio_handles.err != stdio_handles.in) { inherited_handles.push_back(stdio_handles.err); } if (!UpdateProcThreadAttribute( attribute_list, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(), inherited_handles.size() * sizeof(HANDLE), nullptr, nullptr)) { return ReportErrorAndAbort(); } startup_info.lpAttributeList = attribute_list; PROCESS_INFORMATION process_info; ZeroMemory(&process_info, sizeof(process_info)); // Create process. BOOL result = CreateProcessW( /*lpApplicationName=*/nullptr, command_line, /*lpProcessAttributes=*/nullptr, /*lpThreadAttributes=*/nullptr, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/EXTENDED_STARTUPINFO_PRESENT, /*lpEnvironment=*/nullptr, /*lpCurrentDirectory=*/nullptr, reinterpret_cast(&startup_info), &process_info); if (result == 0) { return ReportErrorAndAbort(); } WaitForSingleObject(process_info.hProcess, INFINITE); CloseHandle(process_info.hProcess); CloseHandle(process_info.hThread); } int main(int argc, char* argv[]) { if (argc <= 1) { fprintf(stderr, "Usage: %s ... \n", argv[0]); return -1; } // Generate command line. Assume that it does not contain any white-space // in the arguments. std::wstringstream wstr; for (int i = 1; i < argc; i++) { wstr << (i > 1 ? " " : "") << argv[i]; } HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE); HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); LaunchProcessWith( wstr.str().data(), {.in = stdin_handle, .out = stdout_handle, .err = stdout_handle}); CloseHandle(stdin_handle); CloseHandle(stdout_handle); return 0; } #else int main() { return -1; } #endif // defined(DART_HOST_OS_WINDOWS)