fix: Properly forward stdin to main process from forwarder

(cherry picked from commit 1fc857cf7d2a9a525dec5fcda0727a39046b6c86)
This commit is contained in:
WerWolv
2026-01-07 09:38:08 +01:00
parent 08aa03bab6
commit 684373b88b
3 changed files with 62 additions and 26 deletions

View File

@@ -35,7 +35,7 @@ namespace hex::mcp {
client.writeString(request); client.writeString(request);
auto response = client.readBytesUntil(0x00); auto response = client.readBytesUntil(0x00);
if (!response.empty() && response.front() != 0x00) if (!response.empty() && response.front() != 0x00)
output << std::string(response.begin(), response.end()) << '\n'; output << std::string(response.begin(), response.end()) << std::endl;
if (!client.isConnected()) if (!client.isConnected())
break; break;

View File

@@ -28,6 +28,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <array> #include <array>
#include <thread>
void setupConsoleWindow() { void setupConsoleWindow() {
// Get the handle of the console window // Get the handle of the console window
@@ -66,7 +67,10 @@ int launchExecutable() {
auto executableFullPath = executablePath->parent_path() / "imhex-gui.exe"; auto executableFullPath = executablePath->parent_path() / "imhex-gui.exe";
// Handles for the pipes // Handles for the pipes
HANDLE hChildStdoutRead, hChildStdoutWrite; HANDLE hChildStdinRead = nullptr;
HANDLE hChildStdinWrite = nullptr;
HANDLE hChildStdoutRead = nullptr;
HANDLE hChildStdoutWrite = nullptr;
// Security attributes to allow the pipes to be inherited // Security attributes to allow the pipes to be inherited
SECURITY_ATTRIBUTES saAttr; SECURITY_ATTRIBUTES saAttr;
@@ -74,17 +78,30 @@ int launchExecutable() {
saAttr.lpSecurityDescriptor = nullptr; saAttr.lpSecurityDescriptor = nullptr;
saAttr.bInheritHandle = TRUE; saAttr.bInheritHandle = TRUE;
// Create pipes for stdout redirection // Create pipes for stdin redirection
if (!::CreatePipe(&hChildStdoutRead, &hChildStdoutWrite, &saAttr, 0)) { if (!::CreatePipe(&hChildStdinRead, &hChildStdinWrite, &saAttr, 0)) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Ensure the write handle to stdin is not inherited
::SetHandleInformation(hChildStdinWrite, HANDLE_FLAG_INHERIT, 0);
// Create pipes for stdout redirection
if (!::CreatePipe(&hChildStdoutRead, &hChildStdoutWrite, &saAttr, 0)) {
::CloseHandle(hChildStdinRead);
::CloseHandle(hChildStdinWrite);
return EXIT_FAILURE;
}
// Ensure the read handle to stdout is not inherited
::SetHandleInformation(hChildStdoutRead, HANDLE_FLAG_INHERIT, 0);
// Set up the STARTUPINFO structure for the child process // Set up the STARTUPINFO structure for the child process
STARTUPINFO si; STARTUPINFOW si;
::ZeroMemory(&si, sizeof(STARTUPINFO)); ::ZeroMemory(&si, sizeof(STARTUPINFOW));
si.cb = sizeof(STARTUPINFO); si.cb = sizeof(STARTUPINFOW);
si.hStdOutput = hChildStdoutWrite; // Redirect stdout to the parent process si.hStdInput = hChildStdinRead;
si.dwFlags |= STARTF_USESTDHANDLES; // Enable redirection of stdin, stdout, stderr si.hStdOutput = hChildStdoutWrite;
si.hStdError = hChildStdoutWrite; // Also redirect stderr to stdout
si.dwFlags = STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
@@ -102,29 +119,45 @@ int launchExecutable() {
&si, // STARTUPINFO &si, // STARTUPINFO
&pi // PROCESS_INFORMATION &pi // PROCESS_INFORMATION
)) { )) {
::CloseHandle(hChildStdinRead);
::CloseHandle(hChildStdinWrite);
::CloseHandle(hChildStdoutRead);
::CloseHandle(hChildStdoutWrite);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Close unnecessary pipe handles in the parent process // Close handles that the child inherited
::CloseHandle(hChildStdinRead);
::CloseHandle(hChildStdoutWrite); ::CloseHandle(hChildStdoutWrite);
// Read the child process's stdout and stderr and redirect them to the parent's stdout // Get parent's stdin and stdout handles
DWORD bytesRead; HANDLE hParentStdin = ::GetStdHandle(STD_INPUT_HANDLE);
std::array<char, 4096> buffer; HANDLE hParentStdout = ::GetStdHandle(STD_OUTPUT_HANDLE);
while (true) { // Thread to forward parent stdin -> child stdin
// Read from stdout auto stdinThread = std::thread([hParentStdin, hChildStdinWrite]() {
if (::ReadFile(hChildStdoutRead, buffer.data(), buffer.size(), &bytesRead, nullptr)) { DWORD bytesRead, bytesWritten;
// Write to the parent's stdout std::array<char, 4096> buffer;
if (bytesRead > 0)
::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer.data(), bytesRead, &bytesRead, nullptr); while (::ReadFile(hParentStdin, buffer.data(), buffer.size(), &bytesRead, nullptr) && bytesRead > 0) {
} else { ::WriteFile(hChildStdinWrite, buffer.data(), bytesRead, &bytesWritten, nullptr);
break;
} }
} ::CloseHandle(hChildStdinWrite);
});
// Thread to forward child stdout -> parent stdout
auto stdoutThread = std::thread([hChildStdoutRead, hParentStdout]() {
DWORD bytesRead, bytesWritten;
std::array<char, 4096> buffer;
while (::ReadFile(hChildStdoutRead, buffer.data(), buffer.size(), &bytesRead, nullptr) && bytesRead > 0) {
::WriteFile(hParentStdout, buffer.data(), bytesRead, &bytesWritten, nullptr);
}
::CloseHandle(hChildStdoutRead);
});
// Wait for the child process to exit // Wait for the child process to exit
::WaitForSingleObject(pi.hProcess, INFINITE); ::WaitForSingleObject(pi.hProcess, INFINITE);
// Get the exit code of the child process // Get the exit code of the child process
DWORD exitCode = 0; DWORD exitCode = 0;
@@ -132,10 +165,13 @@ int launchExecutable() {
exitCode = EXIT_FAILURE; exitCode = EXIT_FAILURE;
} }
// Clean up // Clean up process handles
::CloseHandle(pi.hProcess); ::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread); ::CloseHandle(pi.hThread);
::CloseHandle(hChildStdoutRead);
// Wait for I/O threads to finish
if (stdinThread.joinable()) stdinThread.join();
if (stdoutThread.joinable()) stdoutThread.join();
return static_cast<int>(exitCode); return static_cast<int>(exitCode);
} }