0

This is my code:

var command = "ping 127.0.0.1 && exit"
var sI syscall.StartupInfo
var pI syscall.ProcessInformation

argv := syscall.StringToUTF16Ptr(os.Getenv("windir")+"\\system32\\cmd.exe /C " + command)
syscall.CreateProcess(
    nil,
    argv,
    nil,
    nil,
    true,
    0x08000000,
    nil,
    nil,
    &sI,
    &pI)

Now I want get output result of executed code. Is there anyway?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Natasha
  • 1
  • 1
  • 3
    Before anything else, may I ask what's wrong with the standard tools to run a process in the Go stdlib: the code in the `os/exec` package? It's cross platform. – kostix May 23 '19 at 09:49
  • No, in windws I want do – Natasha May 23 '19 at 09:50
  • It works on Windows just fine. (By the way, you may try asking a question [over there](https://ru.stackoverflow.com/questions/tagged/golang) if you have troubles expressing your thoughts in English. If you will do that, please be sure to post a link there as a comment, thanks). – kostix May 23 '19 at 09:51
  • standard package ask for command like this []string{"/C", "del","C:\\file.bat"} because of it, this is so simple to run command like in cmd – Natasha May 23 '19 at 09:51
  • 2
    So you want [this](https://stackoverflow.com/a/13013520/720999). You may also explore the other results of [this search](https://www.google.com/search?q="golang+"os%2Fexec"+"cmd.exe"?hl=en). – kostix May 23 '19 at 09:54
  • Sir, thanks for your time but that not my question answer @kostix – Natasha May 23 '19 at 09:56
  • 2
    Well, that's the 1st part of the answer: it tells you how to start a shell command on Windows without resorting to syscall mumbo-jumbo. The next step is about collecting the output of the spawned process, but it's easy: just read the docs on the `os/exec` package and pick the solution which suits you. (Again, it may happen that I fail to properly understand what you're after. If yes, I suggest you to express yourself in Russian on another SO site I linked to—chances are you'll get the answer faster. Or hit `@kostix` on Telegram after all. :-)) – kostix May 23 '19 at 09:59
  • Give your Telegram @kostix, that's channel ) – Natasha May 23 '19 at 10:06

1 Answers1

4

As said in the comments of the question, the best way to execute a process is to use the os/exec package. But, if you need something very specific on windows, you can use the sys/windows package.

In order for your child process to communicate with you parent process, you must establish a piped communication. You can find explanations about this in this C++ answer for quite the same issue : How to read output from cmd.exe using CreateProcess() and CreatePipe()

Here is the solution in go to get your child process output.


func main() {
    var command = "ping 127.0.0.1 && exit"

    var (
        sI syscall.StartupInfo
        pI syscall.ProcessInformation

        stdOutPipeRead  windows.Handle
        stdOutPipeWrite windows.Handle
        stdErrPipeRead  windows.Handle
        stdErrPipeWrite windows.Handle
        stdInPipeRead   windows.Handle
        stdInPipeWrite  windows.Handle
    )

    sa := windows.SecurityAttributes {
        Length:             uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
        SecurityDescriptor: nil,
        InheritHandle:      1, //true
    }

    windows.CreatePipe(&stdOutPipeRead, &stdOutPipeWrite, &sa, 0)
    windows.CreatePipe(&stdErrPipeRead, &stdErrPipeWrite, &sa, 0)
    windows.CreatePipe(&stdWritePipeRead, &stdWritePipeWrite, &sa, 0)

    sI.Flags = windows.STARTF_USESTDHANDLES
    sI.StdErr = stdErrPipeWrite
    sI.StdOutput = stdOutPipeWrite
    sI.StdInput = stdOutPipeRead

    argv := windows.StringToUTF16Ptr(os.Getenv("windir")+"\\system32\\cmd.exe /C " +     command)
    windows.CreateProcess(
        nil,
        argv,
        nil,
        nil,
        true,
        0x08000000,
        nil,
        nil,
        &sI,
        &pI)

    windows.CloseHandle(stdOutPipeWrite)
    windows.CloseHandle(stdErrPipeWrite)
    windows.CloseHandle(stdInPipeWrite)

    stdErr := readPipe(stdErrPipeRead)
    stdOut := readPipe(stdOutPipeRead)

    windows.CloseHandle(stdOutPipeRead)
    windows.CloseHandle(stdErrPipeRead)
    windows.CloseHandle(stdInPipeWrite)
}

func readPipe(pipe windows.Handle) string {
    result := ""
    buf := make([]byte, 1024+1)
    var read int = 0
    err := windows.ReadFile(pipe, &buf[0], 1024, &read), 0)
    // read until you receive a broken pipe error
    for err == nil {
        result += string(buf[:read])
        err = windows.ReadFile(pipe, &buf[0], 1024, &read), 0)
    }
    return result
}