How can I feed commands to cmd.exe process via an input stream manually?

前端 未结 1 1991
名媛妹妹
名媛妹妹 2021-01-29 12:49

The question sounds a bit, dense. Here is a slightly longer version:

I need to have the main loop wait for user input and also have a process running and waiting for inp

相关标签:
1条回答
  • 2021-01-29 13:33

    I finally got it working. The reason for the erratic behaviour I described in the code samples was that the 3 streams were not accessed in an async'ed manner.

    To rectify I discarded the processOutput function and replaced it by two calls that the process itself triggers. MS documetation gives a fine example here

    I also made the StreamWriter sync, that feeds the process and the whole task it runs in as well.

    Here is the new code:

    private void startCMDtask()
    {
        var task = Task.Factory.StartNew(() => startCMD());
        cmdTask = task;
    }
    
    private async void startCMD()
    {
        try   { CMD.Start(); CMDrunning = true; } 
        catch { cmdErrOutput.Append("\r\nError starting cmd process."); 
                CMDrunning = false; }
    
        CMD.BeginOutputReadLine();
        CMD.BeginErrorReadLine();
    
        using (StreamWriter sw = CMD.StandardInput)
        {
    
            if (sw.BaseStream.CanWrite)
                do {  
                    try 
                    {
                        string cmd = cmdQueue.Dequeue();
                        if (cmd != null & cmd !="")  await sw.WriteLineAsync(cmd);
                    } 
                    catch { } 
                }   while (CMDrunning);
            try   { CMD.WaitForExit(); } 
            catch { cmdErrOutput.Append("WaitForExit Error.\r\n"); }
        }
    }
    

    This is how the process is set up now:

    private void setupCMD()
    {
        CMD = new Process();
        ProcessStartInfo info = new ProcessStartInfo();
        info.FileName = "cmd.exe";
        info.CreateNoWindow = true;
        info.RedirectStandardOutput = true;
        info.RedirectStandardError = true;
        info.RedirectStandardInput = true;
        info.UseShellExecute = false;
    
        CMD.OutputDataReceived += new DataReceivedEventHandler(cmdOutputDataHandler);
        CMD.ErrorDataReceived += new DataReceivedEventHandler(cmdErrorDataHandler);
        cmdOutput = new StringBuilder();
        cmdErrOutput = new StringBuilder();
        CMD.StartInfo = info;
    }
    

    And here are the output handlers:

    private static void cmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        if (!String.IsNullOrEmpty(outLine.Data))
        {  // Add the text to the collected output.
            cmdOutput.Append(Environment.NewLine + outLine.Data);
        }
    }
    
    private static void cmdErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        if (!String.IsNullOrEmpty(outLine.Data))
        {  // Add the text to the collected error output.
            cmdErrOutput.Append(Environment.NewLine + outLine.Data);
        }
    }
    

    At the end of the user input porcessing this is how the input queue is ged and the output fetched:

        cmdUnDoStack.Push(cmd);
        Application.DoEvents();
        TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        Task.Factory.StartNew(() => updateOutputArea(uiScheduler));
    

    Using this little routine:

    private void updateOutputArea(TaskScheduler uiScheduler)
    {
        Task.Factory.StartNew(() =>
        {
            tb_output.AppendText(cmdOutput + "\r\n" + cmdErrOutput + "\r\n");
            cmdOutput.Clear();
            cmdErrOutput.Clear();
        }, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
    
    
        }
    

    And now for the special treament some of the good old commands like CLS or COLOR need.. ;-)

    0 讨论(0)
提交回复
热议问题