首页 > 解决方案 > 使用 ShellExecute 启动子进程并使用 AttachConsole 重用父控制台

问题描述

我有一个对我来说似乎相当简单的问题,但我无法让它在 Windows 上正常工作:

我在 Go 中运行控制台程序,一些命令需要作为提升的进程运行

这就是我以提升用户身份重新启动控制台程序的方式,它工作正常:

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

...但问题是它是在一个新的控制台窗口中开始的。我想关闭它并重新连接到父级。所以这里是代码:


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
}


在那一点上,Windows 似乎很高兴:它没有报告任何错误,并且闪烁的控制台出现和消失的速度与您刷新屏幕一样快。

我也相信子命令在等待用户输入时运行良好 - 我可以看到它仍在任务管理器中运行;但父控制台上没有显示任何内容。

所以我在想 go 运行时可能会将原始句柄保留在某个地方?我查看了初始化,由于这些文件在 Windows 上不存在,我认为它们应该是 go 运行时的神奇值:

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

所以我尝试使用相同的代码重新分配 os.Stdout 及其朋友,以防“/dev/stdout”会选择新的控制台,但这里没有运气。仍然没有任何显示。

我在这里错过了什么或做了一些愚蠢的事情吗?

一些帮助将不胜感激:)

标签: windowsgoconsolestdoutuac

解决方案


推荐阅读