1

遗留程序“LegacyBuilder”运行“batch.cmd”文件,然后将其输出重定向到文件,如下所示。

ProcessStartInfo processStartInfo = new ProcessStartInfo("C:\\batch.cmd");
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.UseShellExecute = false;
using (Process process = Process.Start(processStartInfo))
{
    using (StreamWriter logWriter = new StreamWriter("C:\\log.txt"))
    {              
        while ((logLine = process.StandardOutput.ReadLine()) != null)
        {
            logWriter.WriteLine(logLine);
        }
    }
}

batch.cmd 包含这一行

C:\SomeApp.exe "arg1" "arg2" "arg3"

SomeApp.exe 正在使用来自不同程序集的以下方法。

SomeAssembly.SomeClass.GenerateOutput()

此 GenerateOutput 方法为第 3 方控制台程序创建一个进程。在遇到缓冲区死锁问题后,我发现(我认为来自另一个 SO 问题)以下代码永远不会导致这种死锁,并且“LegacyBuilder”捕获了第 3 方程序的输出。

Console.Write("I'm creating the process!");

ProcessStartInfo processStartInfo = new ProcessStartInfo("C:\\3rdPartyConsoleExe.exe");
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.UseShellExecute = false;

using (Process process = new Process())
{
    process.StartInfo = processStartInfo;
    using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
    using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
    {    
       process.OutputDataReceived += (sender, e) =>
       {
         if (e.Data == null) outputWaitHandle.Set();
         else output.AppendLine(e.Data);
       };
       process.ErrorDataReceived += (sender, e) =>
       {
         if (e.Data == null) errorWaitHandle.Set();
         else error.AppendLine(e.Data);
       };

    process.Start();

    process.BeginOutputReadLine();
    process.BeginErrorReadLine();

    if (process.WaitForExit(60000) && outputWaitHandle.WaitOne(60000) && errorWaitHandle.WaitOne(60000))
    {
        Console.WriteLine(output.ToString());
        Console.WriteLine(error.ToString());

        if (process.ExitCode > 0)
            Console.WriteLine("PROCESS COMPLETED ExitCode=" + process.ExitCode.ToString());
    }
        else
            Console.WriteLine("PROCESS TIMED OUT");
    }
}

请注意 Console.Write("我正在创建进程!");

到目前为止一切顺利,一切正常,所有输出都被“LegacyBuilder”捕获

现在,通过调用 powershell 脚本更新了 batch.cmd。

C:\Windows\SysWOW64\WindowsPowerShell\v1.0\PowerShell.exe -File "C:\powershellscript.ps1"

powershell 脚本基本上是加载 SomeAssembly,创建 SomeObject 然后调用 GenerateOutput 方法。像这样。

Add-Type -Path "C:\SomeAssembly.dll";
$someCls = New-Object SomeAssembly.SomeClass()
$someCls.GenerateOutput();

问题:文本“我正在创建流程!” 正在被“LegacyBuilder”捕获,所以这将是标准输出。

但是 GenerateOutput() 方法的预期输出没有产生任何东西,并且在几次尝试调用后它再次陷入僵局。甚至没有调用“过程超时”。

这很奇怪。

batch.cmd 调用“SomeApp.exe”,后者调用 SomeAssembly.SomeClass.GenerateOutput()。文件中存在第 3 方程序的输出,并且 batch.cmd 成功继续。

batch.cmd 调用 powershell 来加载创建 SomeAssembly.SomeClass 对象的脚本,然后调用 GenerateOutput() 方法。输出不存在,只有直接 Console.Write 调用的输出是......经过几次尝试,它会死锁并且 batch.cmd 永远不会继续。

有什么帮助吗?我希望存在像“StopBufferDeadlocksInPowershell -includeStupidLegacyNestedProcessCalls”这样的东西......

编辑 1

我很想避免在“LegacyBuilder”中进行任何更改,因为其中的代码非常非常复杂(最上面的代码示例只是简化了),虽然我很想完全重写它,但这会非常耗时。

4

1 回答 1

1

经过几天的头痛之后,不知何故,这使它起作用了。

$someCls.GenerateOutput() | Out-Host;

缓冲区不再死锁。

于 2013-09-11T11:57:55.987 回答