In my earlier days of designing and coding .NET projects, I would strive to build pure C# implementations, no matter the scenario. For example, if my .NET app needed to capture and log standard output from an external process, I would have probably done something like this:
ProcessStartInfo psi = new ProcessStartInfo("SomeProg.exe", "/arg val"); psi.UseShellExecute = false; psi.RedirectStandardOutput = true; List<string> lines = new List<string>(); using (Process process = Process.Start(psi)) { process.OutputDataReceived += (o, e) => lines.Add(e.Data); process.BeginOutputReadLine(); process.WaitForExit(); Logger.Log("Process exited with code {0}.", process.ExitCode); } foreach (string line in lines) { Logger.Log("OUTPUT: {0}", line); }
Simple, right?! And that’s not even taking into account all the pitfalls and deadlock potential of this approach.
In my advanced age, I have relaxed my standards somewhat. I now realize that there is room for many tools and techniques even within a .NET program. Just because C# is the language of my main module does not mean I should be locked into using C# for every task in the app.
Using output redirection as the example, there is a more straightforward approach that works quite well in practice. The approach is — brace yourself — using a batch file!
string outputFile = "out.txt"; File.WriteAllText("run.cmd", "SomeProg.exe /arg val 1>%1"); using (Process process = Process.Start("cmd.exe", "/c run.cmd " + outputFile)) { process.WaitForExit(); Logger.Log("Process exited with code {0}.", process.ExitCode); } foreach (string line in File.ReadAllLines(outputFile)) { Logger.Log("OUTPUT: {0}", line); }
Since we’re already shelling out to an external process, we don’t really lose anything by using the shell to do more of the heavy lifting in this case. There are definitely some improvements you could make (e.g. async support, generalizing the batch file creation, etc.), but you get the basic idea. This approach really shines when there are many “shell-oriented” tasks to be performed in addition to launching a process or capturing output. Think of how much easier it is to do a recursive delete in batch or PowerShell compared to the more verbose (and exception-happy) .NET patterns.
If you find yourself in a similar situation, ask if you are using the right tool for the job. Uniformity is not a goal unto itself, and there can be real gains from polyglot programming.
Very succinct example for expressing the idea!