【问题标题】:Equivalent to Process.Start() without separate arguments等效于没有单独参数的 Process.Start()
【发布时间】:2016-08-02 02:58:00
【问题描述】:

我正在编写一个运行任意命令所需的简单应用程序,例如:

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

Process.Start() 将是完美的,除非它需要将参数作为单独的参数给出。最初我以为我可以在第一个空格字符上拆分字符串,但如果可执行路径被引用并包含空格怎么办?有没有像 Process.Start() 这样的东西,它允许你给它一个字符串,带或不带参数,然后让它像粘贴到命令提示符一样执行它?

【问题讨论】:

标签: c# .net command-line process


【解决方案1】:

为什么不直接通过 cmd /C 运行所有内容?

Process.Start("cmd", "/C " + command);

【讨论】:

  • 我为什么不呢?首先,这看起来非常h​​acky。让命令提示符充当中间人可能会导致与您要运行的进程通信时出现问题;考虑标准输入/标准输出/标准错误并返回代码。此外,您需要担心正确转义命令。这通常是一个警告信号,表明有更好的方法。我更熟悉 linux 编码,因为这是一项非常基本且常见的任务。
  • 同意这看起来很老套,但我花了一段时间寻找更清洁的替代方案,但找不到任何东西。然后选项是自己解析命令以将可执行文件与参数分开,或者更明显的是,修改您的应用程序以将参数与命令分开捕获。如果您只想完成工作,使用 CMD 可能是不错的选择。
  • 这似乎是一个根本性的疏忽。从另一个程序启动一个程序是如此原始的事情,它应该是非常微不足道的。将可执行文件和参数作为单个字符串将使程序更简单。我正在编写的程序本质上只是一个登录脚本处理器,所以它的全部意义在于启动从配置文件中读取的其他脚本和程序。
  • 不是疏忽。这是 shell parser 的基本功能。
【解决方案2】:

直接调用CreateProcess函数即可:

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();
    }
}

【讨论】:

  • 看到原生 API 接受命令字符串非常有趣,但让我补充一点,这个问题问的有点多……此时可能还想使用命令行重定向运营商:-)
【解决方案3】:

您可以使用Path 类来评估命令行字符串并像这样运行Process.Start

// 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);

这样的事情应该可以做到,但上面的代码未经测试......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-03
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多