ProcessStartInfoが“ WaitForExit”でハングしていますか?どうして?
-
02-07-2019 - |
質問
次のコードがあります:
info = new System.Diagnostics.ProcessStartInfo("TheProgram.exe", String.Join(" ", args));
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForExit();
Console.WriteLine(p.StandardOutput.ReadToEnd()); //need the StandardOutput contents
開始しているプロセスからの出力が約7MBであることを知っています。 Windowsコンソールで実行すると正常に動作します。残念ながら、プログラムでこれはWaitForExitで無期限にハングします。また、これは小さな出力(3KBなど)に対してハングしないコードを実行します。
ProcessStartInfoの内部StandardOutputが7MBをバッファリングできない可能性はありますか?もしそうなら、私は代わりに何をすべきですか?そうでない場合、私は何を間違えていますか?
解決
問題は、 StandardOutput
や StandardError
をリダイレクトすると、内部バッファがいっぱいになる可能性があることです。どの順序を使用しても、問題が発生する可能性があります。
- プロセスが終了するのを待ってから
StandardOutput
を読み取ると、プロセスは書き込みをブロックするため、プロセスは終了しません。 - ReadToEndを使用して
StandardOutput
から読み取る場合、プロセスがStandardOutput
を閉じない場合(たとえば、終了しない場合)、あなたのプロセスはブロックできます。またはStandardError
)への書き込みがブロックされている場合)。
解決策は、非同期読み取りを使用して、バッファーがいっぱいにならないようにすることです。デッドロックを回避し、 StandardOutput
と StandardError
の両方からすべての出力を収集するには、次のようにします。
編集:タイムアウトが発生した場合に ObjectDisposedException を回避する方法については、以下の回答を参照してください。
using (Process process = new Process())
{
process.StartInfo.FileName = filename;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
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(timeout) &&
outputWaitHandle.WaitOne(timeout) &&
errorWaitHandle.WaitOne(timeout))
{
// Process completed. Check process.ExitCode here.
}
else
{
// Timed out.
}
}
}
他のヒント
のドキュメント Process.StandardOutput
は、待機する前に読み取るように指示します。そうしないと、デッドロック、スニペットがコピーされます:
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Mark Byersの回答は優れていますが、以下を追加するだけです。outputWaitHandleおよびerrorWaitHandleが破棄される前に、OutputDataReceivedおよびErrorDataReceivedデリゲートを削除する必要があります。タイムアウトを超えてからプロセスがデータを出力し続けて終了した場合、outputWaitHandleおよびerrorWaitHandle変数は破棄された後にアクセスされます。
(FYI投稿にコメントできなかったため、この警告を回答として追加する必要がありました。)
未処理のObjectDisposedExceptionの問題は、プロセスがタイムアウトすると発生します。そのような場合、条件の他の部分:
if (process.WaitForExit(timeout)
&& outputWaitHandle.WaitOne(timeout)
&& errorWaitHandle.WaitOne(timeout))
は実行されません。この問題は次の方法で解決しました。
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
using (Process process = new Process())
{
// preparing ProcessStartInfo
try
{
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
{
outputWaitHandle.Set();
}
else
{
outputBuilder.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null)
{
errorWaitHandle.Set();
}
else
{
errorBuilder.AppendLine(e.Data);
}
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (process.WaitForExit(timeout))
{
exitCode = process.ExitCode;
}
else
{
// timed out
}
output = outputBuilder.ToString();
}
finally
{
outputWaitHandle.WaitOne(timeout);
errorWaitHandle.WaitOne(timeout);
}
}
}
これは、.NET 4.5以降向けの、より近代的な待望のタスク並列ライブラリ(TPL)ベースのソリューションです。
使用例
try
{
var exitCode = await StartProcess(
"dotnet",
"--version",
@"C:\",
10000,
Console.Out,
Console.Out);
Console.WriteLine(public static async Task<int> StartProcess(
string filename,
string arguments,
string workingDirectory= null,
int? timeout = null,
TextWriter outputTextWriter = null,
TextWriter errorTextWriter = null)
{
using (var process = new Process()
{
StartInfo = new ProcessStartInfo()
{
CreateNoWindow = true,
Arguments = arguments,
FileName = filename,
RedirectStandardOutput = outputTextWriter != null,
RedirectStandardError = errorTextWriter != null,
UseShellExecute = false,
WorkingDirectory = workingDirectory
}
})
{
process.Start();
var cancellationTokenSource = timeout.HasValue ?
new CancellationTokenSource(timeout.Value) :
new CancellationTokenSource();
var tasks = new List<Task>(3) { process.WaitForExitAsync(cancellationTokenSource.Token) };
if (outputTextWriter != null)
{
tasks.Add(ReadAsync(
x =>
{
process.OutputDataReceived += x;
process.BeginOutputReadLine();
},
x => process.OutputDataReceived -= x,
outputTextWriter,
cancellationTokenSource.Token));
}
if (errorTextWriter != null)
{
tasks.Add(ReadAsync(
x =>
{
process.ErrorDataReceived += x;
process.BeginErrorReadLine();
},
x => process.ErrorDataReceived -= x,
errorTextWriter,
cancellationTokenSource.Token));
}
await Task.WhenAll(tasks);
return process.ExitCode;
}
}
/// <summary>
/// Waits asynchronously for the process to exit.
/// </summary>
/// <param name="process">The process to wait for cancellation.</param>
/// <param name="cancellationToken">A cancellation token. If invoked, the task will return
/// immediately as cancelled.</param>
/// <returns>A Task representing waiting for the process to end.</returns>
public static Task WaitForExitAsync(
this Process process,
CancellationToken cancellationToken = default(CancellationToken))
{
process.EnableRaisingEvents = true;
var taskCompletionSource = new TaskCompletionSource<object>();
EventHandler handler = null;
handler = (sender, args) =>
{
process.Exited -= handler;
taskCompletionSource.TrySetResult(null);
};
process.Exited += handler;
if (cancellationToken != default(CancellationToken))
{
cancellationToken.Register(
() =>
{
process.Exited -= handler;
taskCompletionSource.TrySetCanceled();
});
}
return taskCompletionSource.Task;
}
/// <summary>
/// Reads the data from the specified data recieved event and writes it to the
/// <paramref name="textWriter"/>.
/// </summary>
/// <param name="addHandler">Adds the event handler.</param>
/// <param name="removeHandler">Removes the event handler.</param>
/// <param name="textWriter">The text writer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public static Task ReadAsync(
this Action<DataReceivedEventHandler> addHandler,
Action<DataReceivedEventHandler> removeHandler,
TextWriter textWriter,
CancellationToken cancellationToken = default(CancellationToken))
{
var taskCompletionSource = new TaskCompletionSource<object>();
DataReceivedEventHandler handler = null;
handler = new DataReceivedEventHandler(
(sender, e) =>
{
if (e.Data == null)
{
removeHandler(handler);
taskCompletionSource.TrySetResult(null);
}
else
{
textWriter.WriteLine(e.Data);
}
});
addHandler(handler);
if (cancellationToken != default(CancellationToken))
{
cancellationToken.Register(
() =>
{
removeHandler(handler);
taskCompletionSource.TrySetCanceled();
});
}
return taskCompletionSource.Task;
}
quot;Process Exited with Exit Code {exitCode}!");
}
catch (TaskCanceledException)
{
Console.WriteLine("Process Timed Out!");
}
実装
<*>ロブはそれに答えて、私にもう何時間か試してみました。待機する前に出力/エラーバッファーを読み取ります。
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
この問題(またはバリアント)もあります。
次を試してください:
1)p.WaitForExit(nnnn);にタイムアウトを追加します。 nnnnはミリ秒単位です。
2)ReadForEnd呼び出しをWaitForExit呼び出しの前に配置します。これは、MSが推奨するものであると思われる です。
他のソリューション(EM0を含む)は、内部タイムアウトと、生成されたアプリケーションによるStandardOutputとStandardErrorの両方の使用のために、アプリケーションに対してまだデッドロックがかかっています。ここに私のために働いたものがあります:
Process p = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = exe,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
p.Start();
string cv_error = null;
Thread et = new Thread(() => { cv_error = p.StandardError.ReadToEnd(); });
et.Start();
string cv_out = null;
Thread ot = new Thread(() => { cv_out = p.StandardOutput.ReadToEnd(); });
ot.Start();
p.WaitForExit();
ot.Join();
et.Join();
編集:StartInfoの初期化をコードサンプルに追加
この方法で解決しました:
Process proc = new Process();
proc.StartInfo.FileName = batchFile;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.Start();
StreamWriter streamWriter = proc.StandardInput;
StreamReader outputReader = proc.StandardOutput;
StreamReader errorReader = proc.StandardError;
while (!outputReader.EndOfStream)
{
string text = outputReader.ReadLine();
streamWriter.WriteLine(text);
}
while (!errorReader.EndOfStream)
{
string text = errorReader.ReadLine();
streamWriter.WriteLine(text);
}
streamWriter.Close();
proc.WaitForExit();
入力、出力、エラーの両方をリダイレクトし、出力およびエラーストリームからの読み取りを処理しました。 このソリューションは、Windows 7とWindows 8の両方のSDK 7- 8.1で動作します
この投稿は古いかもしれませんが、通常ハングする主な原因は、redirectStandardoutputのスタックオーバーフローまたはredirectStandarderrorがある場合です。
出力データまたはエラーデータが大きいため、無期限に処理されるため、ハング時間が発生します。
この問題を解決するには:
p.StartInfo.RedirectStandardoutput = False
p.StartInfo.RedirectStandarderror = False
Mark Byers、Rob、stevejayの回答を考慮して、非同期ストリーム読み取りを使用して問題を解決するクラスを作成しようとしました。そうすることで、非同期プロセスの出力ストリームの読み取りに関連するバグがあることに気付きました。
Microsoftでそのバグを報告しました: https://connect.microsoft.com/ VisualStudio / feedback / details / 3119134
概要:
それはできません:
process.BeginOutputReadLine(); process.Start();
System.InvalidOperationExceptionを受け取ります:StandardOutは リダイレクトされていないか、プロセスがまだ開始されていません。
=============================================== ================================================= ===========================
次に、プロセスが完了した後に非同期出力の読み取りを開始する必要があります 開始:
process.Start(); process.BeginOutputReadLine();
そうすることで、出力ストリームが受信できるため、競合状態を作ります 非同期に設定する前のデータ:
process.Start();
// Here the operating system could give the cpu to another thread.
// For example, the newly created thread (Process) and it could start writing to the output
// immediately before next line would execute.
// That create a race condition.
process.BeginOutputReadLine();
=============================================== ================================================= ===========================
その後、ストリームを読むだけでいいと言う人もいます 非同期に設定する前に。しかし、同じ問題が発生します。そこ 同期読み取りと設定の間の競合状態になります 非同期モードにストリーミングします。
=============================================== ================================================= ===========================
出力ストリームの安全な非同期読み取りを実現する方法はありません 実際のプロセスのプロセス&quot;プロセス&quot; and&quot; ProcessStartInfo&quot;持っている 設計されています。
おそらく、あなたのケースで他のユーザーから提案されたような非同期読み取りを使用した方が良いでしょう。ただし、競合状態が原因で一部の情報を見逃す可能性があることに注意してください。
上記の答えのどれも仕事をしていません。
Robソリューションがハングし、「Mark Byers」ソリューションが破棄された例外を取得します(他の回答の「ソリューション」を試しました)。
だから私は別の解決策を提案することにしました:
public void GetProcessOutputWithTimeout(Process process, int timeoutSec, CancellationToken token, out string output, out int exitCode)
{
string outputLocal = ""; int localExitCode = -1;
var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
outputLocal = process.StandardOutput.ReadToEnd();
process.WaitForExit();
localExitCode = process.ExitCode;
}, token);
if (task.Wait(timeoutSec, token))
{
output = outputLocal;
exitCode = localExitCode;
}
else
{
exitCode = -1;
output = "";
}
}
using (var process = new Process())
{
process.StartInfo = ...;
process.Start();
string outputUnicode; int exitCode;
GetProcessOutputWithTimeout(process, PROCESS_TIMEOUT, out outputUnicode, out exitCode);
}
このコードはデバッグされ、完全に機能します。
はじめに
現在受け入れられている回答は機能せず(例外をスロー)、回避策は多すぎますが、完全なコードはありません。これはよくある質問なので、明らかに多くの人の時間を無駄にしています。
Mark Byersの回答とKarol Tylの回答を組み合わせて、Process.Startメソッドの使用方法に基づいて完全なコードを作成しました。
使用法
これを使用して、gitコマンドの進捗ダイアログを作成しました。これは私がそれを使用した方法です:
private bool Run(string fullCommand)
{
Error = "";
int timeout = 5000;
var result = ProcessNoBS.Start(
filename: @"C:\Program Files\Git\cmd\git.exe",
arguments: fullCommand,
timeoutInMs: timeout,
workingDir: @"C:\test");
if (result.hasTimedOut)
{
Error = String.Format("Timeout ({0} sec)", timeout/1000);
return false;
}
if (result.ExitCode != 0)
{
Error = (String.IsNullOrWhiteSpace(result.stderr))
? result.stdout : result.stderr;
return false;
}
return true;
}
理論上、stdoutとstderrを組み合わせることもできますが、私はそれをテストしていません。
コード
public struct ProcessResult
{
public string stdout;
public string stderr;
public bool hasTimedOut;
private int? exitCode;
public ProcessResult(bool hasTimedOut = true)
{
this.hasTimedOut = hasTimedOut;
stdout = null;
stderr = null;
exitCode = null;
}
public int ExitCode
{
get
{
if (hasTimedOut)
throw new InvalidOperationException(
"There was no exit code - process has timed out.");
return (int)exitCode;
}
set
{
exitCode = value;
}
}
}
public class ProcessNoBS
{
public static ProcessResult Start(string filename, string arguments,
string workingDir = null, int timeoutInMs = 5000,
bool combineStdoutAndStderr = false)
{
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
using (var process = new Process())
{
var info = new ProcessStartInfo();
info.CreateNoWindow = true;
info.FileName = filename;
info.Arguments = arguments;
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
if (workingDir != null)
info.WorkingDirectory = workingDir;
process.StartInfo = info;
StringBuilder stdout = new StringBuilder();
StringBuilder stderr = combineStdoutAndStderr
? stdout : new StringBuilder();
var result = new ProcessResult();
try
{
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
outputWaitHandle.Set();
else
stdout.AppendLine(e.Data);
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null)
errorWaitHandle.Set();
else
stderr.AppendLine(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (process.WaitForExit(timeoutInMs))
result.ExitCode = process.ExitCode;
// else process has timed out
// but that's already default ProcessResult
result.stdout = stdout.ToString();
if (combineStdoutAndStderr)
result.stderr = null;
else
result.stderr = stderr.ToString();
return result;
}
finally
{
outputWaitHandle.WaitOne(timeoutInMs);
errorWaitHandle.WaitOne(timeoutInMs);
}
}
}
}
}
これは夕食が古いことを知っていますが、このページ全体を読んだ後、私にとっては解決策はありませんでした正しいトラック。うまくいかなかったと言うと、それは完全に真実ではなく、時にはうまくいくかもしれません。EOFマークの前の出力の長さに関係していると思います。
とにかく、私のために働いた解決策は、異なるスレッドを使用してStandardOutputとStandardErrorを読み取り、メッセージを書き込むことでした。
StreamWriter sw = null;
var queue = new ConcurrentQueue<string>();
var flushTask = new System.Timers.Timer(50);
flushTask.Elapsed += (s, e) =>
{
while (!queue.IsEmpty)
{
string line = null;
if (queue.TryDequeue(out line))
sw.WriteLine(line);
}
sw.FlushAsync();
};
flushTask.Start();
using (var process = new Process())
{
try
{
process.StartInfo.FileName = @"...";
process.StartInfo.Arguments = <*>quot;...";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start();
var outputRead = Task.Run(() =>
{
while (!process.StandardOutput.EndOfStream)
{
queue.Enqueue(process.StandardOutput.ReadLine());
}
});
var errorRead = Task.Run(() =>
{
while (!process.StandardError.EndOfStream)
{
queue.Enqueue(process.StandardError.ReadLine());
}
});
var timeout = new TimeSpan(hours: 0, minutes: 10, seconds: 0);
if (Task.WaitAll(new[] { outputRead, errorRead }, timeout) &&
process.WaitForExit((int)timeout.TotalMilliseconds))
{
if (process.ExitCode != 0)
{
throw new Exception(<*>quot;Failed run... blah blah");
}
}
else
{
throw new Exception(<*>quot;process timed out after waiting {timeout}");
}
}
catch (Exception e)
{
throw new Exception(<*>quot;Failed to succesfully run the process.....", e);
}
}
}
これが難しいと思った人を助けてください!
ここですべての投稿を読んだ後、Marko Avlija&#353;の統合ソリューションに落ち着きました。 ただし、すべての問題を解決できませんでした。
この環境には、長年にわたって蓄積され、さまざまな人々がさまざまなスタイルで記述した数百の異なる.bat .cmd .exeなどのファイルを実行するようにスケジュールされたWindowsサービスがあります。プログラムの記述を管理することはできません。スクリプト、成功/失敗のスケジューリング、実行、レポートを担当します。
だから私はここで提案のほとんどすべてを試し、さまざまなレベルの成功を収めました。 Markoの答えはほぼ完璧でしたが、サービスとして実行する場合、常に標準出力をキャプチャするとは限りませんでした。なぜそうなのか、私は決して理解できませんでした。
すべてのケースで機能する唯一の解決策は次のとおりです。 http://csharptest.net/319/using-the-processrunner-class/index.html
回避策すべての複雑さを避けるために使用することになりました:
var outputFile = Path.GetTempFileName();
info = new System.Diagnostics.ProcessStartInfo("TheProgram.exe", String.Join(" ", args) + " > " + outputFile + " 2>&1");
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForExit();
Console.WriteLine(File.ReadAllText(outputFile)); //need the StandardOutput contents
したがって、一時ファイルを作成し、&gt;を使用して出力とエラーの両方をリダイレクトします。出力ファイル&gt; 2&gt;&amp; 1
を選択し、プロセスの終了後にファイルを読み取ります。
他のソリューションは、出力を使用して他の処理を行いたいシナリオには適していますが、単純な処理では、これにより多くの複雑さが回避されます。
これはシンプルでより良いアプローチであること( AutoResetEvent
は不要です):
public static string GGSCIShell(string Path, string Command)
{
using (Process process = new Process())
{
process.StartInfo.WorkingDirectory = Path;
process.StartInfo.FileName = Path + @"\ggsci.exe";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.UseShellExecute = false;
StringBuilder output = new StringBuilder();
process.OutputDataReceived += (sender, e) =>
{
if (e.Data != null)
{
output.AppendLine(e.Data);
}
};
process.Start();
process.StandardInput.WriteLine(Command);
process.BeginOutputReadLine();
int timeoutParts = 10;
int timeoutPart = (int)TIMEOUT / timeoutParts;
do
{
Thread.Sleep(500);//sometimes halv scond is enough to empty output buff (therefore "exit" will be accepted without "timeoutPart" waiting)
process.StandardInput.WriteLine("exit");
timeoutParts--;
}
while (!process.WaitForExit(timeoutPart) && timeoutParts > 0);
if (timeoutParts <= 0)
{
output.AppendLine("------ GGSCIShell TIMEOUT: " + TIMEOUT + "ms ------");
}
string result = output.ToString();
return result;
}
}
非同期を使用すると、標準出力と標準エラーの両方を使用する場合でも、より洗練されたソリューションを使用でき、デッドロックが発生しない可能性があります:
using (Process process = new Process())
{
process.StartInfo.FileName = filename;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start();
var tStandardOutput = process.StandardOutput.ReadToEndAsync();
var tStandardError = process.StandardError.ReadToEndAsync();
if (process.WaitForExit(timeout))
{
string output = await tStandardOutput;
string errors = await tStandardError;
// Process completed. Check process.ExitCode here.
}
else
{
// Timed out.
}
}
Mark Byersの回答に基づいています。
非同期メソッドを使用していない場合は、 await
string output = tStandardOutput.result;
を使用できます ここに投稿されたサンプルコードをリダイレクタと呼び、他のプログラムをリダイレクトします。もしそれが私なら、おそらく問題を再現するために使用できるテストリダイレクトプログラムを書くでしょう。
だから私はやった。テストデータには、ECMA-334 C#言語仕様v PDFを使用しました。約5MBです。以下がその重要な部分です。
StreamReader stream = null;
try { stream = new StreamReader(Path); }
catch (Exception ex)
{
Console.Error.WriteLine("Input open error: " + ex.Message);
return;
}
Console.SetIn(stream);
int datasize = 0;
try
{
string record = Console.ReadLine();
while (record != null)
{
datasize += record.Length + 2;
record = Console.ReadLine();
Console.WriteLine(record);
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ProcessStartInfo info = new ProcessStartInfo(TheProgram);
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
Process p = Process.Start(info);
string record = p.StandardOutput.ReadLine();
while (record != null)
{
Console.WriteLine(record);
record = p.StandardOutput.ReadLine();
}
p.WaitForExit();
quot;Error: {ex.Message}");
return;
}
datasizeの値は実際のファイルサイズと一致しませんが、それは問題ではありません。 PDFファイルが行末で常にCRとLFの両方を使用するかどうかは明確ではありませんが、これは重要ではありません。他の大きなテキストファイルを使用してテストできます。
これを使用すると、大量のデータを書き込むときにサンプルリダイレクタコードがハングしますが、少量のデータを書き込むときはハングしません。
そのコードの実行を何らかの方法でトレースしようとしましたが、できませんでした。リダイレクトされたプログラムのコンソールの作成を無効にしてリダイレクトされたプログラムの行をコメントアウトし、別のコンソールウィンドウを取得しようとしましたが、できませんでした。
次に、新しいウィンドウ、親のウィンドウ、またはウィンドウなしでコンソールアプリを起動する方法。そのため、1つのコンソールプログラムがShellExecuteを使用せずに別のコンソールプログラムを起動する場合、(簡単に)別のコンソールを持つことはできないようです。
リダイレクトされたプログラムがバッファをどこかでいっぱいにした場合、データが読み込まれるのを待たなければならず、その時点でリダイレクタによってデータが読み込まれなかった場合、それはデッドロックだと思います。
ソリューションは、ReadToEndを使用せず、データの書き込み中にデータを読み取ることですが、非同期読み取りを使用する必要はありません。解決策は非常に簡単です。以下は、5 MBのPDFで機能します。
<*>別の可能性は、GUIプログラムを使用してリダイレクトを行うことです。上記のコードは、明らかな変更を除いてWPFアプリケーションで機能します。
同じ問題を抱えていましたが、理由は異なりました。ただし、Windows 8では発生しますが、Windows 7では発生しません。次の行が問題を引き起こしたようです。
pProcess.StartInfo.UseShellExecute = False
解決策は、UseShellExecuteを無効にしないことでした。シェルのポップアップウィンドウが表示されましたが、これは望ましくありませんが、特に何も起こらないのを待っているプログラムよりもはるかに優れています。そこで、次の回避策を追加しました。
pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
今私を悩ませている唯一のことは、そもそもなぜこれがWindows 8で起こっているのかということです。