Start a child process with ShellExecute and reuse parent console with AttachConsole

Viewed 180

I have a problem which seems rather simple to me, but I cannot make it work properly on Windows:

I'm running a console program in Go, and some commands will need to run as an elevated process

That's how I restart my console program as an elevated user, and it works fine:

func RunElevated() error {
    verb := "runas"
    exe, _ := os.Executable()
    cwd, _ := os.Getwd()
    args := "--as-child " + strings.Join(os.Args[1:], " ")

    verbPtr, _ := syscall.UTF16PtrFromString(verb)
    exePtr, _ := syscall.UTF16PtrFromString(exe)
    cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
    argPtr, _ := syscall.UTF16PtrFromString(args)

    var showCmd int32 = 1 //SW_NORMAL

    // also tried with zero as first parameter; I think it's about GUI window, not console?
    err := windows.ShellExecute(GetConsoleWindow(), verbPtr, exePtr, argPtr, cwdPtr, showCmd)
    if err != nil {
        return err
    }
    return nil
}

// GetConsoleWindow from windows kernel32 API
func GetConsoleWindow() windows.Handle {
    getConsoleWindow := kernel32.NewProc("GetConsoleWindow")
    ret, _, _ := getConsoleWindow.Call()
    return windows.Handle(ret)
}

... but the problem is that it's starting in a new console window. I'd like to close it and reattach to the parent one. So here's the code:


const (
    ATTACH_PARENT_PROCESS windows.Handle = 0x0ffffffff
)

var (
    kernel32 = syscall.NewLazyDLL("kernel32.dll")
)

// AttachParentConsole detach from the current console and attach to the parent process console
func AttachParentConsole() error {
    err := FreeConsole()
    if err != nil {
        return err
    }
    err = AttachConsole(ATTACH_PARENT_PROCESS)
    if err != nil {
        return err
    }
    return nil
}

// FreeConsole from windows kernel32 API
func FreeConsole() error {
    freeConsole := kernel32.NewProc("FreeConsole")
    ret, _, _ := freeConsole.Call()
    if ret == 0 {
        return syscall.GetLastError()
    }
    return nil
}

// AttachConsole from windows kernel32 API
func AttachConsole(consoleOwner windows.Handle) error {
    attachConsole := kernel32.NewProc("AttachConsole")
    ret, _, _ := attachConsole.Call(uintptr(consoleOwner))
    if ret == 0 {
        return syscall.GetLastError()
    }
    return nil
}


At that point, Windows seems happy enough: it reports no error, and a flashing console appear and disappear as fast as you can refresh the screen.

I also believe the child command runs fine as it waits from an input from the user - I can see it in still running in the task manager; but nothing is displayed on the parent console.

So I was thinking the go runtime might keep the original handle somewhere? I had a look at the initialisation, and since these files don't exist on Windows I'm thinking they should be magic values for the go runtime:

var (
    Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
    Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
    Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)

So I tried re-assigning os.Stdout and its friends using the same code in case the "/dev/stdout" would pick up the new console, but no luck here. Still nothing displayed.

Am I missing something or doing something stupid here?

Some help would be much appreciated :)

0 Answers
Related