|
| 1 | +#include <matplot/util/popen.h> |
| 2 | + |
| 3 | +#ifdef _WIN32 |
| 4 | +#include <io.h> |
| 5 | + |
| 6 | +namespace matplot::detail { |
| 7 | + // Function to create a new process and return a FILE pointer for |
| 8 | + // input/output. Mimics _popen behaviour, but does not open console windows |
| 9 | + // in GUI apps |
| 10 | + FILE *hiddenPopen(const char *command, const char *mode) { |
| 11 | + SECURITY_ATTRIBUTES saAttr; |
| 12 | + HANDLE hChildStdinRd, hChildStdinWr, hStdin, hStdout; |
| 13 | + STARTUPINFO si; |
| 14 | + PROCESS_INFORMATION pi; |
| 15 | + |
| 16 | + // Set up security attributes for inheritable handles |
| 17 | + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 18 | + saAttr.bInheritHandle = TRUE; |
| 19 | + saAttr.lpSecurityDescriptor = NULL; |
| 20 | + |
| 21 | + // Create a pipe for the child process's input |
| 22 | + if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) { |
| 23 | + return nullptr; |
| 24 | + } |
| 25 | + |
| 26 | + // Ensure the write handle to the pipe is not inherited by child |
| 27 | + // processes |
| 28 | + if (!SetHandleInformation(hChildStdinWr, HANDLE_FLAG_INHERIT, 0)) { |
| 29 | + CloseHandle(hChildStdinRd); |
| 30 | + return nullptr; |
| 31 | + } |
| 32 | + |
| 33 | + // Create a pipe for the child process's output |
| 34 | + if (!CreatePipe(&hStdin, &hStdout, &saAttr, 0)) { |
| 35 | + CloseHandle(hChildStdinRd); |
| 36 | + CloseHandle(hChildStdinWr); |
| 37 | + return nullptr; |
| 38 | + } |
| 39 | + |
| 40 | + // Ensure the read handle to the output pipe is not inherited by child |
| 41 | + // processes |
| 42 | + if (!SetHandleInformation(hStdout, HANDLE_FLAG_INHERIT, 0)) { |
| 43 | + CloseHandle(hChildStdinRd); |
| 44 | + CloseHandle(hChildStdinWr); |
| 45 | + CloseHandle(hStdin); |
| 46 | + return nullptr; |
| 47 | + } |
| 48 | + |
| 49 | + // Configure STARTUPINFO structure for the new process |
| 50 | + ZeroMemory(&si, sizeof(STARTUPINFO)); |
| 51 | + si.cb = sizeof(STARTUPINFO); |
| 52 | + si.hStdError = hStdout; |
| 53 | + si.hStdOutput = hStdout; |
| 54 | + si.hStdInput = hChildStdinRd; |
| 55 | + si.dwFlags |= STARTF_USESTDHANDLES; |
| 56 | + |
| 57 | + // Create the child process, while hiding the window |
| 58 | + if (!CreateProcess(NULL, const_cast<char *>(command), NULL, NULL, TRUE, |
| 59 | + CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { |
| 60 | + CloseHandle(hChildStdinRd); |
| 61 | + CloseHandle(hChildStdinWr); |
| 62 | + CloseHandle(hStdin); |
| 63 | + CloseHandle(hStdout); |
| 64 | + return nullptr; |
| 65 | + } |
| 66 | + |
| 67 | + // Close unnecessary handles |
| 68 | + CloseHandle(hChildStdinRd); |
| 69 | + CloseHandle(hStdout); |
| 70 | + |
| 71 | + // Create a FILE pointer from the write handle to the child process's |
| 72 | + // input |
| 73 | + FILE *file = _fdopen(_open_osfhandle((intptr_t)hChildStdinWr, 0), mode); |
| 74 | + |
| 75 | + if (file == nullptr) { |
| 76 | + CloseHandle(hChildStdinWr); |
| 77 | + CloseHandle(hStdin); |
| 78 | + CloseHandle(pi.hProcess); |
| 79 | + CloseHandle(pi.hThread); |
| 80 | + return nullptr; |
| 81 | + } |
| 82 | + |
| 83 | + return file; |
| 84 | + } |
| 85 | + |
| 86 | + // Function to close the process and retrieve its exit code |
| 87 | + int hiddenPclose(FILE *file) { |
| 88 | + HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); |
| 89 | + fclose(file); |
| 90 | + DWORD exitCode; |
| 91 | + |
| 92 | + // Wait for the process to finish and get its exit code |
| 93 | + if (WaitForSingleObject(hFile, INFINITE) == WAIT_OBJECT_0 && |
| 94 | + GetExitCodeProcess(hFile, &exitCode)) { |
| 95 | + CloseHandle(hFile); |
| 96 | + return exitCode; |
| 97 | + } else { |
| 98 | + return -1; // Failed to get the exit code |
| 99 | + } |
| 100 | + } |
| 101 | +} // namespace matplot::detail |
| 102 | +#endif // _WIN32 |
0 commit comments