1

I want to get output of console application in realtime (same as run via cmd.exe) by my WinForm application. All actions i perform in non-UI thread (using BackgroundWorker's method bwRezerve_DoWork). AddTextToTextbox use Invoke to update UI.

But now i receive output only when application is exited. I read a lot of question here and on other sites, read similar question Capture output of process synchronously (i.e. "when it happens") but still can't find solution. Here code snippet:

private void bwRezerve_DoWork(object sender, DoWorkEventArgs e)
{
    proc = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = Application.StartupPath + Path.DirectorySeparatorChar + "7z.exe",
            Arguments = e.Argument,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            CreateNoWindow = true,
        }
    };
    proc.EnableRaisingEvents = true;
    proc.OutputDataReceived += (who, what) => AddTextToTextbox(what.Data);
    proc.ErrorDataReceived += (who, what) => AddTextToTextbox(what.Data);

    proc.Start();
    proc.BeginOutputReadLine();
    proc.BeginErrorReadLine();
    //same result with next line commented
    proc.WaitForExit(5 * 60 * 1000);
}

Also i've tried this instead of OutputDataReceived but result is the same

while (!proc.StandardOutput.EndOfStream)
{
    string line = proc.StandardOutput.ReadLine();
    AddTextToTextbox(line);
}
Community
  • 1
  • 1
Varro
  • 73
  • 1
  • 11

3 Answers3

1

Try this code

private void bwRezerve_DoWork(object sender, DoWorkEventArgs e)
{
    ProcessStartInfo psi = new ProcessStartInfo();
    psi.FileName = Application.StartupPath + Path.DirectorySeparatorChar + "7z.exe";
    psi.Arguments = e.Argument;
    psi.UseShellExecute = false;
    psi.RedirectStandardError = true;
    psi.RedirectStandardOutput = true;
    psi.CreateNoWindow = true;

    Process proc = Process.Start(psi);
    proc.WaitForExit();

    while (!proc.StandardOutput.EndOfStream)
    {
       string line = proc.StandardOutput.ReadLine();
       AddTextToTextbox(line);
    }
}
Mostafiz
  • 7,243
  • 3
  • 28
  • 42
  • In this case i didn't receive any input at all. – Varro Oct 12 '16 at 06:57
  • I've used both ReadLine() and ReadToEnd() - result is still the same. – Varro Oct 12 '16 at 07:11
  • new update is working code I have just checked at my machine – Mostafiz Oct 12 '16 at 07:43
  • Same result. It easy see from code - i don't receive any updates before process would be exited: `proc.WaitForExit();` is before stdOut reading. And same result if `proc.WaitForExit();` is after reading output. – Varro Oct 12 '16 at 08:26
1

I think problem there is problem with your thread your process is running under main thread so your output will display only when process is completed. So you need use background worker or thread you can also use dispatcher to get output from current process.

while (!proc.StandardOutput.EndOfStream)
{

  Application.Current.Dispatcher.Invoke(new Action(() =>
     {
    string line = proc.StandardOutput.ReadLine();
    AddTextToTextbox(line);
     }), null);

}

hope its work for you ..

EDIT

you can get current dispatcher using window base Lib.

Assembly: WindowsBase (in WindowsBase.dll) (Ref MSDN)

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
            {
                string line = proc.StandardOutput.ReadLine();
                AddTextToTextbox(line);
            }), null);
Anant Dabhi
  • 10,864
  • 3
  • 31
  • 49
  • I can't use "Application.Current.Dispatcher.Invoke" because it's WinForms project, not WPF. But all actions i perform in BackgroundWorker (bwRezerve_DoWork). And in "AddTextToTextbox" method I use Invoke to perform update in UI thread. – Varro Oct 12 '16 at 07:08
  • I don't need progress, I need to get output (there ). And console application itself doesn't send percentage. – Varro Oct 12 '16 at 07:22
  • Same result - output shows after console application is exited (yes, `WaitForExit()` is after thi code block). – Varro Oct 12 '16 at 08:33
0

7zip doesn't use standard output - you can easily see that since it continually rewrites the screen (to show the progress). There's no way to stream that.

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • But somehow 7zip sends data to cmd.exe, doesn't it? And i want to catch this data. – Varro Oct 12 '16 at 07:26
  • @Varro No, `cmd` has nothing to do with it - that's just a command processor. It sends data directly to console, and if you create a console in your application, it will show the output just like `cmd`. If you don't create the console, you don't get the output. Stdout is shown in the console by default, but that doesn't mean that everything shown in the console goes through stdout :) – Luaan Oct 12 '16 at 07:33
  • @Varro The equivalent in C# is for example when you do `Console.CursorTop = 0` - now you're writing to console buffer instead of stdout. Now, the infrastructure is smart enough that you'll get the output in stdout *eventually*, but it simply can't give you anything that would change - stdout has no "rewind" feature, it's just a stream. – Luaan Oct 12 '16 at 07:41
  • Are you sure? According to 7zip help (same as here https://sevenzip.osdn.jp/chm/cmdline/switches/bs.htm ) it redirects standard output messages to stdout stream by default. – Varro Oct 12 '16 at 07:58
  • @Varro You can see it for yourself. Stdout is append only - is the console output from 7z append only? Run 7z in cmd with `> x.x`. Does the file immediately contain everything you want, up to date, during the decompression (obviously, try this with a big file)? Nope, because the data isn't on stdout yet - only in the console (and console output is disabled when you redirect stdout). The `-bd` switch should have fixed this, but it doesn't seem to in practice. – Luaan Oct 12 '16 at 09:27