Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: os: add a way to start process from full command line #29841

Closed
hallazzang opened this issue Jan 20, 2019 · 6 comments

Comments

@hallazzang
Copy link
Contributor

commented Jan 20, 2019

Currently it's not possible to start a process directly from full command line:

argv0p, err := UTF16PtrFromString(argv0)
if err != nil {
return 0, 0, err
}

if sys.Token != 0 {
err = CreateProcessAsUser(sys.Token, argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
} else {
err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
}

In the code above, argv0p wouldn't be nil, thus we can't use the functionality that CreateProcess(AsUser) API gives, by passing lpApplicationName a NULL to use lpCommandLine only.

Such kind of process is needed when dealing with, for example, UninstallString registry value. Keys under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall usually have UninstallString value, and that is just a command line string to execute when uninstalling a software(within control panel). And to execute that command line, I have to use CreateProcess Windows API directly, without seeing benefits of using os.StartProcess or exec.Command api.

Even using syscall.SysProcAttr.CmdLine, this behavior cannot be achieved. It is still needed to parse executable path manually.

Actually, there is a function commandLineToArgv that converts full command line into os.StartProcess-cousumable chunks:

go/src/os/exec_windows.go

Lines 159 to 174 in ff7b245

// commandLineToArgv splits a command line into individual argument
// strings, following the Windows conventions documented
// at http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
func commandLineToArgv(cmd string) []string {
var args []string
for len(cmd) > 0 {
if cmd[0] == ' ' || cmd[0] == '\t' {
cmd = cmd[1:]
continue
}
var arg []byte
arg, cmd = readNextArg(cmd)
args = append(args, string(arg))
}
return args
}

and works well for this situation(https://play.golang.org/p/lRhDlagni41), but unfortunately it is not exported.

So it would be good to have commandLineToArgv function exported, or to have seperate api that acts like system() function in C. Thanks.

@gopherbot gopherbot added this to the Proposal milestone Jan 20, 2019

@gopherbot gopherbot added the Proposal label Jan 20, 2019

@mvdan mvdan added the OS-Windows label Jan 20, 2019

@bradfitz

This comment has been minimized.

Copy link
Member

commented Jan 22, 2019

@jordanrh1

This comment has been minimized.

Copy link
Contributor

commented Jan 23, 2019

What if you pass cmd.exe as the application name and "/c" as the first argument?

@hallazzang

This comment has been minimized.

Copy link
Contributor Author

commented Jan 24, 2019

@jordanrh1 Ok, it works.

package main

import (
	"fmt"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	if len(os.Args) > 1 {
		for i, arg := range os.Args[1:] {
			fmt.Printf("arg #%d: %s\n", i, arg)
		}
		return
	}

	p, _ := os.Executable()
	cmdLine := fmt.Sprintf(`"%s" foo "bar"`, p)

	fmt.Printf("cmdLine: %s\n", cmdLine)

	cmd := exec.Command("cmd.exe")
	cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: fmt.Sprintf(`/c "%s"`, cmdLine)}
	output, err := cmd.CombinedOutput()
	fmt.Printf("output:\n%s\n", output)
	if err != nil {
		fmt.Printf("error: %+v\n", err)
	}
}

Output:

cmdLine: "C:\Users\...\main.exe" foo "bar"
output:
arg #0: foo
arg #1: bar

I think it'd be good to have this method documented somewhere. Thanks.

@hallazzang

This comment has been minimized.

Copy link
Contributor Author

commented Jan 24, 2019

BTW, this method spawns cmd.exe and then executes actual command, seems not the perfect way to get the job done. In my own opinion it's still worth having separate API.

@jordanrh1

This comment has been minimized.

Copy link
Contributor

commented Jan 24, 2019

I'm glad it worked for you. In this case, since you're given a full command line string, I think cmd.exe is the better option since you can pass the command line string unmodified to cmd.exe, and let the system parse it. I think the overhead of spawning cmd.exe is quite small since the system does this sort of thing all the time.

@hallazzang

This comment has been minimized.

Copy link
Contributor Author

commented Jan 26, 2019

Got it, I'll close this issue. Thank you for your support.

@hallazzang hallazzang closed this Jan 26, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can’t perform that action at this time.