fix(win): route shell-outs through a windowless helper (no cmd.exe flash)

_popen/_popen-style shell-outs flash a cmd.exe console window on Windows. Add
Platform::runHiddenCapture() — CreateProcess + CREATE_NO_WINDOW capturing stdout on
Windows, popen on POSIX — and route the remaining shell-outs through it:
- GPU-aware idle detection (getGpuUtilization: "where nvidia-smi" / "nvidia-smi --query-gpu")
- xmrig discovery + version (findXmrigBinary "where xmrig.exe"; "<bin> --version", stderr merged)
- wallet-rebuild helper (app_network) — keeps its exit-code check via the new exitCode out-param

None of these are on the launch path (that was the daemon spawn, fixed in a2f84be); each
would flash a console only when it ran (idle-GPU mining, mining tab, wallet recovery).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 23:58:54 -05:00
co-authored by Claude Opus 4.8
parent a2f84be2d4
commit 0942691eb3
4 changed files with 101 additions and 71 deletions
+14 -41
View File
@@ -23,6 +23,7 @@
#include <curl/curl.h>
#include "../util/logger.h"
#include "../util/platform.h"
#include "../util/pool_registry.h"
#ifdef _WIN32
@@ -145,32 +146,18 @@ std::string XmrigManager::findXmrigBinary() {
return path;
}
// Fallback: system PATH
// Fallback: system PATH — windowless so it never flashes a console.
#ifdef _WIN32
FILE* f = _popen("where xmrig.exe 2>nul", "r");
std::string out = util::Platform::runHiddenCapture("where xmrig.exe");
#else
FILE* f = popen("which xmrig 2>/dev/null", "r");
#endif
if (f) {
char line[512];
if (fgets(line, sizeof(line), f)) {
std::string s(line);
while (!s.empty() && (s.back() == '\n' || s.back() == '\r'))
s.pop_back();
if (!s.empty() && fs::exists(s)) {
#ifdef _WIN32
_pclose(f);
#else
pclose(f);
#endif
return s;
}
}
#ifdef _WIN32
_pclose(f);
#else
pclose(f);
std::string out = util::Platform::runHiddenCapture("which xmrig");
#endif
{
std::string s = out;
const auto nl = s.find_first_of("\r\n"); // first line only
if (nl != std::string::npos) s.erase(nl);
while (!s.empty() && (s.back() == ' ' || s.back() == '\t')) s.pop_back();
if (!s.empty() && fs::exists(s)) return s;
}
return {};
@@ -927,24 +914,10 @@ void XmrigManager::startVersionDetection()
const bool binShellSafe =
!bin.empty() && bin.find_first_of("\"'`$;&|<>^%\n\r") == std::string::npos;
if (binShellSafe) {
const std::string cmd = "\"" + bin + "\" --version 2>&1";
#ifdef _WIN32
FILE* fp = _popen(cmd.c_str(), "r");
#else
FILE* fp = popen(cmd.c_str(), "r");
#endif
if (fp) {
std::string out;
char buf[256];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) out.append(buf, n);
#ifdef _WIN32
_pclose(fp);
#else
pclose(fp);
#endif
ver = parseMinerVersion(out);
}
// Windowless capture (mergeStderr: xmrig may print --version to stderr) — never flashes.
const std::string cmd = "\"" + bin + "\" --version";
const std::string out = util::Platform::runHiddenCapture(cmd, /*mergeStderr=*/true);
if (!out.empty()) ver = parseMinerVersion(out);
}
std::lock_guard<std::mutex> lk(g_installed_ver_mutex);
g_installed_ver = ver;