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 :)