fix(launcher): hide windows child-process console flashes

- hide windows when launching gateway process from launcher

- hide windows for powershell/tasklist process inspection commands
This commit is contained in:
SiYue 2026-04-24 16:50:19 +08:00
parent a977a92729
commit e1863234f0
3 changed files with 40 additions and 4 deletions

View file

@ -0,0 +1,11 @@
//go:build !windows
package api
import "os/exec"
func launcherExecCommand(name string, args ...string) *exec.Cmd {
return exec.Command(name, args...)
}
func applyLauncherWindowsProcAttrs(_ *exec.Cmd) {}

View file

@ -0,0 +1,24 @@
//go:build windows
package api
import (
"os/exec"
"syscall"
)
func launcherExecCommand(name string, args ...string) *exec.Cmd {
cmd := exec.Command(name, args...)
applyLauncherWindowsProcAttrs(cmd)
return cmd
}
func applyLauncherWindowsProcAttrs(cmd *exec.Cmd) {
if cmd == nil {
return
}
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
cmd.SysProcAttr.HideWindow = true
}

View file

@ -164,7 +164,7 @@ func isLikelyGatewayProcess(pid int) (bool, bool) {
`$p=Get-CimInstance Win32_Process -Filter "ProcessId = %d"; if ($null -eq $p) { "" } else { $p.CommandLine }`, `$p=Get-CimInstance Win32_Process -Filter "ProcessId = %d"; if ($null -eq $p) { "" } else { $p.CommandLine }`,
pid, pid,
) )
out, err := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command", psCmd).Output() out, err := launcherExecCommand("powershell", "-NoProfile", "-NonInteractive", "-Command", psCmd).Output()
if err == nil { if err == nil {
cmdline := strings.TrimSpace(string(out)) cmdline := strings.TrimSpace(string(out))
if cmdline != "" { if cmdline != "" {
@ -173,7 +173,7 @@ func isLikelyGatewayProcess(pid int) (bool, bool) {
} }
// Fallback: determine only whether the process still exists. // Fallback: determine only whether the process still exists.
out, err = exec.Command("tasklist", "/FI", "PID eq "+strconv.Itoa(pid), "/FO", "CSV", "/NH").Output() out, err = launcherExecCommand("tasklist", "/FI", "PID eq "+strconv.Itoa(pid), "/FO", "CSV", "/NH").Output()
if err != nil { if err != nil {
return false, false return false, false
} }
@ -187,7 +187,7 @@ func isLikelyGatewayProcess(pid int) (bool, bool) {
if strings.Contains(line, "\"picoclaw.exe\"") { if strings.Contains(line, "\"picoclaw.exe\"") {
return true, true return true, true
} }
return false, false return false, true
} }
if strings.Contains(line, "no tasks are running") { if strings.Contains(line, "no tasks are running") {
return false, true return false, true
@ -195,7 +195,7 @@ func isLikelyGatewayProcess(pid int) (bool, bool) {
return false, true return false, true
} }
out, err := exec.Command("ps", "-o", "command=", "-p", strconv.Itoa(pid)).Output() out, err := launcherExecCommand("ps", "-o", "command=", "-p", strconv.Itoa(pid)).Output()
if err != nil { if err != nil {
return false, false return false, false
} }
@ -706,6 +706,7 @@ func (h *Handler) startGatewayLocked(initialStatus string, existingPid int) (int
logger.InfoC("gateway", fmt.Sprintf("Starting gateway process (%s)", execPath)) logger.InfoC("gateway", fmt.Sprintf("Starting gateway process (%s)", execPath))
cmd = gatewayExecCommand(execPath, h.gatewayCommandArgs()...) cmd = gatewayExecCommand(execPath, h.gatewayCommandArgs()...)
applyLauncherWindowsProcAttrs(cmd)
cmd.Env = os.Environ() cmd.Env = os.Environ()
// Forward the launcher's config path via the environment variable that // Forward the launcher's config path via the environment variable that
// GetConfigPath() already reads, so the gateway sub-process uses the same // GetConfigPath() already reads, so the gateway sub-process uses the same