Golang调用windows.CreateProcessAsUser函数如何获取Stdout内容
解决Golang调用CreateProcessAsUser时重定向Stdout的问题
要通过windows.CreateProcessAsUser获取子进程的Stdout,核心是通过匿名管道重定向输出,同时正确配置句柄继承和进程启动参数。以下是可行的实现步骤和代码示例:
关键步骤说明
- 创建可继承的匿名管道:父进程持有读端,子进程持有写端作为Stdout/Stderr
- 配置
StartupInfo:指定子进程的标准输出为管道写端,并启用标准句柄重定向标志 - 调用
CreateProcessAsUser时开启句柄继承,确保子进程能获取管道句柄 - 父进程关闭管道写端后,读取管道读端获取子进程输出
完整代码示例
package main import ( "bytes" "fmt" "syscall" "unsafe" "golang.org/x/sys/windows" ) func main() { // 示例:获取当前进程Token(实际场景替换为目标用户Token) var token windows.Token err := windows.OpenProcessToken(windows.CurrentProcess(), windows.TOKEN_ALL_ACCESS, &token) if err != nil { fmt.Printf("OpenProcessToken failed: %v\n", err) return } defer token.Close() // 1. 创建可继承的匿名管道 var sa windows.SecurityAttributes sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 // 允许句柄被继承 var readHandle, writeHandle windows.Handle err = windows.CreatePipe(&readHandle, &writeHandle, &sa, 0) if err != nil { fmt.Printf("CreatePipe failed: %v\n", err) return } defer windows.CloseHandle(readHandle) defer windows.CloseHandle(writeHandle) // 2. 配置子进程StartupInfo,指定标准输出为管道写端 var si windows.StartupInfo si.Cb = uint32(unsafe.Sizeof(si)) si.Flags = windows.STARTF_USESTDHANDLES si.StdOutput = writeHandle si.StdError = writeHandle // 同时重定向Stderr到同一管道 // 3. 准备进程信息结构体 var pi windows.ProcessInformation defer func() { windows.CloseHandle(pi.Process) windows.CloseHandle(pi.Thread) }() // 转换命令行为UTF-16格式 cmdLine, err := windows.UTF16PtrFromString("cmd.exe /c echo Hello from child process") if err != nil { fmt.Printf("UTF16PtrFromString failed: %v\n", err) return } // 4. 调用CreateProcessAsUser启动子进程 err = windows.CreateProcessAsUser( token, nil, cmdLine, nil, nil, true, // 开启句柄继承 0, nil, nil, &si, &pi, ) if err != nil { fmt.Printf("CreateProcessAsUser failed: %v\n", err) return } // 5. 关闭父进程中的管道写端,否则读操作会一直阻塞 windows.CloseHandle(writeHandle) // 6. 读取管道中的子进程输出 var buf [4096]byte var output bytes.Buffer for { n, err := windows.ReadFile(readHandle, buf[:], nil) if err != nil { if err == syscall.ERROR_BROKEN_PIPE || err == windows.ERROR_HANDLE_EOF { break // 子进程关闭写端,读取完成 } fmt.Printf("ReadFile failed: %v\n", err) break } output.Write(buf[:n]) } // 等待子进程退出 windows.WaitForSingleObject(pi.Process, windows.INFINITE) fmt.Println("子进程输出:") fmt.Println(output.String()) }
注意事项
- 句柄继承:必须确保
SecurityAttributes.InheritHandle设为1,且CreateProcessAsUser的inheritHandles参数为true,否则子进程无法获取管道句柄 - 关闭写端:父进程在调用
CreateProcessAsUser后必须关闭管道写端,否则ReadFile会一直等待输入 - Token权限:获取的用户Token需要具备创建进程的必要权限(如
TOKEN_QUERY、TOKEN_DUPLICATE、CREATE_PROCESS等) - 错误处理:所有Windows API调用都要检查错误,尤其是管道创建、进程启动和文件读取环节
内容的提问来源于stack exchange,提问作者Tiki
相关产品推荐
相关产品推荐

