【问题标题】:Sending commands to cmd prompt in C#在 C# 中向 cmd 提示符发送命令
【发布时间】:2025-12-14 08:45:01
【问题描述】:

对于我的一个实现,我正在开发一种工具,该工具应该向/从 cmd 窗口发送/检索命令/结果。一切正常,但下面的用例没有做任何事情。好像我的应用程序正在等待某些东西(而不是显示结果)

从我的工具导航到 python 文件夹。从 python 文件夹中,我尝试启动 python.exe,但此时,我的编辑器不执行任何操作。它只是继续等待。

为了您的好意,我也在此处链接视频。你们会更容易理解我想说的话。

View the Video here (on youtube)

我还附上了我目前拥有的代码。

            ProcessStartInfo info = new ProcessStartInfo("cmd.exe");

            string argument = null;
            if (!string.IsNullOrEmpty(startingDirectory) && System.IO.Directory.Exists(startingDirectory))
            {
               argument += @"cd\";
            }
            else
            {
                argument += "\"";
            }
            info.Arguments = argument;
            info.CreateNoWindow = true;
            info.RedirectStandardError = true;
            info.RedirectStandardInput = true;
            info.RedirectStandardOutput = true;
            info.UseShellExecute = false;
             this.shellProcess = System.Diagnostics.Process.Start(info);
            this.shellProcess.EnableRaisingEvents = true;
            //this.InputStream.AutoFlush = true;
            this.shellProcess.Exited += new EventHandler(ProcessExited);
            this.ErrorBeginRead();
            this.OutputBeginRead();

 private void OutputBeginRead()
    {
        this.shellProcess.StandardOutput.BaseStream.BeginRead(outputBuffer, 0, outputBuffer.Length, new AsyncCallback(this.OnOutputInput), this.shellProcess);
    }

        private void ErrorBeginRead()
    {
        this.shellProcess.StandardError.BaseStream.BeginRead(errorBuffer, 0, errorBuffer.Length, new AsyncCallback(this.OnErrorInput), this.shellProcess);
    }

谢谢!

编辑: 启动 python 只是一个例子。我也需要对其他正常的 cmd 行命令使用相同的方法。如果有人可以指出我在我拥有的代码中做错了什么或我必须做什么,以实现预期的功能,那就太好了。

编辑 2:正常的 cmd 命令运行良好。 python、perl等命令行工具不工作。

编辑 3:所以我设法按照 Jamie 的建议向前移动了一点。用户界面不再“挂起”。但是当我访问 python 解释器时,解释器的输出在我的工具中仍然不可见。任何建议为什么会发生这种情况?

