Skip to content

Commit 6f8c26b

Browse files
authored
fix: prevent unnecessary console windows
fix #376
1 parent a40344e commit 6f8c26b

File tree

4 files changed

+116
-13
lines changed

4 files changed

+116
-13
lines changed

source/matplot/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ add_library(matplot
3737
util/handle_types.h
3838
util/keywords.h
3939
util/popen.h
40+
util/popen.cpp
4041
util/type_traits.h
4142
util/world_cities.cpp
4243
util/world_map_10m.cpp

source/matplot/util/common.cpp

+1-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <iostream>
99
#include <matplot/util/colors.h>
1010
#include <matplot/util/common.h>
11+
#include <matplot/util/popen.h>
1112
#include <random>
1213
#include <regex>
1314
#include <set>
@@ -25,17 +26,6 @@
2526
#pragma warning(pop)
2627
#endif
2728

28-
#ifdef _WIN32
29-
#include <windows.h>
30-
#define PCLOSE _pclose
31-
#define POPEN _popen
32-
#define FILENO _fileno
33-
#else
34-
#define PCLOSE pclose
35-
#define POPEN popen
36-
#define FILENO fileno
37-
#endif
38-
3929
namespace matplot {
4030
bool iequals(std::string_view str1, std::string_view str2) {
4131
if (str1.size() != str2.size()) {

source/matplot/util/popen.cpp

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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

source/matplot/util/popen.h

+12-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,19 @@
66
#define MATPLOTPLUSPLUS_POPEN_H
77

88
#ifdef _WIN32
9+
#include <stdio.h> //needed for FILE type
910
#include <windows.h>
10-
#define PCLOSE _pclose
11-
#define POPEN _popen
11+
12+
namespace matplot::detail {
13+
// in a GUI application, _popen pops up a window. This version does not.
14+
FILE *hiddenPopen(const char *command, const char *mode);
15+
16+
// close the handle opened by hiddenPopen
17+
int hiddenPclose(FILE *file);
18+
} // namespace matplot::detail
19+
20+
#define PCLOSE ::matplot::detail::hiddenPclose
21+
#define POPEN ::matplot::detail::hiddenPopen
1222
#define FILENO _fileno
1323
#else
1424
#define PCLOSE pclose

0 commit comments

Comments
 (0)