Print the output to the command window when the golang application is compiled with -ldflags -H = windowsgui

I have an application that usually runs in the background, so I compile it with

go build -ldflags -H=windowsgui <gofile> 

To check the version on the command line, I wanted to pass the -V flag to the command line to get a line containing the version to be printed on the command line, and then exit the application. I added the package and flag code. When I test it with

 go run <gofile> -V 

... he prints the version perfectly. When I compile exe, it just exits without typing anything. I suspect that the compilation flag causes it to not access the console and send my text to a bucket of bits.

I tried printing options on stderr and stdout using println and fprintf and os.stderr.write, but nothing appears from the compiled application. How can I try to print a line on the command line when compiling with these flags?

+8
go
source share
3 answers

The problem is that when a process is created using an executable file that has a โ€œsubsystem variableโ€ set to โ€œWindows โ€ in its PE header , the process has three standard descriptors that are closed and this is not associated with any console, regardless whether you run it from the console or not. (In fact, if you run an executable file that has a subsystem configured to "console" rather than from the console, the console is forcibly created for this process, and the process is bound to it. Usually you see it as a console window that pops up suddenly.)

Therefore, in order to print anything to the console from the Windows GUI process, you must explicitly connect this process to the console, which is bound to its parent process (if any), as described here . To do this, you call the AttachConsole API function. With 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 be really complete when AttachConsole() not working, this code should probably take one of these two paths:

  • Call AllocConsole() to create your own console window for it.

    He would say that it is rather useless for displaying version information, since the process usually ends after it is printed, and as a result, the user interface will appear in the console window and immediately disappear; experienced users will get a hint that they should re-launch the application from the console, but mere mortals will probably not cope.

  • Set up a GUI dialog that displays the same information.

    I think this is exactly what you need: note that displaying help / use messages in response to a user with some command line argument is often mentally connected to the console, but this is not a dogma: try running msiexec.exe /? on the console and see what happens.

+14
source share

The answer above was useful, but, alas, it did not work for me out of the box. After some 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 key point: after selecting / connecting the console, you need to get the stdout handle, open the file with this handle and assign it to the os.Stdout variable. If you need stdin, you need to repeat the same for stdin.

+1
source share

You can get the desired behavior without using -H = windowsgui; you basically create a standard application (with your console window) and 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 use it like this:

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

All Articles