fix(core): 所有子进程统一走 platform::child::quiet + 角色守卫(M213)

GUI 父进程(PE Subsystem = 2)spawn 控制台子进程时,Windows 会为子进程新建一块
控制台——桌面上就是「黑框闪一下」。改前 crates/core/src 有 21 处 Command::new,
只有 tree_kill 那一处带了 CREATE_NO_WINDOW,于是探 N 个 JDK 就闪 N 次 java -version。

- 新增 crates/core/src/platform/child.rs:唯一构造器 quiet(program),Windows 上统一
  .creation_flags(CREATE_NO_WINDOW),其它平台原样返回。tree_kill 里那份就地合并进来,
  不再保留第二份常量。
- 20 处 spawn 全部改走它(region_system / java{discover,manual,probe,proc,termux} /
  addon exec / platform{linux_caps,macos_version,theme_probe,vk_probe,windows_version,
  wallpaper/io})。
- launch/process.rs 零改动:游戏进程要 CREATE_NEW_PROCESS_GROUP 撑 taskkill /T,
  带不带控制台窗口是产品决定。
- crates/core/tests/child_quiet.rs:按角色判「有没有绕开构造器」的守卫,外加构造器
  本身的 4 条行为用例。三条变异(裸 Command::new / 别名 Spawn::new / 删掉 creation_flags)
  都实测变红并已还原。
This commit is contained in:
AskaEth committed 2026-11-09 15:07:56 +08:00
1 parent bb8be87c8f
commit 40a296471d
17 files changed
+417 -39

No files matched your search

+2 -2
View File
@@ -21,8 +21,8 @@
//! (别为了一次启动白跑 N 次 `java -version`)。
use std::path::{Path, PathBuf};
use std::process::Command;
use crate::platform::child::quiet;
use crate::platform::paths::{EnvSource, ProcessEnv};
use crate::platform::termux;
@@ -153,7 +153,7 @@ pub fn registry_homes() -> Vec<PathBuf> {
}
let mut homes = Vec::new();
for view in ["64", "32"] {
let Ok(output) = Command::new("reg")
let Ok(output) = quiet("reg")
.args(["query", "HKLM\\SOFTWARE\\JavaSoft\\JDK", "/s", "/reg:"])
.arg(view)
.output()
+1 -1
View File
@@ -105,7 +105,7 @@ pub fn probe_with_timeout(
cancelled: &dyn Fn() -> bool,
) -> (JavaRuntime, String) {
let mut runtime = JavaRuntime::unknown(binary.to_path_buf(), origin);
let mut command = std::process::Command::new(binary);
let mut command = crate::platform::child::quiet(binary);
command.arg("-version");
let Some(output) = super::proc::run_with_timeout(command, timeout, cancelled) else {
return (runtime, String::new());
+2 -2
View File
@@ -8,10 +8,10 @@
//! 候选运行时由调用方注入,测试因此不需要真的装三个 JDK。
use std::path::{Path, PathBuf};
use std::process::Command;
use super::extract::{java_binary_name, read_stamp};
use super::vendor::JdkVendor;
use crate::platform::child::quiet;
/// 缺省 Java 主版本(探测不到任何东西时的保守值,D13:JDK 17 基准)。
pub const DEFAULT_MAJOR: u32 = 17;
@@ -119,7 +119,7 @@ impl JavaRuntime {
#[must_use]
pub fn probe(binary: &Path, origin: JavaOrigin) -> JavaRuntime {
let mut runtime = JavaRuntime::unknown(binary.to_path_buf(), origin);
let Ok(output) = Command::new(binary).arg("-version").output() else {
let Ok(output) = quiet(binary).arg("-version").output() else {
return runtime;
};
let mut text = String::from_utf8_lossy(&output.stderr).into_owned();
+4 -4
View File
@@ -236,7 +236,7 @@ mod tests {
seconds: u32,
) -> std::process::Command {
let mut command = if cfg!(windows) {
let mut command = std::process::Command::new("powershell");
let mut command = crate::platform::child::quiet("powershell");
command.args([
"-NoProfile",
"-Command",
@@ -248,7 +248,7 @@ mod tests {
]);
command
} else {
let mut command = std::process::Command::new("sh");
let mut command = crate::platform::child::quiet("sh");
command.args([
"-c",
&format!(
@@ -267,11 +267,11 @@ mod tests {
/// 一个必然超时的命令(两个平台都有,且在「等很久」这件事上行为一致)。
fn sleeping_command(seconds: u32) -> std::process::Command {
if cfg!(windows) {
let mut command = std::process::Command::new("cmd");
let mut command = crate::platform::child::quiet("cmd");
command.args(["/C", &format!("ping -n {seconds} 127.0.0.1 > NUL")]);
command
} else {
let mut command = std::process::Command::new("sleep");
let mut command = crate::platform::child::quiet("sleep");
command.arg(seconds.to_string());
command
}
+2 -2
View File
@@ -10,8 +10,8 @@
//! 镜像顺序是实测可用的国内源;全部失败才报错,错误里带每一跳的原因。
use std::path::{Path, PathBuf};
use std::process::Command;
use crate::platform::child::quiet;
use crate::platform::paths::ProcessEnv;
use crate::platform::termux as host;
@@ -183,7 +183,7 @@ fn apt_install(prefix: &Path, workspace: &Path, mirror: &str, package: &str) ->
/// 执行一次 `apt-get`(`-o` 选项在前,子命令在后)。
fn run_apt(prefix: &Path, options: &[String], args: &[&str]) -> Result<(), String> {
let apt = prefix.join("bin").join("apt-get");
let mut command = Command::new(&apt);
let mut command = quiet(&apt);
command.env("DEBIAN_FRONTEND", "noninteractive");
for option in options {
command.arg("-o").arg(option);