Process.WaitForExit() on Console vs Windows Forms - c#

I have a console app and a win forms app that both need to call out to a remote server for some data, they make a call to the command line part of Putty, plink.exe, to run a remote command over SSH.
I created a tiny class library for both to share, running the following:
public static string RunCommand(string command, string arguments) {
ProcessStartInfo startInfo = new ProcessStartInfo {
FileName = command,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true
};
string output = null;
using (Process p = new Process()) {
p.StartInfo = processStartInfo;
p.Start();
output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
return output;
}
Under the console application everything works fine, under the win forms it doesn't error, it seems that WaitForExit() just doesn't wait. I get an empty string for output. I've confirmed from the remote server the user logged in, so it seems the command has run.
Any ideas?

Under Windows Console applications have STDIN, STDOUT, and STDERR. Windowed applications do not. When you create a process under a Console application the STDIN etc. are inherited by the child application. This does not happen in the Windowed application.
The RedirectStandardInput=true works because it makes the system create a Writer for the STDIN that you can use to send input to the child process. In your case the child doesn't need the input it just needs the presence of the input. YMMV.

Related

Stream commands to WSL

I'm trying to write a console application that starts wsl in a background process and streams different commands to it for some automation stuff.
Important: I cannot just do wsl -- mycommand and then exit as I need wsl to stay running in the background thus I chose to keep the process alive as long as my application runs.
To start the process I'm using Process.Start
var startInfo = new ProcessStartInfo
{
FileName = "wsl",
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
var process = Process.Start(startInfo);
To execute commands I just feed them into process.StandardInput followed by \n
process.StandardInput.Write("uname -a\n");
process.StandardInput.Flush();
After that I'm able to read the commands output using e.g. process.StandardOutput.ReadLine.
The problem with the code is that I'm not able to detect when the command finished i.e. there will be no more output to capture.
uname -a outputs a single line so that case is easy to handle but what if the command writes multiple lines to stdout?
Is there a way to solve this or is my approach to do this already wrong to start with?
(I need a synchronous way to get the output after executing the command as I need to evaluate it after execution)

Is it possible to require user rights when opening second form ,when application run as administrator?

I know you can create a manifest file to specify access level administrator for the whole application. But is it possible to only require it for a specific form?
No, this is not possible.
What you can do: Let your process run without elevation.
When you discover that elevation is required but your process does not run elevated, restart the process (Process.Start) with the "runas" verb and maybe some command line option that you can evaluate in the newly started process to immediately open the form.
if (!RunningElevated())
{
// restart as elevated process
ProcessStartInfo psi = new ProcessStartInfo
{
UseShellExecute = true,
Verb = "runas",
WorkingDirectory = Environment.CurrentDirectory,
FileName = Assembly.GetExecutingAssembly().Location,
Argument = "--open MyForm" // has to be evaluated on the startup code
};
var process = Process.Start(psi);
if (process != null)
Application.Current.Shutdown(0); // this is for WPF
}

Attach Console to (java) process from WPF app

I currently start a bunch of java processes from my WPF app using Process, normally those would run in their own window. However when I want to send commands to the java app from my WPF app it requires me to run it with
StartInfo.UseShellExecute = false; which causes the process to run without a console.
I've tried to look for answers here on SO (How to open/close console window dynamically from a wpf application?) and a few others but most seem to be for the app to be either ran in console mode or in UI mode. Nothing about attaching a console to a System.Diagnostics.Process
This version would open a Console
namespace MMSG.Instances
{
public abstract class JavaServer : Process
{
protected JavaServer(string workingDir)
{
StartInfo.FileName = "java";
StartInfo.UseShellExecute = true;
StartInfo.RedirectStandardInput = true;
StartInfo.WorkingDirectory = workingDir;
StartInfo.CreateNoWindow = false;
StartInfo.ErrorDialog = true;
EnableRaisingEvents = true;
}
protected JavaServer(string ram, string workingDir, string jarLocation) : this(workingDir)
{
StartInfo.Arguments = $"-Xms{ram} -Xmx{ram} -jar \"{jarLocation}\"";
}
}
}
however when trying to send input to the Processusing javaServer.StandardInput.WriteLine("stop");
I get this error: System.InvalidOperationException: The Process object must have the UseShellExecute property set to false in order to redirect IO streams. And as stated before setting that to false removes the Console window altogether.
Expected result: allow sending of commands to the process while still having a Console window.
Actual result: Either no commands or no Console

How can I open an external Java console application with arguments, capture output and execute commands on it?

I have a Java .jar application that is ran from a .bat file to have arguments passed to the Java application. The application opens a console (cmd.exe to be exact), writes things to it regularly and accepts some commands. I'm trying to create a kind-of wrapper around it in C# Winforms to ease it's use. How can I run the .jar with the same arguments as are in the .bat file, capture realtime output and write execute commands?
Yes, it is possible to do this using the System.Diagnostics.Process class and the ProcessStartInfo class from the .NET Framework. The Process class is used to control (start / stop) the desired process (application) and the ProcessStartInfo class is used to configure the process instance that will be started (arguments, redirect input and output, show / hide process window, and so on).
The code for starting a jar file looks like this:
var jarFile = "D:\\software\\java2html\\java2html.jar");
// location of the java.exe binary used to start the jar file
var javaExecutable = "c:\\Program Files (x86)\\Java\\jre7\\bin\\java.exe";
try
{
// command line for java.exe in order to start a jar file: java -jar jar_file
var arguments = String.Format(" -jar {0}", jarFile);
// create a process instance
var process = new Process();
// and instruct it to start java with the given parameters
var processStartInfo = new ProcessStartInfo(javaExecutable, arguments);
process.StartInfo = processStartInfo;
// start the process
process.Start();
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
The usual way to start a jar file is:
java -jar file.jar
To be sure, that the process will find the (in this case java) executable, it is a good practice to specify the fully qualified path to the process you want to start.
In order to redirect the standard output of the application you are starting, you need to set the ProcessStartInfo.RedirectStandardOutput property to true and then use the Process.StandardOutput property stream to fetch the output of the started application. The modified code for the application from the example above looks like this:
// command line for java.exe in order to start a jar file: java -jar jar_file
var arguments = String.Format(" -jar {0}", jarFile);
// indicate, that you want to capture the application output
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
// create a process instance
var process = new Process();
// and instruct it to start java with the given parameters
var processStartInfo = new ProcessStartInfo(javaExecutable, arguments);
process.StartInfo = processStartInfo;
// start the process
process.Start();
// read the output from the started appplication
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
If you want to control the input too, set the ProcessStartInfo.RedirectStandarInput property to true and then use the Process.StandardInput property stream to send input data to the started application.

How to run a command in terminal and capture the output

I am working on an application using MonoDevelop in Ubuntu. Through this application I need to run a terminal command and capture it's output. Is such thing possible? Any help/ideas will be appreciated!
What I mean is, if user clicks on a button, the command will be run and the output will be displayed in a text box or something. I don't want a terminal window pop-up, this action should be entirely done inside the application.
In C# Windows you would do this:
Process p = new Process(); // Redirect the output stream of the child process.
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "program.exe";
p.Start(); // Do not wait for the child process to exit before reading to the end of its redirected stream.
p.WaitForExit(); // Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

Categories