EDIT: Found some duplicates with no answers:
- Issue with output redirection in batch
- How do I use the "start" command without inheriting handles in the child process?
I have some C# code that tries to be a generic process launcher, within a larger long-running program. This code needs to capture the output from the processes it launches, and also wait for them to finish. We typically launch batch files with this code, and everything works fine, except when we want to start another child process from inside the batch file, such that it will outlive the batch file process. As an example, let's say I simply want to execute "start notepad.exe" from inside the batch file.
I encountered the same problems as in this question: Process.WaitForExit doesn't return even though Process.HasExited is true
Basically, even though the batch file process appears to be exiting very quickly (as expected), my program freezes until the child process (eg. notepad) also exits. However, notepad is not meant to exit, in my scenario.
I tried injecting "cmd.exe /C" at all points in the chain, with no luck. I tried explicitly terminating the batch file with "exit" or "exit /B". I tried reading the output both synchronously and asynchronously - with or without worker threads. I tried the patterns here: https://github.com/alabax/CsharpRedirectStandardOutput/tree/master/RedirectStandardOutputLibrary (see FixedSimplePattern.cs and AdvancedPattern.cs), again with no luck.
EDIT: I also tried with some C# P/Invoke code that does the process launching via the Windows API (CreatePipe/CreateProcess, etc), so I don't think this problem is specific to the C# Process API.
The only workaround I found was to replace the start command with a tool that calls CreateProcess with the DETACHED_PROCESS flag (CREATE_NO_WINDOW also works).
The accepted answer in the aforementioned SO question (https://stackoverflow.com/a/26722542/5932003) is the closest thing in the entire Internet that would appear to work, but it turns out it's leaking threads with every batch file you launch. I would have left a comment there, but I don't have the reputation to do that yet :).
Modified code that demonstrates thread leakage:
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace TestSO26713374WaitForExit
{
class Program
{
static void Main(string[] args)
{
while(true)
{
string foobat =
@"@echo off
START ping -t localhost
REM START ping -t google.com
REM ECHO Batch file is done!
EXIT /B 123
";
File.WriteAllText("foo.bat", foobat);
Process p = new Process
{
StartInfo =
new ProcessStartInfo("foo.bat")
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
p.Start();
var _ = ConsumeReader(p.StandardOutput);
_ = ConsumeReader(p.StandardError);
//Console.WriteLine("Calling WaitForExit()...");
p.WaitForExit();
//Console.WriteLine("Process has exited. Exit code: {0}", p.ExitCode);
//Console.WriteLine("WaitForExit returned.");
ThreadPool.GetMaxThreads(out int max, out int max2);
ThreadPool.GetAvailableThreads(out int available, out int available2);
Console.WriteLine(
$"Active thread count: {max - available} (thread pool), {System.Diagnostics.Process.GetCurrentProcess().Threads.Count} (all).");
Thread.Sleep(8000);
}
}
async static Task ConsumeReader(TextReader reader)
{
string text;
while ((text = await reader.ReadLineAsync()) != null)
{
Console.WriteLine(text);
}
}
}
}
The output from the above:
Active thread count: 2 (thread pool), 15 (all).
Active thread count: 4 (thread pool), 18 (all).
Active thread count: 6 (thread pool), 19 (all).
Active thread count: 8 (thread pool), 20 (all).
Active thread count: 9 (thread pool), 21 (all).
Active thread count: 11 (thread pool), 23 (all).
Active thread count: 13 (thread pool), 25 (all).
Active thread count: 15 (thread pool), 27 (all).
Active thread count: 17 (thread pool), 29 (all).
Active thread count: 19 (thread pool), 31 (all).
Active thread count: 21 (thread pool), 33 (all).
...
My questions:
- Why doesn't the start command completely break the chain of output redirection?
- Am I stuck with the aforementioned tool that calls CreateProcess(...DETACHED_PROCESS...)?
Thanks!
See Question&Answers more detail:
os