如何通过手动输入流食到的cmd.exe进程命令? [英] How can I feed commands to cmd.exe process via an input stream manually?

查看:189
本文介绍了如何通过手动输入流食到的cmd.exe进程命令?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题听起来有点,密实。这里是一个稍长版本

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 input from a stream to which the user input is to be sent.

全文:我建立一个Cmd的模拟器,并在第一一切罚款看着:用户输入一个命令,它就会回显到输出区,处理和STDOUT和StdErrOut被捕获并还加到输出文本框

Full story: I'm building a Cmd emulator and at first everything looked fine: The user enters a command, it gets echoed to the output area, processed and StdOut and StdErrOut are captured and also added to the output TextBox.

唯一的问题是,即,当CMD过程的创建和单独启动每一个命令,没有状态保持。不管变量还是代码页,也没有工作目录等等。

The only problem was, that, as the cmd process was created and started separately for each command, no state was kept. Neither variables nor codepage nor working directory etc..

所以我决定发明一个小黑客:输入一个左或右括号启动和停止收集的命令,而不是执行它们的。右括号的命令列表后(批处理)在processBatch方法用于他们都喂到CMD进程争夺其重定向输入。工作得很好。

So I decided to invent a little hack: Entering an opening or closing parenthesis starts and stops collecting the commands instead of executing them. After the closing parenthesis the list of commands ('batch') is used in the processBatch method to feed them all to the cmd process vie its redirected input. Worked fine.

唯一的问题是,很明显,现在我的状态,但失去立即响应,所以任何错误就不会弹出直到批次运行。

The only problem was, obviously, now I got state but lost immediate response, so any errors wouldn't pop up until the batch was run.

所以我决定好的部分结合起来,那么,我知道我遇到麻烦时,我意识到,为了保持两个循环的工作和放大器;等待我必须使用线程。我已经好几年没..

So I decided to combine the good parts and, well, I knew I was heading for trouble when I realized, that to keep two loops working & waiting I have to use threading. Which I haven't done in years..

在布局完成我选择了用户输入和startCMDtask()在main()循环等待运行startCMD()在任务。这里输入流被扫描,直到有数据,然后将CMD过程是处理他们..

In the layout I chose the main() loop waits for user input and startCMDtask() runs startCMD() in a task. Here the input stream is scanned until is has data and then the cmd process is to process them..

但是它不工作

List<string> batch = new List<string>();

public volatile string output = "+";
public volatile string outputErr = "-";

Process CMD;
Task cmdTask;

volatile Queue<string> cmdQueue = new Queue<string>();
volatile public bool CMDrunning = false;



Tthis工作得很好。

Tthis works just fine

private void processBatch()
{
    Process p = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = "cmd.exe";
    info.RedirectStandardOutput = true;
    info.RedirectStandardError = true;
    info.RedirectStandardInput = true;
    info.UseShellExecute = false;

    p.StartInfo = info;
    p.Start();

    using (StreamWriter sw = p.StandardInput)
    {
        if (sw.BaseStream.CanWrite)
            foreach(string line in batch) sw.WriteLine(line);
    }
    output = "^"; outputErr = "~";
    try { output = p.StandardOutput.ReadToEnd(); } catch { }
    try { outputErr = p.StandardError.ReadToEnd(); } catch { }
    try { p.WaitForExit(); } catch { }
    tb_output.AppendText(output + "\r\n" + outputErr + "\r\n");
}



这些做不大,但几乎..

These don't quite, but almost..

private void setupCMD()
{
    CMD = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = "cmd.exe";
     // info.Arguments = "/K";   // doesn't make a difference
    info.CreateNoWindow = true;
    info.RedirectStandardOutput = true;
    info.RedirectStandardError = true;
    info.RedirectStandardInput = true;
    info.UseShellExecute = false;
    CMD.StartInfo = info;
}


private void startCMDtask()
{
    var task = Task.Factory.StartNew(() => startCMD());
    cmdTask = task;
}


private void startCMD()
{
    try   { CMD.Start(); CMDrunning = true; } 
    catch { output = "Error starting cmd process.\r\n"; CMDrunning = false; }

    using (StreamWriter sw = CMD.StandardInput)
    {
        if (sw.BaseStream.CanWrite)
            do {  
                try 
                {
                    string cmd = cmdQueue.Dequeue();
                    if (cmd != null & cmd !="")
                    {
                        sw.WriteLine(cmd);
                        processOutputStreams();
                    }
                } 
                catch {} 
            } while (CMDrunning);
    }


private void processOutputStreams()
{
    string newOutput = ""; string newOutputErr = "";
    while (CMD.StandardOutput.Peek() > 0)
              newOutput += (char)(CMD.StandardOutput.Read());

    newOutput += "!?";    // at this point stdout is correctly captured  (1)  

    try {
      while (CMD.StandardError.Peek() > 0)    // from here execution jumps away (2)
      { newOutputErr += (char)(CMD.StandardError.Read()); }
    } catch { 
        newOutputErr = "?";   // never comes here
    }



    lock (output)    // no noticable difference
    lock (outputErr) //
    {                // if I jump here (3) from (1) the result is displayed
                     // but not if i comment out the 2nd while loop (2)
        if (newOutput != null & newOutput != "") output += newOutput + "\r\n";
        if (newOutputErr != null & newOutputErr != "") outputErr += newOutputErr + "\r\n";
    }
}

这是从主输入处理器通话主题:

This is the call from the input processor in the main thread:

lock (cmdQueue) cmdQueue.Enqueue(cmd);



我不知道哪一部分的问题是:这个过程中,CMD外壳,输入流,输出流,线程,锁或轮流这一切??

I have no idea which part is the problem: the process, the cmd shell, the input stream, the output stream, the threading, the locks or all of it in turns..??

推荐答案

我终于得到了它的工作。究其原因,因为我的代码示例中所描述的反常行为是,3个数据流并没有在async'ed方式访问。

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.

要纠正我丢弃processOutput功能通过这个过程本身会触发两个电话取代了它。 MS documetation给出了一个很好的例子的这里

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

我也做了StreamWriter的同步,即饲料的过程,它也运行整个任务。

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

下面是新的代码:

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



在用户输入结束porcessing这是怎么了输入队列盖德和输出牵强:

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


    }

和现在的特殊黄泥一些历久弥新的命令,像CLS或颜色需要..; - )

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

这篇关于如何通过手动输入流食到的cmd.exe进程命令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