10

私の実装の1つでは、コマンド/結果をcmdウィンドウとの間で送受信することになっているツールに取り組んでいます。すべてが正常に機能しますが、以下のユースケースは何もしません。アプリケーションが(結果を表示する代わりに)何かを待っているようです

私のツールから、pythonフォルダーに移動します。pythonフォルダーからpython.exeを起動しようとしましたが、この時点では、エディターは何もしません。それは単に待ち続けます。

よろしくお願いします。ビデオもここにリンクしています。私が何を言おうとしているのか理解しやすいでしょう。

ここでビデオを見る(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:それで、私はジェイミーの提案に従って少し前に進むことができました。UIはもう「ぶら下がっていません」。しかし、Pythonインタープリターにアクセスしても、インタープリターの出力はツールに表示されません。なぜそれが起こっているのかという提案はありますか?

4

3 に答える 3

21

この方法でシェルにコマンドを送信することはできません。info.Argumentsの文字列は、コマンドラインでプログラムに提供される引数です。cmd.exeシェルで一連のコマンドを実行してから終了する場合は、/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""""";

CmdおよびProcessStartInfo.Argumentsプロパティを参照してください。

Edit 3の問題に答えるには、おそらく出力に正しくフックしていません。StreamReaderのBaseStreamをフックしようとする代わりに、this.shellProcess.OutputDataReceived += ProcessOutputHandler;ProcessOutputHandlerがのような署名を持つStartを呼び出す前に、OutputDataReceivedイベントをフックしますpublic static void ProcessOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)。Startを呼び出した直後に、を呼び出しますthis.shellProcess.BeginOutputReadLine();。このプロセスは、エラー出力についても同様です。詳細については、 Process.BeginOutputReadLineメソッドおよびProcess.BeginErrorReadLineメソッドを参照してください。

それでも問題が解決しない場合は、試してみると何が得られますprocess.StartInfo.Arguments = @"/c ""python.exe -c ""import sys; print 'Test.';""""";か?

また、以下のコードは、シェル通信に必要な概念のほとんどを示しています。

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

注意すべき点の1つは、Form1_Loadイベントが完了しない場合、Invokeは完了するまでハングすることです。イベントに長時間実行されるコードがある場合は、BeginInvokeを使用して非同期的に呼び出すか、長時間実行されるコードで定期的にDoEventsを呼び出す必要があります。

編集

あなたのコメントによると、私はインタラクティブな提出で動作するようにコードを変更しました。ただし、問題があります。Pythonプロンプト(>>>)はStandardError出力で提供され、StandardInputをエコーし​​ません。また、回線を終了しません。これにより、プロンプトの検出が困難になり、プロセスが終了するか行の終わりが表示されるまでprocess_ErrorDataReceivedが起動しないため、プロンプト文字の出力が乱れます。

于 2012-08-01T22:04:05.657 に答える
3

あなたの質問には、アプリケーションが何にぶら下がっているのかを正確に把握するのに十分なコードがありません。コードには奇妙に見えるものがいくつかあります。たとえば、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にライフタイムの問題がある可能性があると私は信じています。より多くのコードを投稿すると、より良いガイダンスを提供できます。

于 2012-08-01T22:49:26.217 に答える
2

すべてのコードを表示することはできませんが、 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

于 2012-08-01T22:53:17.260 に答える