Issue Seperate Arguments to Exe in one construct - c#

I'm currently trying to pipe multiple parameters to the adb.exe file in the google sdk. an example of my inputs are:
adb shell getprop ro.build.version.release
adb shell getprop ro.product.brand
which are outputting correct from my application. Though, the problem is I want to populate a list view of information, the problem that i'm currently encountering though, is the method to pipe commands to get desired output. I've currently got:
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "Resources/adb.exe",
Arguments = "devices",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
string Output = proc.StandardOutput.ReadToEnd().ToString();
This currently returns as expected, but i'm wishing to get multiple results from piping something like:
Arguments = "devices ro.build.version.release ro.product.brand"
This provides no avail, even when piping directly into command prompt.
adb shell getprop devices ro.build.version.release ro.product.brand
empty, I have come around with a (assumingly) load heavy solution, which is to move the initialization of the executional into it's own function to be called multiple times. See code below:
public string GetInfo(string CommandArg)
{
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "Resources/adb.exe",
Arguments = CommandArg,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
return proc.StandardOutput.ReadToEnd().ToString();
}
public void SetDefineInformation()
{
AndroidVersion = decimal.Parse(GetInfo("ro.build.version.release"));
DeviceModel = GetInfo("ro.product.device");
...
}
To me, this seems like a load heavy task with constantly opening a single executable for it to close then re-open until the task is complete. Is there an overall work around, which might allow one to pipe multiple parameters to an executional and get the desired results?

adb can start a shell so you could create the shell with adb shell then redirect stdin and stdout to write to it directly

No measurable performance gain would come from the "optimization" you are proposing. The adb code is pretty efficient. The overhead it introduces is very minimal. Specially in the client part. Most of the delay comes from waiting for the device's response anyway.
So stop overcomplicating things and just run multiple adb shell sessions. If loading the adb binary bothers you so much - just use sockets to talk to the adb server directly. Take a look at the Mad Bee library code to see how to do it in c#. Or just use the library.
As for optimizing querying multiple system properties - just use adb shell getprop command to pull all properties at once.

Related

c# start a exe until it is completely started and then append the arguments