【问题讨论】:

    标签: c# winforms cmd


    【解决方案1】:

    您不能以这种方式向 shell 发送命令。 info.Arguments 中的字符串是在命令行上提供给程序的参数。如果您希望 cmd.exe shell 执行一系列命令然后退出,您必须提供 /c 参数。如果您希望它执行多个命令,则必须将命令放在批处理文件中并执行该命令,或者将它们括在引号中并用 && 分隔它们,即info.Arguments = @"/c ""cd \ && dir""";。您永远不会返回的另一个问题是 cmd.exe 在没有任何或适当的参数的情况下默认以交互模式打开。 /c 选项告诉 cmd.exe 执行相关命令然后退出。

    此外,像 python 和 perl 这样的解释器在直接从 ProcessStartInfo 启动时有时会出现奇怪的行为。如果带有 perl.exe 的 info.Arguments = @"""MyPerlProgram.pl"""; 不起作用,您可能会发现有必要在 cmd.exe 中启动它们以使其正常运行,即 info.Arguments = @"/c ""perl.exe ""MyPerlProgram.pl""""";

    请参阅 CmdProcessStartInfo.Arguments Property

    要回答您的 Edit 3 问题,您可能没有正确连接到输出。不要尝试挂钩 StreamReader 的 BaseStream,而是在调用 Start 之前将 OutputDataReceived 事件与 this.shellProcess.OutputDataReceived += ProcessOutputHandler; 挂钩,其中 ProcessOutputHandler 具有类似 public static void ProcessOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 的签名。拨打 Start 后立即拨打 this.shellProcess.BeginOutputReadLine();。错误输出的过程也类似。请参阅Process.BeginOutputReadLine MethodProcess.BeginErrorReadLine Method 了解更多详情。

    如果还是有问题,试试process.StartInfo.Arguments = @"/c ""python.exe -c ""import sys; print 'Test.';""""";会得到什么?

    此外,下面的代码演示了 shell 通信的大部分必要概念:

    public static void Main()
    {
        using (Process process = new Process())
        {
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.WorkingDirectory = @"C:\";
            process.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "cmd.exe");
    
            // Redirects the standard input so that commands can be sent to the shell.
            process.StartInfo.RedirectStandardInput = true;
            // Runs the specified command and exits the shell immediately.
            //process.StartInfo.Arguments = @"/c ""dir""";
    
            process.OutputDataReceived += ProcessOutputDataHandler;
            process.ErrorDataReceived += ProcessErrorDataHandler;
    
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
    
            // Send a directory command and an exit command to the shell
            process.StandardInput.WriteLine("dir");
            process.StandardInput.WriteLine("exit");
    
            process.WaitForExit();
        }
    }
    
    public static void ProcessOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        Console.WriteLine(outLine.Data);
    }
    
    public static void ProcessErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        Console.WriteLine(outLine.Data);
    }
    

    您可能遇到导致问题的线程问题。我已经对此做了一些进一步的工作,并且能够在表单上获取一个文本框以使用以下代码进行更新:

    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Timers;
    
    namespace DummyFormsApplication
    {
        class ProcessLauncher : IDisposable
        {
            private Form1 form;
            private Process process;
            private bool running;
    
            public bool InteractiveMode
            {
                get;
                private set;
            }
    
            public ProcessLauncher(Form1 form)
            {
                this.form = form;
    
                process = new Process();
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.WorkingDirectory = @"C:\";
                process.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "cmd.exe");
    
                // Redirects the standard input so that commands can be sent to the shell.
                process.StartInfo.RedirectStandardInput = true;
    
                process.OutputDataReceived +=new DataReceivedEventHandler(process_OutputDataReceived);
                process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived);
                process.Exited += new EventHandler(process_Exited);
            }
    
            public void Start()
            {
                if (running == false)
                {
                    running = true;
                    InteractiveMode = true;
    
                    // Runs the specified command and exits the shell immediately upon completion.
                    process.StartInfo.Arguments = @"/c ""C:\python27\python.exe -i""";
    
                    process.Start();
    
                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine();
                }
            }
    
            public void Start(string scriptFileName)
            {
                if (running == false)
                {
                    running = true;
                    InteractiveMode = false;
    
                    // Runs the specified command and exits the shell immediately upon completion.
                    process.StartInfo.Arguments = string.Format(@"/c ""C:\python27\python.exe ""{0}""""", scriptFileName);
                }
            }
    
            public void Abort()
            {
                process.Kill();
            }
    
            public void SendInput(string input)
            {
                process.StandardInput.Write(input);
                process.StandardInput.Flush();
            }
    
            private void process_OutputDataReceived(object sendingProcess, DataReceivedEventArgs outLine)
            {
                if (outLine.Data != null)
                {
                    form.Invoke(form.appendConsoleTextDelegate, new object[] { outLine.Data });
                }
            }
    
            private void process_ErrorDataReceived(object sendingProcess, DataReceivedEventArgs outLine)
            {
                if (outLine.Data != null)
                {
                    form.Invoke(form.appendConsoleTextDelegate, new object[] { outLine.Data });
                }
            }
    
            private void process_Exited(object sender, EventArgs e)
            {
                running = false;
            }
    
            public void Dispose()
            {
                if (process != null)
                {
                    process.Dispose();
                }
            }
        }
    }
    

    我创建了一个表单并在表单中添加了一个文本框和以下代码:

        public delegate void AppendConsoleText(string text);
        public AppendConsoleText appendConsoleTextDelegate;
    
        private void Form1_Load(object sender, EventArgs e)
        {
            appendConsoleTextDelegate = new AppendConsoleText(textBox1_AppendConsoleText);
            using (ProcessLauncher launcher = new ProcessLauncher(this))
            {
                launcher.Start();
    
                launcher.SendInput("import sys;\n");
                launcher.SendInput("print \"Test.\";\n");
                launcher.SendInput("exit()\n");
            }
        }
    
        private void textBox1_AppendConsoleText(string text)
        {
            textBox1.AppendText(string.Format("{0}\r\n", text));
        }
    

    需要注意的一点是,如果 Form1_Load 事件没有完成,Invoke 将一直挂起,直到它完成。如果事件中有长时间运行的代码,则需要使用 BeginInvoke 异步调用,或者在长时间运行的代码中定期调用 DoEvents。

    编辑

    根据您的评论,我已修改代码以处理交互式提交。但是,有一个问题。 Python 提示符 (>>>) 在 StandardError 输出中提供,它不回显 StandardInput。它也不会终止线路。这使得检测提示变得困难,并且由于 process_ErrorDataReceived 直到进程结束或看到行结束才会触发,从而导致提示字符的一些乱序输出。

    【讨论】:

    • 嗨 Jamie,您能否提供一个示例或链接,可能会有所帮助?
    • 使用示例 info.Arguments 为 cmd.exe 更新了答案,并添加了为什么进程永远不会自行终止的解释。
    • 用 Edit 3 的解决方案更新了答案。
    • 嗨@Gagan。如果我的答案的最终更新对你有用,你能接受它作为你问题的答案吗?
    • 嗨,杰米,不幸的是它不适合我。我很抱歉,但我仍在努力..
    【解决方案2】:

    您的问题中没有足够的代码来准确确定您的应用程序挂在什么上面。您的代码中有一些看起来很奇怪的东西。例如,为什么要开始自己的错误和输出读取循环,而不是使用 Process 类中内置的循环?像这样:

    var shellProcess = System.Diagnostics.Process.Start(info);
    shellProcess.EnableRaisingEvents = true;
    shellProcess.Exited += ProcessExited;
    
    shellProcess.OutputDataReceived += ShellProcess_OutputDataReceived;
    shellProcess.ErrorDataReceived  += ShellProcess_ErrorDataReceived;
    shellProcess.BeginOutputReadLine();
    shellProcess.BeginErrorReadLine();
    
    void ShellProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
    {
        // Do Something
    }
    
    void ShellProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        // Do Something
    }
    

    由于您的错误和输出异步事件没有触发,这让我相信 shellProcess 可能存在终身问题。如果您发布更多代码,我们可以提供更好的指导。

    【讨论】:

      【解决方案3】:

      我看不到您的所有代码,但您可以轻松使用 Steam 对象向 您创建的 CMD 窗口写入/发送命令。例如:

      StreamWriter inputStream = shellProcess.StandardInput;
      //send command to cmd prompt and wait for command to execute with thread sleep
      inputStream.WriteLine("echo "CMD just received input");
      inputStream.Flush();
      

      例如,在上面的示例中,命令提示符将收到 echo 命令,就像在窗口中输入的一样。要显示输出,您必须创建 StreamReader 对象并将其分配给进程的 StandardOutput

      【讨论】: