11

I'm writing a simple application that's required to run arbitrary commands, for example:

powershell -File myscript.ps1
cmd /C "ping localhost"

Process.Start() would be perfect except it requires the arguments be given as a separate parameter. Initially I thought I could just split the string on the first space character, but then what if the executable path is quoted and contains spaces? Is there something like Process.Start() which allows you to just give it a string, with or without arguments, and just have it execute it as if it was pasted to a command prompt?

TomWardrop
  • 557
  • 5
  • 12

3 Answers3

7

Why don't you just run everything through cmd /C?

Process.Start("cmd", "/C " + command);
itsme86
  • 19,266
  • 4
  • 41
  • 57
  • 1
    Why don't I? First of all, this seems awfully hacky. Having the command prompt act as a middleman can cause problems with communicating with the process you want to run; think stdin/stdout/stderr and return codes. In addition, you need to worry about properly escaping the command. This is normally a warning sign that there's a better way. I'm more familiar with coding for linux where this stuff is trivial because it's such a basic and common task. – TomWardrop Mar 26 '14 at 04:20
  • Agree it seems hacky, but I've spent a while looking for a cleaner alternative and can't find anything. Options then are to parse the command yourself to separate executable from parameters or, perhaps more obviously, modify your app to capture parameters separately from the command. If you want to just get job done though, using CMD may be good option. – andyb Mar 26 '14 at 09:51
  • 1
    It just seems like such a fundamental oversight. Launching one program from another is such a primitive thing, it should be super trivial. Having the executable and arguments as a single string would make the program much simpler. The program I'm writing is essentially just a logon script processor, so the whole point of it is to launch other scripts and programs which are read in from a configuration file. – TomWardrop Mar 28 '14 at 06:18
  • It is **not** an oversight. It is the basic of what a shell **parser** does. –  Aug 02 '16 at 07:10
6

Just call the CreateProcess function directly:

public static class Native
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct STARTUPINFO
    {
        public Int32 cb;
        public string lpReserved;
        public string lpDesktop;
        public string lpTitle;
        public Int32 dwX;
        public Int32 dwY;
        public Int32 dwXSize;
        public Int32 dwYSize;
        public Int32 dwXCountChars;
        public Int32 dwYCountChars;
        public Int32 dwFillAttribute;
        public Int32 dwFlags;
        public Int16 wShowWindow;
        public Int16 cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public int dwProcessId;
        public int dwThreadId;
    }

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool CreateProcess(
        string lpApplicationName,
        string lpCommandLine,
        IntPtr lpProcessAttributes,
        IntPtr lpThreadAttributes,
        bool bInheritHandles,
        uint dwCreationFlags,
        IntPtr lpEnvironment,
        string lpCurrentDirectory,
        [In] ref STARTUPINFO lpStartupInfo,
        out PROCESS_INFORMATION lpProcessInformation);

    public static void CreateProcessFromCommandLine(string commandLine)
    {
        var si = new STARTUPINFO();
        var pi = new PROCESS_INFORMATION();

        CreateProcess(
            null,
            commandLine,
            IntPtr.Zero,
            IntPtr.Zero,
            false,
            0,
            IntPtr.Zero,
            null,
            ref si,
            out pi);
    }
}

internal class Program
{
    public static void Main()
    {
        Native.CreateProcessFromCommandLine("ping google.com -t");

        Console.Read();
    }
}
ken2k
  • 48,145
  • 10
  • 116
  • 176
  • Very interesting to see that the native API accepts a command string, but let me add that the question is asking a bit too much... At this point one may also want to use command-line redirection operators :-) –  Aug 02 '16 at 07:16
-1

You can use the Path class to evaluate the command line string and run through Process.Start like this:

// This is our command string
var cmd = @"C:\Program Files\Sub folder 1\executable name.exe -arg1 -arg2 -argN"; 

var exeName = Path.GetFileName(cmd); // "executable name.exe -arg1 -arg2 -argN"

var borderPos = exeName.IndexOf(".exe") // or .cmd, .bat, etc.
borderPos = borderPos == -1 ? exeName.IndexOf(" ") : borderPos + 4;

var arguments = exeName.Substring(borderPos).Trim();
var program = cmd.Substring(0, cmd.Length - arguments.Length);

Process.Start(program, aguments);

Something like this should do it, but the above codes are untested...

Bozhidar Stoyneff
  • 3,576
  • 1
  • 18
  • 28