来自C#的命令提示被卡住了

mit*_*med 2 c# cmd

前几天我问过这个问题,但我既没有答案,也没有让它成功.因此,我试图将其缩小,因为问题中有很多噪音.

事实上,如果我在一个web api中公开一个运行cmd.exe的方法,那么如果我不按每次请求调用它两次就可以正常工作.

我的意思是,这段代码运行正常:

public class FilesController : ApiController
{
    private readonly IRunner _runner;

    public FilesController(IRunner runner)
    {
        _runner = runner;
    }

    public string Get()
    {
        return _runner.GetFiles();
    }
}

public class Runner : IRunner
{
    public Runner()
    {
        //var cd = @"cd C:\DummyFolder";
        //RunCmdPromptCommand(cd);
    }

    public string GetFiles()
    {
        var dir = @"cd C:\DummyFolder & dir";
        //var dir = "dir";
        return RunCmdPromptCommand(dir);
    }

    private string RunCmdPromptCommand(string command)
    {
        var process = new Process
        {
            StartInfo =
            {
                UseShellExecute = false,
                CreateNoWindow = true,
                WindowStyle = ProcessWindowStyle.Hidden,
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                FileName = @"cmd.exe",
                Arguments = string.Format("/C {0}", command)
            }
        };

        process.Start();
        var error = process.StandardError.ReadToEnd();

        if (!string.IsNullOrEmpty(error))
        {
            throw new Exception(error);
        }

        var output = process.StandardOutput.ReadToEnd();

        process.WaitForExit();

        return output;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我取消注释评论的行(并且明显注释掉第一行GetFiles,当代码第二次到达时(即使用"dir"),RunCmdPromptCommand它会卡在试图读取标准错误的行中.

我不知道为什么,而且我不知道如何在可能发生时强制退出(可能是其他可能发生的情况)

谢谢,

Ian*_*Ian 5

这是因为:

process.StandardOutput.ReadToEnd();
Run Code Online (Sandbox Code Playgroud)

是同步操作.

摘自MSDN:

可以同步或异步读取重定向的StandardError流.Read,ReadLine和ReadToEnd等方法对进程的错误输出流执行 同步读取操作.在关联的Process 写入其StandardError流或关闭流之前,这些同步读取操作不会完成.

换句话说,只要进程没有写任何标准错误或关闭流,它就会永远陷入困境.

要解决此问题,我建议使用Async BeginErrorReadLine.摘自MSDN:

相反,BeginErrorReadLine在StandardError流上启动异步读取操作.此方法为流输出启用指定的事件处理程序,并立即返回到调用程序,调用程序可以在将流输出定向到事件处理程序时执行其他工作.

我认为这将适合您的需要.

要使用它.MSDN中给出的示例非常简单.特别检查这些行:

 netProcess.ErrorDataReceived += new DataReceivedEventHandler(NetErrorDataHandler); //note this event handler add

if (errorRedirect) //in your case, it is not needed
{
    // Start the asynchronous read of the standard
    // error stream.
    netProcess.BeginErrorReadLine(); //note this
}
Run Code Online (Sandbox Code Playgroud)

以及如何定义事件处理程序:

private static void NetErrorDataHandler(object sendingProcess, 
    DataReceivedEventArgs errLine)
{
    // Write the error text to the file if there is something
    // to write and an error file has been specified.

    if (!String.IsNullOrEmpty(errLine.Data))
    {
        if (!errorsWritten)
        {
            if (streamError == null)
            {
                // Open the file.
                try 
                {
                    streamError = new StreamWriter(netErrorFile, true);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Could not open error file!");
                    Console.WriteLine(e.Message.ToString());
                }
            }

            if (streamError != null)
            {
                // Write a header to the file if this is the first
                // call to the error output handler.
                streamError.WriteLine();
                streamError.WriteLine(DateTime.Now.ToString());
                streamError.WriteLine("Net View error output:");
            }
            errorsWritten = true;
        }

        if (streamError != null)
        {
            // Write redirected errors to the file.
            streamError.WriteLine(errLine.Data);
            streamError.Flush();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)