9

I have a third party DOS process which writes data about its progress to the command line. I want to react on the progress. Normally I would use a Process with RedirectStandardOutput = true and RedirectStandardError = true and then

.OutputDataReceived +=xyzOutputDataReceived; 
.ErrorDataReceived += xyzErrorDataReceived;
.Start();
.BeginOutputReadLine();
.BeginErrorReadLine();

Normally this works. and I got what i need as DataReceivedEventArg.

In this case the process seems to update the same line it has written (how is that possible?), so it writes 15 %, 15% changes to 18% and so on. Only at the end of execution it seems that the data is flushed to StandardOutput.

Also if i just try to pipe data to a text file (eg odb.exe >> output.txt) it shows nothing.

Is there any way to get the temporary data?

The question is not about getting the Standard Output, this works fine (synchronously and asynchronously). It is about how to get output from a process which I cannot change, and which does not seem to flush it's output to the standard stream.

Offler
  • 1,223
  • 1
  • 12
  • 34
  • 1
    The "going back to overwrite something in the same line" can also be achieved in C# applications, by using the `Console.SetCursorPosition()` function (https://msdn.microsoft.com/de-de/library/system.console.setcursorposition%28v=vs.110%29.aspx). I'm not sure however why writing doesn't trigger the `OutputDataReceived` event in your case.. – Maximilian Gerhardt Sep 16 '15 at 13:03
  • http://stackoverflow.com/questions/186822/capturing-console-output-from-a-net-application-c , almost the same question – Thorarins Sep 16 '15 at 13:05
  • @Thorarins Not the same. This Q has an app that is apparently not printing newlines and not flushing stdout so `OutputDataReceived` is not getting fired. – 001 Sep 16 '15 at 13:07
  • @Thorarins Not the same - capturing of output works. I search for a way that another application starts to flush its output. The command line seems to get the output somehow in another way - so is it possible somehow to get the text? Only questions I found so far are ones which altered the other programm. – Offler Sep 16 '15 at 13:10
  • 1
    Well the documentation for [`PRocess.OutputDataReceived`](https://msdn.microsoft.com/en-us/library/system.diagnostics.process.outputdatareceived(v=vs.110).aspx) says _The OutputDataReceived event indicates that the associated Process has written a line, terminating with a newline character, to its redirected StandardOutput stream._ So, no newline, no event. Not sure how to work around it though. – juharr Sep 16 '15 at 13:17
  • Here's an example of doing this I wrote a while back: http://pietschsoft.com/post/2004/09/07/How-do-you-call-a-Console-application-from-ASPNET-and-get-the-results – Chris Pietschmann Sep 16 '15 at 13:19
  • I'm not sure why piping the data shows nothing. When a character would be printed in the console, then pipe should directly write it in the file, shouldn't it? Maybe you should also redirect `stderr` to the file, then put a `FileWatcher` on that file. `odb.exe > output.txt 2>&1` – Maximilian Gerhardt Sep 16 '15 at 13:20
  • What happens if you try to read from `Process.StandardOutput`? – juharr Sep 16 '15 at 13:21
  • @juharr the same - nothing is in before the process finishes. No newline - no events would be clear up to some point - somehow the command console gets the things to be displayed – Offler Sep 16 '15 at 13:25
  • @MaximilianGerhardt that was what i thought. error handling does not change anything. If you take juharrs comment then you see it seems to have to do with NewLine. So the qeustion is: After all command line can display anything, so is it possible with some rather strange thing (some w32 call?) to get this data? – Offler Sep 16 '15 at 13:30
  • I wonder if there's some type of screen scraping approach that you could use instead. – juharr Sep 16 '15 at 13:30
  • @ChrisPietschmann Unfortuanlly - no. In this case the ReadToEnd does not get any data for some minutes until programm finishes. – Offler Sep 16 '15 at 14:19

1 Answers1

0

Like juharr says, you need to use Win32 to screen scrape the console. Fortunately you don't need to write that code yourself. You can use the buffer-reader from this post: https://stackoverflow.com/a/12366307/5581231

The BufferReader reads from standardout. I suppose you are writing a wpf or winforms application so we'll also have to get a reference to the console window of the DOS application. For this, we will use the Win32 API call AttachConsole.

[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int pid);

I wrote a small example program that demonstrates the usage. It starts the exe and attaches to its console. It then scrapes the entire window once a second, and dumps the output to the debugger output window. You should be able to modify this to search the console content for any keywords etc. that you can use to track the progress of the program. Or you could dump it to a textfield or something in your UI, possibly after comparing it for changes?

var process = Process.Start(@"..path to your exe....");       
//Wait for the DOS exe to start, and create its console window
while (process.MainWindowHandle == IntPtr.Zero)
{
  Thread.Sleep(500);
}

//Attach to the console of our DOS exe
if (!AttachConsole(process.Id))
  throw new Exception("Couldn't attach to console");

while (true)
{
  var strings = ConsoleReader.ReadFromBuffer(0, 0,  
                    (short)Console.BufferWidth, 
                     short)Console.BufferHeight);
  foreach (var str  in strings.
                     Select(s => s?.Trim()).
                     Where(s => !String.IsNullOrEmpty(s)))
 {
    Debug.WriteLine(str);          
 }
 Thread.Sleep(1000);
}

Good Luck!

Community
  • 1
  • 1
Mikael Nitell
  • 1,069
  • 6
  • 16