//go:build windows package main import ( "os" "syscall" "golang.org/x/sys/windows" ) // attachToParentConsole reconnects stdin/stdout/stderr to the console of the // process that launched us, if any. It is a no-op when the binary was started // from Explorer (double-click) — there is no parent console to attach to. // // Why we need this: the binary is built with -H=windowsgui, which means // Windows treats it as a GUI application and does NOT allocate a console // when it starts. That gives a clean double-click experience (no flashing // console window). But it also disconnects fmt.Println output when the // user runs us from cmd.exe / PowerShell. Calling AttachConsole here // re-attaches us to the parent's console so CLI output works as expected. func attachToParentConsole() { const ATTACH_PARENT_PROCESS = ^uint32(0) // (DWORD)-1 kernel32 := windows.NewLazySystemDLL("kernel32.dll") attachConsole := kernel32.NewProc("AttachConsole") r, _, _ := attachConsole.Call(uintptr(ATTACH_PARENT_PROCESS)) if r == 0 { // AttachConsole failed — most likely there is no parent console // (we were started from Explorer). That's fine: GUI mode is the // default UX and stdout/stderr just go nowhere. return } // Re-bind os.Stdout / os.Stderr to the freshly-attached console. if h, err := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE); err == nil && h != 0 { os.Stdout = os.NewFile(uintptr(h), "stdout") } if h, err := syscall.GetStdHandle(syscall.STD_ERROR_HANDLE); err == nil && h != 0 { os.Stderr = os.NewFile(uintptr(h), "stderr") } if h, err := syscall.GetStdHandle(syscall.STD_INPUT_HANDLE); err == nil && h != 0 { os.Stdin = os.NewFile(uintptr(h), "stdin") } }