Detach managed processes from run console
This commit is contained in:
@@ -22,17 +22,23 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const managedProcessHelperFlag = "--run-managed-process-helper"
|
||||
const (
|
||||
managedProcessHelperFlag = "--run-managed-process-helper"
|
||||
managedProcessDetachedFlags = windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP | windows.CREATE_BREAKAWAY_FROM_JOB
|
||||
)
|
||||
|
||||
func configureManagedProcessCommand(cmd *exec.Cmd) {
|
||||
// Run may itself be launched by a service or task scheduler job that
|
||||
// terminates its process tree on shutdown. The helper is the durable
|
||||
// owner of the game process, so ask Windows to keep it outside that job.
|
||||
// The helper itself does not need a console: its stdout/stderr are already
|
||||
// durable files. CREATE_NEW_CONSOLE creates a second hidden conhost in a
|
||||
// non-interactive Task Scheduler session and prevents the child pseudo
|
||||
// console from initializing on Windows Server.
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: windows.CREATE_NO_WINDOW | windows.CREATE_BREAKAWAY_FROM_JOB, HideWindow: true}
|
||||
// durable files. Detaching also prevents an operator Ctrl+C used to restart
|
||||
// Run from propagating into the helper, cmd wrapper, or game process whose
|
||||
// logs the next Run instance must resume tailing.
|
||||
// CREATE_NEW_CONSOLE creates a second hidden conhost in a non-interactive
|
||||
// Task Scheduler session and prevents the child pseudo console from
|
||||
// initializing on Windows Server.
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: managedProcessDetachedFlags, HideWindow: true}
|
||||
}
|
||||
|
||||
// Windows console output is collected by a child helper that owns the
|
||||
@@ -532,7 +538,7 @@ func runManagedShellWithPipes(command ProcessCommand, stopEventName string, stdo
|
||||
}
|
||||
startup := &windows.StartupInfo{Cb: uint32(unsafe.Sizeof(windows.StartupInfo{})), Flags: windows.STARTF_USESTDHANDLES | windows.STARTF_USESHOWWINDOW, ShowWindow: windows.SW_HIDE, StdInput: inputRead, StdOutput: stdoutWrite, StdErr: stderrWrite}
|
||||
var processInfo windows.ProcessInformation
|
||||
if err := windows.CreateProcess(applicationName, &commandLine[0], nil, nil, true, windows.CREATE_BREAKAWAY_FROM_JOB|windows.CREATE_NO_WINDOW|windows.CREATE_UNICODE_ENVIRONMENT, environment, workDir, startup, &processInfo); err != nil {
|
||||
if err := windows.CreateProcess(applicationName, &commandLine[0], nil, nil, true, managedProcessDetachedFlags|windows.CREATE_UNICODE_ENVIRONMENT, environment, workDir, startup, &processInfo); err != nil {
|
||||
closeOnError()
|
||||
return 1, fmt.Errorf("start managed shell: %w", err)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func TestConfigureManagedProcessCommandPreservesInheritedConsole(t *testing.T) {
|
||||
func TestConfigureManagedProcessCommandDetachesFromRunConsole(t *testing.T) {
|
||||
cmd := exec.Command("cmd.exe")
|
||||
configureManagedProcessCommand(cmd)
|
||||
if cmd.SysProcAttr == nil {
|
||||
@@ -21,9 +21,9 @@ func TestConfigureManagedProcessCommandPreservesInheritedConsole(t *testing.T) {
|
||||
if !cmd.SysProcAttr.HideWindow {
|
||||
t.Fatal("expected managed console window to stay hidden")
|
||||
}
|
||||
expected := uint32(windows.CREATE_NO_WINDOW | windows.CREATE_BREAKAWAY_FROM_JOB)
|
||||
expected := uint32(windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP | windows.CREATE_BREAKAWAY_FROM_JOB)
|
||||
if cmd.SysProcAttr.CreationFlags != expected {
|
||||
t.Fatalf("expected a hidden breakaway helper without a console, got %#x", cmd.SysProcAttr.CreationFlags)
|
||||
t.Fatalf("expected a detached breakaway helper isolated from Ctrl+C, got %#x", cmd.SysProcAttr.CreationFlags)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user