16

say i have the following code, using syscall to hide command line window

process := exec.Command(name, args...)
process.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
err := process.Start()
if err != nil {
    log.Print(err)
}

but when i compiled it and tried to run it in Windows, command line window showed up again

what can i do to prevent command line window from appearing?

PS i already know how to compile golang source into a Windows GUI executable using go build -ldflags -H=windowsgui, but doing so only ensures the program itself doesn't bring up a command line window, Exec will show those windows anyway

Shannon Matthews
  • 9,649
  • 7
  • 44
  • 75
jm33_m0
  • 595
  • 2
  • 9
  • 17
  • Can you please be more precise about which prompt you are trying to hide? You already stated the correct commands hide both, the Go command line window and the windows spawned by exec. My best guess at the moment is that the command you are executing spawns an additional window. – Marco Feb 28 '17 at 07:02
  • It's possible too, I am going to check which command spawns the command prompt (disappears very soon though) – jm33_m0 Mar 01 '17 at 01:21
  • @jm33_m0 I have just tried, the sysProcAttr method in your question and it is a vast improvement -windows only flash briefly. Did you ever find a solution to prevent the flash completely – SwiftD Sep 11 '17 at 14:08

3 Answers3

27

There is a better solution, which can run exec.Command() without spawn a visible window, ( ͡° ͜ʖ ͡°).

Here is my code:

Firstly import "syscall"

cmd_path := "C:\\Windows\\system32\\cmd.exe"
cmd_instance := exec.Command(cmd_path, "/c", "notepad")
cmd_instance.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd_output, err := cmd_instance.Output()

Origin: https://www.reddit.com/r/golang/comments/2c1g3x/build_golang_app_reverse_shell_to_run_in_windows/

Yu Chen
  • 386
  • 3
  • 3
  • This actually answers the question. Thanks! – Karel Bílek Apr 01 '18 at 15:07
  • It is probably safer to use `os.Getenv("windir")` for the directory though. – Karel Bílek Apr 01 '18 at 15:15
  • @KarelBílek Yeah, but I haven't seen that a cmd in windows system doesn't locate in that place :) . – Yu Chen Apr 16 '18 at 12:59
  • I'm not sure how should I thank you. Since yesterday I've tried at least 50 ways of hiding this terminal window. I was frustrated with the terminal window and I was starting to hate black color in real life. Thanks again. You are a saviour. – Sunil Kumar Nov 05 '19 at 06:22
  • Don't forget to add the flag when build `go build -ldflags="-H windowsgui" main.go` – Mike S Jan 23 '23 at 05:43
6

Instead of hiding the console window, you should be able to prevent it with:

cmd := exec.Command(...)
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000} // CREATE_NO_WINDOW
Andrew McKinlay
  • 2,431
  • 1
  • 24
  • 27
  • This seems to work in Windows, but in WSL ubuntu, when I compiled (with an older version of golang), I got `csrc/sclaunch.go:759:42: unknown field 'CreationFlags' in struct literal of type syscall.SysProcAttr` – philwalk May 07 '21 at 15:44
  • CreationFlags is only for Windows, not Linux – Andrew McKinlay May 07 '21 at 19:17
5

If you build with -ldflags -H=windowsgui, each exec.Command will spawn a new console window.

If you build without the flag, you get one console window and all exec.Commands print into that one.

My current solution is thus to build without the flag, i.e. have a console window on program start, then immediately hide the console window with this code at the start of my program:

import "github.com/gonutz/w32/v2"

func hideConsole() {
    console := w32.GetConsoleWindow()
    if console == 0 {
        return // no console attached
    }
    // If this application is the process that created the console window, then
    // this program was not compiled with the -H=windowsgui flag and on start-up
    // it created a console along with the main application window. In this case
    // hide the console window.
    // See
    // http://stackoverflow.com/questions/9009333/how-to-check-if-the-program-is-run-from-a-console
    _, consoleProcID := w32.GetWindowThreadProcessId(console)
    if w32.GetCurrentProcessId() == consoleProcID {
        w32.ShowWindowAsync(console, w32.SW_HIDE)
    }
}

See this thread for the details about the process ID stuff.

What happens is that all exec.Commands now print their output to the hidden console window instead of spawning their own.

The compromise here is that your program will flash a console window once when you start it, but only for a brief moment before it goes into hiding.

gonutz
  • 5,087
  • 3
  • 22
  • 40
  • 1
    I had only seen an issue on win 7, not 8 but I just retested this on a windows 7 machine and it does indeed work there as described - I'm not sure why I got an error when i tried previously - comment removed to avoid confusion. answer upvoted as I will now be using this approach – SwiftD Oct 01 '17 at 15:09
  • Also relevant: https://stackoverflow.com/a/494000/278842 – Christopher Oezbek Aug 30 '22 at 12:45