2010-01-18 72 views
6

我正在使用C#并无法理解如何从子进程异步读取标准输出。我想要做的是创建一个执行应用程序的子进程,然后在文本框中显示从进程的stdout接收到的任何内容。我需要立即看到子进程中的每个输出字符,并且不能等待一行完成,因此我认为事件并不适合我的目的。你能告诉我一个完美的方法吗?C#异步读取子进程的stdout

我尝试过拨打电话Process.StandardOutput.BaseStream.BeginRead()并将回拨功能传递给它,但在此回拨功能中,我收到来自Process.StandardOutput.BaseStream.EndRead()的例外。

我的代码看起来像这样(子进程是一个脚本引擎 - 简称“SE” - 用于验证外部设备的功能脚本按顺序执行,每个脚本需要SE应用程序的一个实例)

private bool startScript() 
{ 
    // Starts the currently indexed script 

    if (null != scriptList) 
    { 

    if (0 == scriptList.Count || scriptListIndexer == scriptList.Count) 
    { 
     // If indexer equals list count then there are no active scripts in 
     // the list or the last active script in the list has just finished 
     return false;        // ## RETURN ## 
    } 
    if (ScriptExecutionState.RUNNING == scriptList[scriptListIndexer].executionState) 
    { 
     return false;        // ## RETURN ## 
    } 

    if (0 == SE_Path.Length) 
    { 
     return false;        // ## RETURN ## 
    } 

    SE_Process = new Process(); 
    SE_Process.StartInfo.FileName = SE_Path; 
    SE_Process.StartInfo.CreateNoWindow = true; 
    SE_Process.StartInfo.UseShellExecute = false; 
    SE_Process.StartInfo.RedirectStandardError = true; 
    SE_Process.StartInfo.RedirectStandardOutput = true; 
    SE_Process.EnableRaisingEvents = true; 
    SE_Process.StartInfo.Arguments = scriptList[scriptListIndexer].getParameterString(); 

    // Subscribe to process exit event 
    SE_Process.Exited += new EventHandler(SE_Process_Exited); 

    try 
    { 
     if (SE_Process.Start()) 
     { 
     // Do stuff 
     } 
     else 
     { 
     // Do stuff 
     } 
    } 
    catch (Exception exc) 
    { 
     // Do stuff 
    } 

    // Assign 'read_SE_StdOut()' as call-back for the event of stdout-data from SE 
    SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE, read_SE_StdOut, null); 

    return true;          // ## RETURN ## 

    } 
    else 
    { 
    return false;          // ## RETURN ## 
    } 
} 

private void read_SE_StdOut(IAsyncResult result) 
{ 
    try 
    { 
    int bytesRead = SE_Process.StandardOutput.BaseStream.EndRead(result); // <-- Throws exceptions 

    if (0 != bytesRead) 
    { 
     // Write the received data in output textbox 
     ... 
    } 

    // Reset the callback 
    SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE,  read_SE_StdOut, null); 
    } 
    catch (Exception exc) 
    { 
    // Do stuff 
    } 
} 

void SE_Process_Exited(object sender, EventArgs e) 
{ 

    // Keep track of whether or not the next script shall be started 
    bool continueSession = false; 

    switch (SE_Process.ExitCode) 
    { 
    case 0: // PASS 
    { 
     // Do stuff 
    } 

    ... 

    } 

    SE_Process.Dispose(); // TODO: Is it necessary to dispose of the process? 

    if (continueSession) 
    { 
    ts_incrementScriptListIndexer(); 

    if (scriptListIndexer == scriptList.Count) 
    { 
     // Last script has finished, reset the indexer and re-enable 
     // 'scriptListView' 
     ... 
    } 
    else 
    { 
     if (!startScript()) 
     { 
     // Do stuff 
     } 
    } 
    } 
    else 
    { 
    ts_resetScriptListIndexer(); 
    threadSafeEnableScriptListView(); 
    } 
} 

会发生什么事是,一个SE过程完成后,我得到InvalidOperationException型,说

StandardOut没有被重定向或 进程尚未启动的一个例外。

从呼叫到SE_Process.StandardOutput.BaseStream.EndRead()。我不明白为什么,因为我在每个新流程开始之前都设置了SE_Process.StartInfo.RedirectStandardOutput。在我看来,如果退出进程的标准输出流在处理完成后调用我的read_SE_StdOut()函数,那么这可能吗?

谢谢您的阅读!

+0

您可能会对[MedallionShell]库(https://github.com/madelson/MedallionShell)库感兴趣,它简化了处理io流,特别是异步操作 – ChaseMedallion 2014-08-29 23:41:35

回答

2

你得到的异常是正常的。一个BeginRead()调用永远不会成功:最后一个,在进程终止后。如果您知道该过程已完成,您通常会避免再次拨打BeginRead(),以免发生异常。但是,你很少知道。赶上例外。或者使用BeginOutputReadLine()代替它,它会为您抓到它。

我猜你也会重定向stderr并且该工具使用它来输出“X”。在stderr标准输出被缓冲并重定向后,无法保持同步输出。

+0

我明白了,我只是处理异常。 关于人物的扰乱顺序;还有X:es被标准输出,所以我不认为这解释了为什么它们出现乱序。我会让这个问题没有得到答复,因为我最大的问题是输出的显示不正确(我认为它可能与异常有关,但也许不是)。谢谢您的回答! – jokki 2010-01-19 09:46:28

+0

我收到的字符无序显示的问题不再发生,我必须修复它意外:) 我已经删除了上述问题的部分,并设置问题为“回答”。 再次感谢! – jokki 2010-01-21 11:43:05