2

I have a BackgroundWorker thread that runs a cmd process and writes multiple commands to it. Some of the commands may take a while to complete so I want to show the user the cmd output of the progress. My code for running the cmd commands looks like this:

private void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    var startInfo = new ProcessStartInfo("cmd.exe")
    {
        UseShellExecute = false,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    };

    cmd = new Process { StartInfo = startInfo };

    cmd.OutputDataReceived += Cmd_OutputDataReceived;
    cmd.Start();
    cmd.BeginOutputReadLine();

    cmd.StandardInput.WriteLine($"cd {baseFolder}\\External Resources\\img files\\uboot");

    string[] commands = CmdCommands.GetUbootFlashCommands();
    foreach (var command in commands)
        cmd.StandardInput.WriteLine(command);

    cmd.StandardInput.WriteLine($"cd {baseFolder}\\External Resources\\img files\\android");
    commands = CmdCommands.GetKernelFlashCommands();
    foreach (var command in commands)
        cmd.StandardInput.WriteLine(command);

    cmd.StandardInput.WriteLine("exit");
    cmd.WaitForExit();
}

private void Cmd_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    Invoke(new Action(() =>
    {
        txtCmd.Text += e.Data + Environment.NewLine;
    }));
}

But the cmd output loos nothing the real output as shown in a regular cmd window. Here is how my output looks like:

Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\>cd C:\img files\uboot

C:\img files\uboot>fastboot flash XXX.bin

C:\img files\uboot>fastboot flash XXX.bin

C:\img files\uboot>cd C:\img files\android

C:\img files\android>fastboot flash kernel

C:\img files\android>fastboot flash system XXX.img

C:\img files\android>fastboot flash userdata XXX.img

C:\img files\android>fastboot flash cache XXX.img

C:\img files\android>exit

And here is how output looks like when I open a cmd window and type in the commands:

Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\WINDOWS\system32>cd C:\img files\uboot

C:\img files\uboot>fastboot flash bl2 bl2.bin
target didn't report max-download-size
sending 'bl2' (14 KB)...
OKAY [  0.006s]
writing 'bl2'...
OKAY [  0.042s]
finished. total time: 0.052s

C:\img files\uboot>fastboot flash bootloader u-boot.bin
target didn't report max-download-size
sending 'bootloader' (275 KB)...
OKAY [  0.049s]
writing 'bootloader'...
OKAY [  0.046s]
finished. total time: 0.098s

C:\img files\uboot>cd C:\img files\android

C:\img files\android>fastboot flash kernel zImage-dtb
target didn't report max-download-size
sending 'kernel' (5099 KB)...
OKAY [  0.839s]
writing 'kernel'...
OKAY [  0.145s]
finished. total time: 0.988s

C:\img files\android>fastboot flash system system.img
target didn't report max-download-size
sending 'system' (426874 KB)...
OKAY [ 70.327s]
writing 'system'...
OKAY [ 30.963s]
finished. total time: 101.295s

C:\img files\android>fastboot flash userdata userdata.img
target didn't report max-download-size
sending 'userdata' (35680 KB)...
OKAY [  5.895s]
writing 'userdata'...
OKAY [  2.301s]
finished. total time: 8.200s

C:\img files\android>fastboot flash cache cache.img
target didn't report max-download-size
sending 'cache' (6248 KB)...
OKAY [  1.036s]
writing 'cache'...
OKAY [  0.380s]
finished. total time: 1.422s

C:\img files\android>

How do I get this output in my my C# code???

Liran Friedman
  • 4,027
  • 13
  • 53
  • 96
  • I think you might want to pipe the stdout of the launched programs into your console – Marv Mar 30 '17 at 08:20
  • Some of it may be going to standard error - the rest - does it update as it makes progress? It may be that the program detects whether its connected to a terminal and only shows that output then. – Damien_The_Unbeliever Mar 30 '17 at 08:21
  • 1
    @Marv: What do you mean by that? Can you pls post a code sample? – Liran Friedman Mar 30 '17 at 08:22
  • @Damien_The_Unbeliever: Why would the output go the `StandartError`? And why is the difference between running cmd from C# or manually? – Liran Friedman Mar 30 '17 at 08:24
  • 3
    You only see the lines you see because that is all that you do - you write them into the console. To receive the output of the launched processes you have to connect yourself to them and print what they return. This is usually done by the console, but as you are in charge of the console in this case, it is not happening – Marv Mar 30 '17 at 08:25
  • I don't know a specific reason. It's a *choice* that any program may choose to make to send some of its output to standard error rather than standard out. It was just one partial possibility. The reason for this separation is so e.g. you can redirect the output of a command but it will still report diagnostic information to the console. – Damien_The_Unbeliever Mar 30 '17 at 08:27
  • @Marv: Are you saying it is not possible? – Liran Friedman Mar 30 '17 at 08:27
  • No. I am saying your approach might be wrong. Start the programs as a `Process` like the CMD and write their stdout and stderr to your console for example – Marv Mar 30 '17 at 08:28
  • @Damien_The_Unbeliever: Do you think that calling `ErrorDataReceived` will give me the missing output ? – Liran Friedman Mar 30 '17 at 08:29
  • @Marv: What programs do you mean I should start as a process? – Liran Friedman Mar 30 '17 at 08:29
  • Not a complete one. Consider running the programs as suggested and follow the instructions of this question: http://stackoverflow.com/a/4291965/5396946 . The output you receive will be what you want to show to your user. – Marv Mar 30 '17 at 08:32
  • @Marv: Since I'm working with `BackgourndWorker` here this solution gives an error of mixing synchronous and asynchronous threads – Liran Friedman Mar 30 '17 at 08:35
  • @Damien_The_Unbeliever: I've tried calling `cmd.ErrorDataReceived += Cmd_ErrorDataReceived;` as well, but no output there as well... – Liran Friedman Mar 30 '17 at 08:45
  • @Damien_The_Unbeliever: COOL now it works. I forgot to also call `cmd.BeginErrorReadLine();` Thanks. Can I post the solution code and tag you as the answer ? Or would you like to post the solution code ans I will mark it as the answer? – Liran Friedman Mar 30 '17 at 09:05