I was trying to start a exe with arguments by Process.Start.
My first try is using Process.Start("Path/of/the/exe", "arguments of exe").
Here's my code snippets:
Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
However the initialization of this exe is a bit slow, and the result is, I can only start the exe but the failed passing arguments. The following is the screenshot:
which is exactly the same result that starts without arguments.
By referencing this post C# - Making a Process.Start wait until the process has start-up, I changed my code as follows:
var process = Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
while (string.IsNullOrEmpty(process.MainWindowTitle))
{
System.Threading.Thread.Sleep(100);
process.Refresh();
}
however these changes does not work.
I think my goal is to wait until exe completely started and then run it with arguments, but I dont know how to implement this.
=====================================================
New additions:
if I type in arguments call 'D:\Work\202205\20220525\tunnel-for-cmd.txt' in this started process, I will get my result:
SO I think the input arguments should be OK?
=======================================
new addition 2:
code for checking outputstream end
It appears this is a console application and you are typing in the console after it starts. This typing is not arguments: Arguments are provided only when starting a new process and never change.
What you are doing is providing something to the standard input of the program. Console programs have three streams the OS provides (one input and two output). You need to redirect these to detect when the program has started and to provide the proper input.
Something like this:
// Start with stdio redirected
var psi = new ProcessStartInfo()
{
UseShellExecute = false,
FileName = #"your exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
};
var p = System.Diagnostics.Process.Start(psi);
// Read until the udec> prompt
while(true)
{
var line = p.StandardOutput.ReadLine();
if(line.StartsWith("udec>"))
break;
}
// Write the command
p.StandardInput.WriteLine(#"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
// Read the result
p.StandardOutput.ReadToEnd();

Git diff - path is outside repository

I would like to execute a git diff command. The following command results in an 'fatal - path is outside repository' error. It is implemented in a C# application, using the Process class.
git diff HEAD -- "Folder\TestFile.cs" >
"C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff"
fatal: C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff:
C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff is outside repository.
The command 'works', because I could successfuly execute it in a command prompt (cmd).
Could you explain what is wrong with the command and how to solve it in the C# application?
It looks like you're trying to redirect standard output to a file. Redirection is handled by the shell, and the Process class does not support it. Instead, it's passing the > C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff to the git process. That's why git is complaining that the path C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff is not in a repository.
If you're invoking a process and want to read its standard output, it's your responsibility to do that.
You can capture the output with the Process class. For example:
var proc = new Process {
StartInfo = new ProcessStartInfo {
FileName = "git.exe",
Arguments = "diff HEAD -- "Folder\TestFile.cs"",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
while (!proc.StandardOutput.EndOfStream) {
var line = proc.StandardOutput.ReadLine();
}
Of course, you could also use LibGit2Sharp and not have to worry about process creation and screen scraping the output.

Process.Start leaves streams empty

I have the code to run a console command/utility, monitor the live output using 'Debug.WriteLine' and write the final output to a log file when needed.
Edit: It does not work for Praatcon.exe an analysis command line utility. It can be downloaded from here . Just invoke praatcon.exe without argument, it should write on 'stdout' about the Usage. The code wont catch it.
The issue is, it works good for certain utilities and I can see the debug output as well as log in the file. But for certain utilities, I see empty commands, even though when I run those commands through a CMD window, I see the output. I am capturing both the streams Output and Error.
Can someone help me with this ?
Full code can be found here
Here is how I am trying to do it
Initialization of ProcessStartInfo
var info = new ProcessStartInfo(command, parameters)
{
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
Running the process and initializing the string builders for output and error streams.
var process = Process.Start(info);
var output = new StringBuilder();
var error = new StringBuilder();
Starting the tasks for Reading Streams
var errorTask = process.StandardError.ReadLineAsync();
var lineTask = process.StandardOutput.ReadLineAsync();
Here is my while loop to monitor the progress and write output to the Debug Output window when there is any available.
while (process.HasExited == false)
{
if (lineTask.IsCompleted)
{
output.AppendLine(lineTask.Result);
Debug.WriteLine(lineTask.Result);
lineTask = process.StandardOutput.ReadLineAsync();
}
if (errorTask.IsCompleted)
{
error.AppendLine(errorTask.Result);
Debug.WriteLine(errorTask.Result);
errorTask = process.StandardError.ReadLineAsync();
}
errorTask.Wait(TimeSpan.FromMilliseconds(100.0));
lineTask.Wait(TimeSpan.FromMilliseconds(100.0));
}
After this, I am reading the streams further to see if there is anything left in there.
I get empty strings in output and error for one command. The only thing I get correct is the 'ExitCode'.
Please tell me if there is anything I am doing the wrong way.
As discussed on IRC, there was a possibility that the program you're calling may have been writing to a stream other than standard out or standard error. There are also streams with numbers 3-9 on Windows.
This was not the case with the process you were calling. It was actually using the Win32 call 'WriteConsole', which seems to access the console directly.
It would be possible to move the output back to stderr by preloading a DLL (DLL injection) but this is hackish, so as the source of the program is available, it's perhaps better to 'fix' it or submit a patch to the authors.
Your question is borderline too broad, in that it's missing details, and "anything I am doing the wrong way" is fairly open-ended.
That said, you are doing the reading of the streams the wrong way, in the sense that you should not be polling. I don't see any specific reason that would cause the behavior you've (vaguely) described. But just in case, I offer this correct implementation of the reading:
async Task ConsumeStream(StreamReader reader, StringBuilder builder)
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
builder.AppendLine(line);
Debug.WriteLine(line);
}
}
Then call that method like this:
var errorTask = ConsumeStream(process.StandardError, error);
var lineTask = ConsumeStream(process.StandardOutput, output);
// Technically superfluous, since you'll also wait on the tasks,
// but won't hurt.
process.WaitForExit();
Task.WaitAll(errorTask, lineTask);
// error and output StringBuilders will be valid here
If that doesn't help, you'll need to post a better code example, which will allow others to reproduce the actual problem. See https://stackoverflow.com/help/mcve

Process stalls when launched in .NET (ffmpeg)

I'm trying to launch ffmpeg as a Process in .NET (C#), but in some cases, depending on the arguments (specifically, if I omit video arguments in order to create an audio file), it's stalling. It launches, outputs some lines, but then just stalls (using 0% CPU). When the parent .NET process is killed, it continues, and if I let it continue, ffmpeg produces the file correctly. I thought it might be due to using Peek() to look at the stream, so I just simplified it to the following, which behaves the same:
_process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = false,
RedirectStandardError = true,
FileName = "c:\\ffmpeg.exe",
Arguments = string.Format(
"-i {0} {1} {2} {3} -y {4}", inputPath, videoArgs, audioArgs, options, outputPath)
}
};
_process.Start();
_process.WaitForExit();
ffmpeg gets to the point where it outputs information about the input video/audio streams before stalling. Executing the command via the command prompt works as expected.
Does anyone know what the problem could be?
Edit:
Just to add, I tried UseShellExecute = true (and RedirectStandardError = false), and this works. I still need to read the output, however, so this doesn't really help me.
Have a read of this
MSDN on RedirectStandardError
Apparently this is a bit fiddly and can deadlock if the output or error stream buffers get filled up. Sit's there waiting for you to read what it's wrote...
According to the comments on this answer, in some cases FFmpeg manipulates the screen memory directly without using the standard output and error streams. One example of this is the [file] already exists. Overwrite? [y/N] prompt.
I'm guessing you've run the program outside if .NET to see what the output is? If it is a prompt like the one above then it might pay to avoid the trouble and attempt to provide an argument that will skip it eg. the -y command that forces FFmpeg to overwrite a file.

