windows - 使用 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”会选择新的控制台,但这里没有运气。仍然没有任何显示。
我在这里错过了什么或做了一些愚蠢的事情吗?
一些帮助将不胜感激:)
解决方案
推荐阅读
- python - 抛出错误:遇到未解决的自定义操作:Normalize.Node number 0 (Normalize) 准备失败
- c# - 如何从 GridView 中的特定行下载文件
- linux - 了解 Git 命令
- android-gridview - 我的网格视图中有一个按钮,我想在单击按钮(位于网格视图内部)时将数据发送到另一个活动
- java - 避免基本数学运算符的正则表达式
- php - PHP中简单HTML dom解析器中元素的纯文本和内部文本有什么区别?
- java - 从带有消息和收件人的 Android 应用程序启动 Telegram 应用程序
- jpa - JPA:单独更新映射表
- android - Flutter:如何防止重建整个 Reorderable Listview?
- c# - 插入后DataGridView不更新显示