package main import ( //This is a modified version of natefinch's npipe where I exposed the handle in PipeConn struct so that I can use it as needed "sepipe/npipe" "bytes" "fmt" "io" "os" "sync" "unsafe" "golang.org/x/sys/windows" ) const pipeAddr string = `\\.\pipe\mypipeee` var ( procCreateThread *windows.LazyProc = windows.NewLazySystemDLL("kernel32.dll").NewProc("CreateThread") ) func redirectStdout(to windows.Handle) { windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, to) os.Stdout = os.NewFile(uintptr(to), "") } func restoreStdout(f *os.File) { windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, windows.Handle(f.Fd())) os.Stdout = f } func redirectStderr(to windows.Handle) { windows.SetStdHandle(windows.STD_ERROR_HANDLE, to) os.Stderr = os.NewFile(uintptr(to), "") } func restoreStderr(f *os.File) { windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(f.Fd())) os.Stderr = f } func redirectIO(to windows.Handle) { redirectStdout(to) redirectStderr(to) } func restoreIO(origStdout, origStderr *os.File) { restoreStdout(origStdout) restoreStderr(origStderr) } func dup(src windows.Handle) (windows.Handle, error) { var ( dupedHandle windows.Handle err error ) err = windows.DuplicateHandle(windows.CurrentProcess(), src, windows.CurrentProcess(), &dupedHandle, 0, false, windows.DUPLICATE_SAME_ACCESS) return dupedHandle, err } func hello() { fmt.Println("Hello world from stdout") println("Hello world from stderr") } func makeCallback(f func()) uintptr { return windows.NewCallback(func() uintptr { f() return 0 }) } func createThread(rf func()) (windows.Handle, error) { var threadID uint32 r1, _, err := procCreateThread.Call(0, 0, makeCallback(rf), 0, 0, uintptr(unsafe.Pointer(&threadID))) if r1 == 0 { return 0, fmt.Errorf("error during CreateThread: %v", err) } fmt.Println("Started new thread with ID", threadID) return windows.Handle(r1), nil } func initPipeListener(output *bytes.Buffer, wg *sync.WaitGroup) { lis, err := npipe.Listen(pipeAddr) if err != nil { panic(err) } go func() { for { clConn, err := lis.Accept() if err != nil { println(err) continue } go func() { println(io.Copy(output, clConn)) wg.Done() }() } }() } func main() { saveStdout := os.Stdout output := new(bytes.Buffer) wg := sync.WaitGroup{} initPipeListener(output, &wg) pipeConn, err := npipe.Dial(pipeAddr) if err != nil { panic(err) } redirectStdout(pipeConn.Handle) wg.Add(1) tHandle, err := createThread(hello) if err != nil { panic(err) } windows.WaitForSingleObject(tHandle, windows.INFINITE) pipeConn.Close() wg.Wait() restoreStdout(saveStdout) fmt.Println(`"` + output.String() + `"`) }