【问题标题】:How to write and read in a process without 'useshellexecute = false'如何在没有'useshellexecute = false'的进程中读写
【发布时间】:2014-12-30 08:34:42
【问题描述】:

我到处寻找,但找不到正确的答案。

我使用 VS2013 > C# > Windows Forms-Application

您可以在下面看到我的流程的工作版本。 但是我有两个小问题,我不知道如何解决。

  1. *.exe 是一种优化算法,它显示它执行的每次迭代以及它找到的当前最佳解决方案。 -> 但因为我有 'useshellexecute = false' 我在命令 shell 中看不到任何内容

  2. 用户可以随时按“Ctrl+C”中断算法,算法将停止并返回当前最佳解 -> 但因为我有 'useshellexecute = false' 我不能输入任何键命令

我该如何解决这个问题?? - 我需要查看交互并能够按“Ctrl+C”。 - 它不必在命令外壳中,我可以使用替代“界面”。 - 如果我设置'useshellexecute = true',我如何输入命令并读取所有行。

请注意:

P.StartInfo.Arguments

输入命令不起作用。 *.exe 将抛出“无效输入”错误。

有效的代码:

private void btn_Optimize_Start_Click(object sender, EventArgs e)
    {
        Process P = new Process();

        P.StartInfo.FileName = @Application.StartupPath + @"\Algorithm.exe";
        P.StartInfo.UseShellExecute = false;
        P.StartInfo.RedirectStandardInput = true;
        P.StartInfo.RedirectStandardOutput = true;
        P.StartInfo.RedirectStandardError = true;

        P.Start();
        //sets timelimit 30 min
        P.StandardInput.WriteLine("set lim tim 1800"); 
        //reads the modell for which an optimal solution has to be found 
        P.StandardInput.WriteLine("read modell.zpl");
        //command that starts the optimization algorithm
        P.StandardInput.WriteLine("optimize");         //this part can take hours
        //command that displays the solution
        P.StandardInput.WriteLine("display solution");
        //ends the *.exe
        P.StandardInput.WriteLine("quit");

        //saves all information in a log-file with which I can work
        string[] log = P.StandardOutput.ReadToEnd().Split(new string[] { Environment.NewLine }, StringSplitOptions.None);

        //opens a function, that formats the solution
        this.result_create(log);
    }

编辑 11.11.2014 / 线程化进程 / RichTextBox 中的输出:

private void btn_Optimize_Start_Click(object sender, EventArgs e)
    {
        Process P = new Process();

        P.StartInfo.FileName = @Application.StartupPath + @"\Algorithm.exe";
        P.StartInfo.UseShellExecute = false;
        P.StartInfo.RedirectStandardInput = true;
        P.StartInfo.RedirectStandardOutput = true;
        P.StartInfo.RedirectStandardError = true;
        //*** NEW *** Event Handler for the asynchron Output-Process
        P.OutputDataReceived += new DataReceivedEventHandler(this.Asyn_Process);

        P.Start();

        //*** NEW *** Starts asynchron Output-Process
        P.BeginOutputReadLine();

        //sets timelimit 30 min
        P.StandardInput.WriteLine("set lim tim 1800"); 
        //reads the modell for which an optimal solution has to be found 
        P.StandardInput.WriteLine("read modell.zpl");
        //command that starts the optimization algorithm
        P.StandardInput.WriteLine("optimize");         //this part can take hours
        //command that displays the solution
        P.StandardInput.WriteLine("display solution");
        //ends the *.exe
        P.StandardInput.WriteLine("quit");

        //*** DELETED ***
        //saves all information in a log-file with which I can work
        //string[] log = P.StandardOutput.ReadToEnd().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); 

        //opens a function, that formats the solution
        //this.result_create(log);
    }

    //*** NEW *** The asynchronous Process
    private void Asyn_Process(object sender, DataReceivedEventArgs e)
    {
        if (this.rTB_Log.InvokeRequired && e.Data != null)
        {
            //Anonym Invoke Function
            this.rTB_Log.Invoke(new MethodInvoker(delegate()
            {
                //Writes Output continuously in the RichTextBox
                this.rTB_Log.Text += e.Data + Environment.NewLine;
                //Scroll to End of RichTextBox continuously
                this.rTB_Log.SelectionStart = this.rTB_Log.Text.Length;
                this.rTB_Log.ScrollToCaret();                          
            }));
        }
        //When the process has finished (e.Data == null)
        else
        {
            //Anonym Invoke Function
            this.rTB_Log.Invoke(new MethodInvoker(delegate()
            {
                //Saves the RichTextBox-Content in a Text-File
                this.rTB_Log.SaveFile(Algorithm.log", RichTextBoxStreamType.PlainText);
            }));
        }
    }

【问题讨论】:

  • 去掉InvokeRequired。当您不需要时调用Invoke 不是错误,跳过该逻辑将是错误。

标签: c# input process output invoke


【解决方案1】:

首先,UseShellExecute = true 没有给您带来任何好处。这只是副作用——ShellExecute 与重定向不兼容,因此您会看到没有重定向会发生什么。

你的核心问题是你需要程序的输出去两个地方。在 Unix 上,您可以使用 tee 将输出发送到控制台和您选择的文件(或伪文件),您的应用程序将读取该文件以获取结果。

Windows 没有现成的工具可以做到这一点,但所有必要的部分都在那里。您想要的方法是:

  1. 使用重定向,而不是ShellExecute
  2. 连续读取应用程序输出,而不是ReadToEnd()
  3. 您从子应用程序中读取的所有内容也会显示给用户。将其发送到您自己的控制台窗口是可以的,但将其转储到文本框或解析它并绘制图表同样有效。
  4. 当用户想要中断搜索时,他们将使用您的 UI 来执行此操作,因为子进程不再从控制台读取其输入。而且您必须将其传递给子进程,就好像按下了 CTRL+C 一样。有一个 Win32 函数可以做到这一点,GenerateConsoleCtrlEvent。我认为没有任何 .NET 替代品,因此您可能必须使用 p/invoke。
  5. 更糟糕的是,如this answer 中所述,GenerateConsoleCtrlEvent 作用于整个进程组,而Process.Start 不使用创建新组的标志,因此您可能还需要 p/invoke @987654330 @。这意味着您还必须使用 p/invoke 来设置流重定向,这基本上意味着创建管道并将它们粘贴在传递给 CreateProcessSTARTUPINFO 结构中。这是很多工作。也许你会很幸运地共享进程组,并且组中没有其他人反应不好。文档还说“只有与调用进程共享同一控制台的组中的那些进程才能接收信号。换句话说,如果组中的进程创建了一个新的控制台,则该进程不会收到信号,它的子孙。”所以你的应用程序中也需要有一个控制台,要么使用控制台子系统,要么使用AllocConsole

【讨论】:

  • 感谢您提供的信息 - 我非常担心。- 我编辑了我的代码以连续和异步读取。 - 我还没有尝试过 GenerateConsoleCtrlEvent,因为这不是优先事项,所以它必须等到我有时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
  • 2011-04-05
  • 1970-01-01
相关资源
最近更新 更多