在 C# 中将命令发送到 cmd 提示符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11767654/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 19:09:21  来源:igfitidea点击:

Sending commands to cmd prompt in C#

c#winformscmd

提问by Gagan

For one of my implementations I am working on a tool that is supposed to send/retrieve commands/results to/from the cmd window. Everything works fine but the Use case below fails to do anything. It seems as if my application is waiting for something (instead of displaying the result)

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

From my tool I navigate to the python folder . From the python folder I try to launch python.exe but at this point, my editor does not do anything. it simply keeps on waiting.

我从我的工具导航到 python 文件夹。我尝试从 python 文件夹启动 python.exe,但此时,我的编辑器没有做任何事情。它只是继续等待。

For your kind consideration I am also linking the video here. It would be easier for you guys to understand what I am trying to say.

出于您的考虑,我也在此处链接了视频。你们更容易理解我想说什么。

View the Video here (on youtube)

在此处查看视频(在 youtube 上)

I am also attaching the code that I currently have.

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

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

Thank you !

谢谢 !

EDIT: Launching python is just an example. I need to use the same method for other normal cmd line commands as well.It would be nice, if somebody can point what i am doing wrong with the code that I have or what I must do , in order to achieve the intended functionality.

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

EDIT 2 : The normal cmd commands are working perfectly. The command line tools like python,perl are not working .

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

Edit 3 : So I managed to do move a wee bit forward following Jamie's suggestions. The ui is not "hanging" anymore. but when i access the python interpreter , the interpreter's output is still not visible in my tool. Any suggestions why that might be happening ?

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

采纳答案by JamieSee

You cannot send commands to a shell this way. The string in info.Arguments is the arguments provided to the program on the command line. If you want the cmd.exe shell to execute a series of command and then quit you will have to provide the /c argument. If you have multiple commands that you want it to perform you will either have to put the commands in a batch file and execute that or enclose them in quotes and separate them with &&, i.e. info.Arguments = @"/c ""cd \ && dir""";. Your other issue with never returning is that cmd.exe opens in interactive mode by default when it is executed without any, or proper, arguments. The /c option tells cmd.exe to execute the relevant commands and then quit.

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

Additionally, interpreters like python and perl sometimes have weird behaviors when launched directly from ProcessStartInfo. If info.Arguments = @"""MyPerlProgram.pl""";with perl.exe doesn't work, you may find it necessary to launch them inside cmd.exe to get normal behavior out of them, i.e. info.Arguments = @"/c ""perl.exe ""MyPerlProgram.pl""""";.

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

See Cmdand ProcessStartInfo.Arguments Property.

请参阅CmdProcessStartInfo.Arguments 属性

To answer your Edit 3problem, you're probably not correctly hooking into the outputs. Instead of trying to hook the StreamReader's BaseStream, hook the OutputDataReceived event with this.shellProcess.OutputDataReceived += ProcessOutputHandler;before you call Start where ProcessOutputHandler has a signature like public static void ProcessOutputHandler(object sendingProcess, DataReceivedEventArgs outLine). Immediately after calling Start, call this.shellProcess.BeginOutputReadLine();. The process is similar for the error ouput as well. See Process.BeginOutputReadLine Methodand Process.BeginErrorReadLine Methodfor more details.

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

If you still have a problem, what do you get if you just try process.StartInfo.Arguments = @"/c ""python.exe -c ""import sys; print 'Test.';""""";?

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

Also, the code below demonstrates most of the necessary concepts for shell communication:

此外,下面的代码演示了 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);
}

You may have threading issues causing your problems. I've done some further work with this and was able to get a textbox on a form to update with the following code:

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

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

I created a form and added a textbox and the following code in the form:

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

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

One thing to note is that if the Form1_Load event doesn't complete, Invoke will hang until it does. If you have long-running code in an event you'll either need to invoke asynchronously using BeginInvoke, or periodically call DoEvents in your long-running code.

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

EDIT

编辑

Per your comment, I've modified the code to work with interactive submissions. There is, however, a problem. The python prompt (>>>) is provided on the StandardError output and it does not echo the StandardInput. It also does not terminate the line. This makes detecting a prompt difficult and causes some out of order output of the prompt characters due to the process_ErrorDataReceived not firing until either the process ends or a line end is seen.

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

回答by Scott

There's not enough code in your question to figure out exactly what your application is hanging on. There are some things in your code which look odd. For example, why are you starting your own error and output read loops instead of using the ones built into the Process class? Like this:

您的问题中没有足够的代码来准确确定您的应用程序挂在什么上面。您的代码中有一些看起来很奇怪的东西。例如,为什么要开始自己的错误和输出读取循环,而不是使用 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
}

Since your error and output async events are not firing, it leads me to believe that there might be a lifetime issue with the shellProcess. If you post more of your code, we can give better guidance.

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

回答by Chibueze Opata

I can't see all your code, but you can easilyuse Steam objects to write/send commands to the CMD Window created by you. e.g.:

我看不到您所有的代码,但是您可以轻松地使用 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();

In the above example for instance, Command prompt will receive the echocommand just like it was entered in the window. To show the output you will have to create StreamReaderobject and assign it to the process's StandardOutput.

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