How to wait for ADB output in C#?

Viewed 1663

I want to receive information from a device connected in USB via a C# program using ADB commands then flush the output.

Process process = new Process();
process.StartInfo.FileName = "adb.exe";
process.StartInfo.Arguments = "logcat -d -b main -v raw -s Command:I"; // get data in Log.I() with name == "Command"
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start();

string output;
do {
    process.StandardInput.WriteLine("logcat -d -b main -v raw -s Command:I");
    output = process.StandardOutput.ReadToEnd();
} while (output.Length < 1);

Console.WriteLine(output);

process.StandardInput.WriteLine("logcat -c"); // flush logcat

process.WaitForExit();

Doing that I understand that the program is constantly executing adb logcat -d -b main -v raw -s Command:I, isn't there a way to call a command one time and wait for the output?

I can do that in windows prompt using :

adb shell 'logcat -b main -v raw -s UNIQUE_TAG:I | (read -n 1 && kill -2 $((BASHPID-1)))'

but is this doable in C#? Also the output doesn't seem to be flushed

4 Answers

You can't read to the end more than once; ReadToEnd ultimately means "until the pipe is closed and I've consumed everything, and no more data will ever ever arrive". ReadToEnd will also never finish until adb.exe either intentionally signals that it is done writing (and will never write again), or terminates - so this is why you aren't getting as far as flushing.

What you can do is read (a byte/character at a time, if needed) until you have, say, a single line - so if the expectation here is "one command in, one line out" then it is simple enough. If there isn't a way of knowing how many lines will come back, then you would usually use two threads, and have a dedicated reader thread that takes data from the process.StandardOutput (byte/character at a time, or line by line) and does whatever is needed with it.

This is how i used to code for such things Here is my code Hope u understand it

  //to run the all the cmd,adb.fastboot.exe command using these function
   //here in startfunc pass the .exe file u want to execute,Arguments which u want to pass 
    //string msg = It is nothing Extra message if u want to add to your final output


    private static string Execute(string Startfunc, String Arguments,string msg)
    {   
        string text = null;
        Process p = new Process();
        p.StartInfo.FileName = Startfunc;
        p.StartInfo.Arguments = Arguments;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.RedirectStandardOutput = true;         
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.EnableRaisingEvents = true;
        p.Start(); 
        do
        {
            Application.DoEvents();
            string output = p.StandardOutput.ReadToEnd();
            text += output;
            string err = p.StandardError.ReadToEnd();
            text += err;
        }
        while (!p.HasExited);
        text += msg;
        return text;
    }

     //now i will call above function and make it wait until we get the result

     private void normal_reboot_Click(object sender, EventArgs e)
    { 
        
            txt_Log.Text = "rebooting" +Environment.NewLine;
            Task t = Task.Factory.StartNew(() =>
            {
                txt_Log.Text=Execute("adb.exe","reboot","Done");
            });
            do { Application.DoEvents(); } while (!t.IsCompleted);                                    
    }

You can also look for output/error messages asyncly. So you don't need a (blocking) loop: This example just outputs the messages, but can be extended by full methods

p.OutputDataReceived+= (s, e) => Console.WriteLine(e.Data);
p.BeginOutputReadLine();//This is important, otherwise the event won't be fired
p.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);
p.BeginErrorReadLine();

This should print every line received by adb

Working code below, we don't call adb.exe directly but through cmd.exe with a command waiting for any information log coming from logcat with the title "Command" : adb shell logcat -b main -v raw -s Command:I

private Process listeningProc;

public someClass(){
   listeningProc = new Process();
   listeningProc.StartInfo.FileName = "cmd.exe";
   listeningProc.StartInfo.UseShellExecute = false;
   listeningProc.StartInfo.RedirectStandardOutput = true;
   listeningProc.StartInfo.RedirectStandardInput = true;
   listeningProc.Start();

   listeningProc.OutputDataReceived += new DataReceivedEventHandler(
       (s, e) => {
           if (e.Data == "SPECIFIC_COMMAND") {
               // do something
           }
       }
   );
   listeningProc.BeginOutputReadLine();
}

private void ListenToCommands(){
   listeningProc.StandardInput.WriteLine("adb shell logcat -b main -v raw -s Command:I"); //Listening for Info Logs titled "Command"

   while (true) { }
}

To flush logcat or send any command from the PC through ADB, I open another process and send the commands from there.

Edit: You might want to add System.Threading.Thread.Sleep(2000); //pause for 2 seconds after each commands sent from the PC through ADB otherwise the device sometimes doesn't execute all commands when sent successively too quickly

Related