[ACCEPTED]-Printing output to a command window when golang application is compiled with -ldflags -H=windowsgui-go

Accepted answer
Score: 22

The problem is that when a process is created 36 using an executable which has the "subsystem" variable in its PE header set 35 to "Windows", the process has 34 its three standard handles closed and it is not associated with any console—no matter if 33 you run it from the console or not. (In 32 fact, if you run an executable which has 31 its subsystem set to "console" not from 30 a console, a console is forcibly created 29 for that process and the process is attached 28 to it—you usually see it as a 27 console window popping up all of a sudden.)

Hence, to 26 print anything to the console from a GUI 25 process on Windows you have to explicitly 24 connect that process to the console which 23 is attached to its parent process (if it 22 has one), like explained here for instance. To 21 do this, you call the AttachConsole API function. With 20 Go, this can be done using the syscall package:

package main

import (
    "fmt"
    "syscall"
)

const (
    ATTACH_PARENT_PROCESS = ^uint32(0) // (DWORD)-1
)

var (
    modkernel32 = syscall.NewLazyDLL("kernel32.dll")

    procAttachConsole = modkernel32.NewProc("AttachConsole")

)

func AttachConsole(dwParentProcess uint32) (ok bool) {
    r0, _, _ := syscall.Syscall(procAttachConsole.Addr(), 1, uintptr(dwParentProcess), 0, 0)
    ok = bool(r0 != 0)
    return
}

func main() {
    ok := AttachConsole(ATTACH_PARENT_PROCESS)
    if ok {
        fmt.Println("Okay, attached")
    }
}

To 19 be truly complete, when AttachConsole() fails, this code 18 should probably take one of these two routes:

  • Call 17 AllocConsole() to get its own console window created for 16 it.

    It'd say this is pretty much useless 15 for displaying version information as the 14 process usually quits after printing it, and 13 the resulting user experience will be a 12 console window popping up and immediately 11 disappearing; power users will get a hint 10 that they should re-run the application 9 from the console but mere mortals won't 8 probably cope.

  • Post a GUI dialog displaying 7 the same information.

    I think this is just 6 what's needed: note that displaying help/usage 5 messages in response to the user specifying 4 some command-line argument is quite often 3 mentally associated with the console, but this is not a dogma 2 to follow: for instance, try running msiexec.exe /? at 1 the console and see what happens.

Score: 4

One problem with the solutions already posted 5 here is that they redirect all output to the 4 console, so if I run ./myprogram >file, the redirection to 3 file gets lost. I've written a new module, github.com/apenwarr/fixconsole, that 2 avoids this problem. You can use it like 1 this:

import (
        "fmt"
        "github.com/apenwarr/fixconsole"
        "os"
)

func main() {
        err := fixconsole.FixConsoleIfNeeded()
        if err != nil {
                fmt.Fatalf("FixConsoleOutput: %v\n", err)
        }
        os.Stdout.WriteString(fmt.Sprintf("Hello stdout\n"))
        os.Stderr.WriteString(fmt.Sprintf("Hello stderr\n"))
}
Score: 3

Answer above was helpful but alas it did 7 not work for me out of the box. After some 6 additional research I came to this code:

// go build -ldflags -H=windowsgui
package main

import "fmt"
import "os"
import "syscall"

func main() {
    modkernel32 := syscall.NewLazyDLL("kernel32.dll")
    procAllocConsole := modkernel32.NewProc("AllocConsole")
    r0, r1, err0 := syscall.Syscall(procAllocConsole.Addr(), 0, 0, 0, 0)
    if r0 == 0 { // Allocation failed, probably process already has a console
        fmt.Printf("Could not allocate console: %s. Check build flags..", err0)
        os.Exit(1)
    }
    hout, err1 := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE)
    hin, err2 := syscall.GetStdHandle(syscall.STD_INPUT_HANDLE)
    if err1 != nil || err2 != nil { // nowhere to print the error
        os.Exit(2)
    }
    os.Stdout = os.NewFile(uintptr(hout), "/dev/stdout")
    os.Stdin = os.NewFile(uintptr(hin), "/dev/stdin")
    fmt.Printf("Hello!\nResult of console allocation: ")
    fmt.Printf("r0=%d,r1=%d,err=%s\nFor Goodbye press Enter..", r0, r1, err0)
    var s string
    fmt.Scanln(&s)
    os.Exit(0)
}

The 5 key point: after allocating/attaching the 4 console, there is need to get stdout handle, open 3 file using this handle and assign it to 2 os.Stdout variable. If you need stdin you 1 have to repeat the same for stdin.

Score: 2

You can get the desired behavior without 4 using -H=windowsgui; you'd basically create 3 a standard app (with its own console window), and 2 hide it until the program exits.

func Console(show bool) {
    var getWin = syscall.NewLazyDLL("kernel32.dll").NewProc("GetConsoleWindow")
    var showWin = syscall.NewLazyDLL("user32.dll").NewProc("ShowWindow")
    hwnd, _, _ := getWin.Call()
    if hwnd == 0 {
            return
    }
    if show {
       var SW_RESTORE uintptr = 9
       showWin.Call(hwnd, SW_RESTORE)
    } else {
       var SW_HIDE uintptr = 0
       showWin.Call(hwnd, SW_HIDE)
    }
}

And then 1 use it like this:

func main() {
    Console(false)
    defer Console(true)
    ...
    fmt.Println("Hello World")
    ...
}
Score: 0

If you build a windowless app you can get 3 output with PowerShell command Out-String

.\\main.exe | out-string

your 2 build command may look like:

cls; go build -i -ldflags -H=windowsgui main.go; .\\main.exe | out-string;

or

cls; go run -ldflags -H=windowsgui main.go | out-string

No tricky 1 syscalls nor kernel DLLs needed!

More Related questions