3 Answers3

0

Can you try this simple solution. I don't know how much it can be successful in your case.

        Process p = new Process();
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.Arguments = "/c ping 192.168.x.x"; //or your thing
        p.Start();


        string result = p.StandardOutput.ReadToEnd();
        p.WaitForExit();
Vandita
  • 708
  • 4
  • 13
  • I tried it, but calling `StandardOutput.ReadToEnd()` gives the same output and it waits until all process completes, and I want the output to be shown during the process. – Liran Friedman Mar 30 '17 at 08:26
  • @LiranFriedman By placing `p.WaitForExit()` below reading output it should work ! I have edited my answer – Vandita Mar 30 '17 at 08:31
  • It won't matter because the code stops at this like `string result = p.StandardOutput.ReadToEnd();`. Need to add and `exit` command to the arguments, but still it gives the same output so no need for that as well... – Liran Friedman Mar 30 '17 at 08:37
0

if the missing information is defined as an error in fastboot from some reason, RedirectStandardError should solve it

 var startInfo = new ProcessStartInfo("cmd.exe")
    {
        UseShellExecute = false,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        CreateNoWindow = true,
        RedirectStandardError = true,
    }; 

after that you would need to use the event of ErrorDataReceived to get the error messages.

cmd.ErrorDataReceived+=Cmd_OutputDataReceived
cmd.BeginErrorReadLine();
Yuval Perelman
  • 4,499
  • 1
  • 22
  • 32
  • I've just tried now you solution to add `RedirectStandardError = true` and calling `cmd.ErrorDataReceived += Cmd_ErrorDataReceived;` as well, but no output there as well... – Liran Friedman Mar 30 '17 at 08:45
  • Try to use cmd.StandardError.ReadToEnd() to see if what you are missing is really the error. If it isnt I have no idea what it could be. Good luck – Yuval Perelman Mar 30 '17 at 08:52
  • oh wait "cmd.ErrorDataReceived += Cmd_ErrorDataReceived;" != "cmd.ErrorDataReceived+=Cmd_OutputDataReceived" – Yuval Perelman Mar 30 '17 at 08:52
  • I know that: "cmd.ErrorDataReceived += Cmd_ErrorDataReceived;" != "cmd.ErrorDataReceived+=Cmd_OutputDataReceived". In my comment you can see that I created a new method as should :-) – Liran Friedman Mar 30 '17 at 08:54
  • Do you think `cmd.StandardError.ReadToEnd()` is any different from `cmd.ErrorDataReceived` ? – Liran Friedman Mar 30 '17 at 08:55
  • of course they are different, one is the event handler and one is a function that read the stream that is associated with the event. The data though should be the same, that is just a way to test if the problem is with your code or its just not an error. Moreover, since I didnt see the new method you've written, I dont know if its correct. And I dont see why another method is needed here, since you need both methods to do the exact same thing – Yuval Perelman Mar 30 '17 at 08:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/139472/discussion-between-liran-friedman-and-user2033402). – Liran Friedman Mar 30 '17 at 08:59
  • It works, just forgot to mention calling `cmd.BeginErrorReadLine();` Can you please edit your answer to show the complete code so that I'll mark it as well ? – Liran Friedman Mar 30 '17 at 09:08
0

As @Damien_The_Unbeliever and @user2033402 has suggested, it seems that for some reason the output I needed was redirected to the StandardError instead of to the StandardOutput.

So the working solution in my case was this code:

private void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    var startInfo = new ProcessStartInfo("cmd.exe")
    {
        UseShellExecute = false,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        CreateNoWindow = true
    };

    cmd = new Process { StartInfo = startInfo };

    cmd.OutputDataReceived += Cmd_OutputDataReceived;
    cmd.ErrorDataReceived += Cmd_ErrorDataReceived;
    cmd.Start();
    cmd.BeginOutputReadLine();
    cmd.BeginErrorReadLine();

    cmd.StandardInput.WriteLine($"cd {baseFolder}\\External Resources\\img files\\uboot");

    string[] commands = CmdCommands.GetUbootFlashCommands();
    foreach (var command in commands)
        cmd.StandardInput.WriteLine(command);

    cmd.StandardInput.WriteLine($"cd {baseFolder}\\External Resources\\img files\\android");
    commands = CmdCommands.GetKernelFlashCommands();
    foreach (var command in commands)
        cmd.StandardInput.WriteLine(command);

    cmd.StandardInput.WriteLine("exit");
    cmd.WaitForExit();
}

private void Cmd_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    Invoke(new Action(() =>
    {
        txtCmd.Text += e.Data + Environment.NewLine;
    }));
}

private void Cmd_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
    Invoke(new Action(() =>
    {
        txtCmd.Text += e.Data + Environment.NewLine;
    }));
}
Liran Friedman
  • 4,027
  • 13
  • 53
  • 96