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
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
I tried lot's of samples posted here and elsewhere that should capture and output the StandardOutput of a process.start command. But no matter if synchronous or asynchronous the output is always empty.
StringBuilder outputBuilder;
ProcessStartInfo processStartInfo;
Process process;
outputBuilder = new StringBuilder();
processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.Arguments = "-ss 00:00:50 -t 240 -i Input.MOV -to 00:00:02 -qscale 0 _OUTPUT.MOV";
processStartInfo.FileName = "ffmpeg.exe";
process = new Process();
process.StartInfo = processStartInfo;
// enable raising events because Process does not raise events by default
process.EnableRaisingEvents = true;
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
// append the new data to the data already read-in
outputBuilder.Append(e.Data);
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelOutputRead();
// use the output
string output = outputBuilder.ToString();
Process is executed just as intended but there is not output captured nevertheless it's shown in the VS IDE output pane.
Any idea whats going wrong here?
Best Regards.
Thanks Amy, it's correct, the normal output is returned by
BeginErrorReadLine
I`m working in the CLI commands in my Visual Studio C# application. This application does the interface with the Flash Programmer 2 (Texas Instruments Software to bootload the firmware in CC2560) using CLI ( Command-Line interface).
I checked some examples in StackOverflow, but I didn't have success to implement the solutions.
Steps of my application:
Select the serial port
Open the console
execute the srfprog.exe
execute the command line (srfprog -t soc(COM84,CC2650) -e -p -v -f c:\test.bin )
Check if the programming was a success
My file is located in: D:\Projects\Test_Fixture\Test_Fixture_Visual_Studio\SmartRF Tools\Flash Programmer 2\bin\srfprog.exe
When I execute this in my CMD (windows) the Prompt window shows
After this screen pop up I sent the commands to program the chip
srfprog -t soc(COM84,CC2650) -e -p -v -f c:\test.bin
This command will program and verify the code in the microcontroller CC2650 as show in the screenshot below:
The programming is perfect.
However when I run(Click the button - Load Firmware) my application open and close Shell window immediately. As shown in the figure below
My code is below:
private void button2_Click(object sender, EventArgs e)
{
System.Diagnostics.Process CC2650 = new System.Diagnostics.Process();
CC2650.StartInfo.FileName = #"D:\Projects\Test_Fixture\Test_Fixture_Visual_Studio\SmartRF Tools\Flash Programmer 2\bin\srfprog.exe";
CC2650.StartInfo.Arguments = "srfprog -t soc(COM84,CC2650) -e -p -v -f c:\test.bin"; //argument
CC2650.StartInfo.UseShellExecute = false;
CC2650.StartInfo.RedirectStandardOutput = true;
CC2650.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
CC2650.StartInfo.CreateNoWindow = false; //not diplay a windows
CC2650.Start();
string output = CC2650.StandardOutput.ReadToEnd(); //The output result
CC2650.WaitForExit();
}
In this test I`m using the fixed COM port. Later I will select the port.
Why my software is falling and doesn`t load the firmware?
Thank you for your attention and time.
Your code seems to be doubling up on srfprog by including it in the arguments. Filename defines the executable and Arguments should just be the stuff that comes after the Filename.
CC2650.StartInfo.Arguments = "-t soc(COM84,CC2650) -e -p -v -f c:\test.bin";
As mentioned before you don't need to pass the program name again in the arguments. To inspect what is happening please use code below. It will read the output and the errors in an async approach so we can analyse the output while the srfprog.exe is running.
I don't have any C# compiler here so I could not test this code before writing here, maybe you will have to change something to make it work. Please try it and let my know what will be printed in your Visual Studio output console.
// Add "using System.Diagnostics;"
private void button2_Click(object sender, EventArgs e)
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = #"D:\Projects\Test_Fixture\Test_Fixture_Visual_Studio\SmartRF Tools\Flash Programmer 2\bin\srfprog.exe";
startInfo.Arguments = "-t soc(COM84,CC2650) -e -p -v -f c:\test.bin";
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
Process process = new Process();
process.StartInfo = startInfo;
process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
Console.WriteLine(outLine.Data);
}
Well, first of all, when executing PsExec commands from C#, part of the output is in the StandardOutput and another part is in the StandardError for some reason, as mentioned in other posts, but that's not big of a deal.
Now, the problem is: Even when combining the two parts together, the "output" isn't exactly the same compared to what gets displayed on the Command Prompt window. Sometimes it's different and sometimes it's incomplete.
Here's my code:
string args = $#"{computerName} -u ""{username}"" -p ""{password}"" {pathOrCommand}";
var pi = new ProcessStartInfo("PsExec.exe", args);
pi.CreateNoWindow = true;
pi.WindowStyle = ProcessWindowStyle.Hidden;
pi.UseShellExecute = false;
pi.RedirectStandardOutput = true;
pi.RedirectStandardError = true;
using (Process p = Process.Start(pi))
{
var resultLines = new List<string>();
var handler = new DataReceivedEventHandler(delegate (object o, DataReceivedEventArgs e)
{
resultLines.Add(e.Data);
});
p.ErrorDataReceived += handler;
p.OutputDataReceived += handler;
p.BeginErrorReadLine();
p.BeginOutputReadLine();
p.WaitForExit();
string result = string.Join("\r\n", resultLines);
}
An example of when it's different:
When passing the wrong username or password, the following message will be displayed on Command Prompt: *
Logon failure: unknown user name or bad password.
..while the output of the above code will be:
The handle is invalid.
An example of when it's incomplete (this is more important):
When executing this command PsExec \\computer -u username -p password query session, the result looks something like this: *
SESSIONNAME USERNAME ID STATE TYPE DEVICE
>services 0 Disc
console someUserName 1 Active
rdp-tcp 65537 Listen
..but the output when executing from C# is only:
SESSIONNAME USERNAME ID STATE TYPE DEVICE
So, my questions are:
Why is Process.StandardOutput / Process.StandardError different from the actual output?
Is there any way around this?
* I'm only presenting the relevant part of the output here.
I still don't understand why the output is different but I managed to create a workaround which I'm going to use unless someone provides a better solution.
I used cmd.exe to redirect the stdout and stderr to a temp file. After the process exits, I read the contents of the file and then delete it:
string tempFile = Path.GetTempFileName();
string args = $#"/c PsExec.exe {computerName} -u ""{username}"" -p ""{password}"" {pathOrCommand}";
var pi = new ProcessStartInfo("cmd.exe", $"{args} 1> {tempFile} 2>&1");
pi.CreateNoWindow = true;
pi.WindowStyle = ProcessWindowStyle.Hidden;
using (Process p = Process.Start(pi))
{
p.WaitForExit();
string result = File.ReadAllText(tempFile);
File.Delete(tempFile);
}
Hope this could help someone else having the same problem.
I am currently working on a C# Program which needs to call a local PHP script and write its output to a file. The problem is, that I need to be able to stop the execution of the script.
First, I tried to call cmd.exe and let cmd write the output to the file which worked fine. But I found out, that killing the cmd process does not stop the php cli.
So I tried to call php directly, redirect its output and write it from the C# code to a file. But here the problem seems to be, that the php cli does not terminate when the script is done. process.WaitForExit() does not return, even when I am sure that the script has been fully executed.
I cannot set a timeout to the WaitForExit(), because depending on the arguments, the script may take 3 minutes or eg. 10 hours.
I do not want to kill just a random php cli, there may be others currently running.
What is the best way to call a local php script from C#, writing its output to a file and beeing able to stop the execution?
Here is my current code:
// Create the process
var process = new System.Diagnostics.Process();
process.EnableRaisingEvents = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "php.exe";
// CreateExportScriptArgument returns something like "file.php arg1 arg2 ..."
process.StartInfo.Arguments = CreateExportScriptArgument(code, this.content, this.options);
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
// Start the process or cancel, if the process should not run
if (!this.isRunning) { return; }
this.currentProcess = process;
process.Start();
// Get the output
var output = process.StandardOutput;
// Wait for the process to finish
process.WaitForExit();
this.currentProcess = null;
To kill the process I am using:
// Mark as not running to prevent starting new
this.isRunning = false;
// Kill the process
if (this.currentProcess != null)
{
this.currentProcess.Kill();
}
Thanks for reading!
EDIT
That the cli does not return seems to be not reproducible. When I test a different script (without arguments) it works, probably its the script or the passing of the arguments.
Running my script from cmd works just fine, so the script should not be the problem
EDIT 2
When disabling RedirectStandardOutput, the cli quits. could it be, that I need to read the output, before the process finishes? Or does the process wait, when some kind of buffer is full?
EDIT 3: Problem solved
Thanks to VolkerK, I / we found a solution. The problem was, that WaitForExit() did not get called, when the output is not read (probably due to a full buffer in the standard output). My script wrote much output.
What works for me:
process.Start();
// Get the output
var output = process.StandardOutput;
// Read the input and write to file, live to avoid reading / writing to much at once
using (var file = new StreamWriter("path\\file", false, new UTF8Encoding()))
{
// Read each line
while (!process.HasExited)
{
file.WriteLine(output.ReadLine());
}
// Read the rest
file.Write(output.ReadToEnd());
// flush to file
file.Flush();
}
Since the problem was that the output buffer was full and therefore the php process stalled while waiting to send its output, asynchronously reading the output in the c# program is the solution.
class Program {
protected static /* yeah, yeah, it's only an example */ StringBuilder output;
static void Main(string[] args)
{
// Create the process
var process = new System.Diagnostics.Process();
process.EnableRaisingEvents = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "php.exe";
process.StartInfo.Arguments = "-f path\\test.php mu b 0 0 pgsql://user:pass#x.x.x.x:5432/nominatim";
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
output = new StringBuilder();
process.OutputDataReceived += process_OutputDataReceived;
// Start the process
process.Start();
process.BeginOutputReadLine();
// Wait for the process to finish
process.WaitForExit();
Console.WriteLine("test");
// <-- do something with Program.output here -->
Console.ReadKey();
}
static void process_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data)) {
// edit: oops the new-line/carriage-return characters are not "in" e.Data.....
// this _might_ be a problem depending on the actual output.
output.Append(e.Data);
output.Append(Environment.NewLine);
}
}
}
see also: https://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline%28v=vs.110%29.aspx
am doing application of downloading images using wget process in c#.
But while downloading images I am providing type of image i.e. .jpeg or .png. Through wget command.
If while downloading I am passing .jpeg and if .jpeg is not present, Then I want to trap error "file not found" through process class. But it is not happening.
My code is below:
class TrapProcessError
{
private Process process = new Process();
public TrapProcessError()
{
}
public void Start()
{
string args= "-r -c -np --retry-connrefused --tries=0 "url containing image folder" -P C:\\Images --cut-dirs=2 -nH -A jpeg -o C:\\Images\\error.log";
string filename= "path of wget\\wget.exe";
process.StartInfo.FileName = filename;
process.StartInfo.Arguments = args;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.ErrorDataReceived += this.ProcessErrorData;
process.OutputDataReceived += this.ProcessOutputData;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
}
private void ProcessErrorData(object sender, DataReceivedEventArgs e)
{
string message = e.Data;
if (message.Contains("file not found"))
{
Console.WriteLine("Error :" +message);
process.Close();
}
}
private void ProcessOutputData(object sender, DataReceivedEventArgs e)
{
string message = e.Data;
Console.WriteLine("Output :" +message);
}
public static void Main(string[] args)
{
TrapProcessError trapProcessError= new TrapProcessError();
trapProcessError.Start();
}
}
In above code if jpeg is not present then in erroe.log coming "file not found". But through process class not trapping error i.e. in ProcessErrorData e.Data is always coming null. So how can I trap error is there any other way?
Any Help is appreciated.
wget (in versions above 1.12) does return a reliable exitcode.
0 No problems occurred.
1 Generic error code.
2 Parse error—for instance, when parsing command-line options, the ‘.wgetrc’ or ‘.netrc’...
3 File I/O error.
4 Network failure.
5 SSL verification failure.
6 Username/password authentication failure.
7 Protocol errors.
8 Server issued an error response.
Prior to 1.12 you're in trouble:
In versions of Wget prior to 1.12, Wget’s exit status tended to be unhelpful and inconsistent. Recursive downloads would virtually always return 0 (success), regardless of any issues encountered, and non-recursive fetches only returned the status corresponding to the most recently-attempted download.
The exitcode of the process is handed back to the Process instance in the ExitCode property. You should leverage that and keep the (error) logging for convenience.
public void Start()
{
string args= "-r -c -np --retry-connrefused --tries=0 "url containing image folder" -P C:\\Images --cut-dirs=2 -nH -A jpeg -o C:\\Images\\error.log";
string filename= "path of wget\\wget.exe";
process.StartInfo.FileName = filename;
process.StartInfo.Arguments = args;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.ErrorDataReceived += this.ProcessErrorData;
process.OutputDataReceived += this.ProcessOutputData;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
if (process.ExitCode > 0)
{
// do what you need to do in case of an Error
Console.WriteLine("Error occured:{0}", process.ExitCode);
}
}
If you absolutely want to respond to the error messages that are logged (either to standardoutout or erroroutput), make sure you check the correct strings: If the log shows ERROR 404: Not Found then this message.Contains("file not found") will never be true. This is a reason I always tend to stay away from logfile parsing.
Do notice that processes are not required to write errormessage to the standard error stream. The errormessages can very well come in the standard output stream as well...