How to use Process.Start() or equivalent with Mono on a Mac and pass in arguments

I am trying to write some c# code to start a browser using Process.Start(app,args); where apps is the path to the browser e.g. /Applications/Google Chrome.app/Contents/MacOS/Google Chrome and the args are --no-default-browser-check
If i do, which works on Windows and on Linux
Process.Start("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","--no-first-run");
I get
open: unrecognized option `--no-first-run'
Usage: open [-e] [-t] [-f] [-W] [-n] [-g] [-h] [-b <bundle identifier>] [-a <application>] [filenames]
Help: Open opens files from a shell.
By default, opens each file using the default application for that file.
If the file is in the form of a URL, the file will be opened as a URL.
Options:
-a Opens with the specified application.
-b Opens with the specified application bundle identifier.
-e Opens with TextEdit.
-t Opens with default text editor.
-f Reads input from standard input and opens with TextEdit.
-W, --wait-apps Blocks until the used applications are closed (even if they were already running).
-n, --new Open a new instance of the application even if one is already running.
-g, --background Does not bring the application to the foreground.
-h, --header Searches header file locations for headers matching the given filenames, and opens them.
I have also tried Monobjc to try run the code with
// spin up the objective-c runtime
ObjectiveCRuntime.LoadFramework("Cocoa");
ObjectiveCRuntime.Initialize();
NSAutoreleasePool pool = new NSAutoreleasePool();
// Create our process
NSTask task = new NSTask();
NSPipe standardOut = new NSPipe();
task.StandardOutput = standardOut;
task.LaunchPath = #"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
// add some arguments
NSString argumentString = new NSString("--no-first-run");
NSArray arguments = NSArray.ArrayWithObject(argumentString);
task.Arguments = arguments;
// We should have liftoff
task.Launch();
// Parse the output and display it to the console
NSData output = standardOut.FileHandleForReading.ReadDataToEndOfFile;
NSString outString = new NSString(output,NSStringEncoding.NSUTF8StringEncoding);
Console.WriteLine(outString);
// Dipose our objects, gotta love reference counting
pool.Release();
But when I run my code using NUnit it causes NUnit to blow up.
I suspect that this is a bug but can't prove it. I appreciate any and all help!
To make Process.Start use exec directly instead of using the OS' mechanism for opening files, you must set UseShellExecute to false. This is also true on Linux and Windows.
Process.Start(new ProcessStartInfo (
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"--no-first-run")
{ UseShellExecute = false });
Note that you can also use 'open' for your use case, to run the Chrome app bundle properly. Use the '-a' argument to force it to run a specific app, the '-n' argument to open a new instance, and '--args' to pass in arguments:
Process.Start(new ProcessStartInfo (
"open",
"-a '/Applications/Google Chrome.app' -n --args --no-first-run")
{ UseShellExecute = false });
Looks like Process uses the open command line utility to launch.
You should avoid calling the executable directly. If the application is already running, this would launch a second instance of it instead of activating the already running instance. That's probably not what you want, and not all applications can handle this anyway.
With open, the syntax to launch Chrome would be
open -a Chrome
I don't know how the Process class works on MacOS X, but I assume that the parameters should be similar.
Note, if you just want to open a web page, you should not specify the executable; instead, just pass the URL, so that it will be opened in the user's default browser. This is valid for any platform.
Process.Start("http://www.google.com");
Have you tried something like concatenating the parameters into the process name instead of passing it separated?
var processName = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
var args = "--no-default-browser-check";
Process.Start(String.Format("{0} {1}", processName, args));
Why dont you try something like this:
Process P = new Process();
P.StartInfo.FileName = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
P.StartInfo.Arguments = "--no-default-browser-check";
P.UseShellExecute = false;
P.Start();

Categories