Redirect command line output to MVC controller - c#

I'm developing an internet application using MVC and C# with Visual Studio 2013. I need to implement a feature that enables to automatically login in a website. To do that, I use the CasperJs framework.
The following code creates a thread that will execute js code in a command line.
[HttpPost]
public ActionResult Publish()
{
try
{
new Thread(new ParameterizedThreadStart(x =>
{
ExecuteCommand("casperjs test.js");
})).Start();
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
catch (Exception e)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, e.Message);
}
}
private void ExecuteCommand(string Command)
{
try
{
ProcessStartInfo processInfo = new ProcessStartInfo("cmd.exe", "/K " + Command);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
Process process = Process.Start(processInfo);
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
}
catch (Exception e)
{
Trace.Listeners.Add(new TextWriterTraceListener(e.ToString()));
}
}
To be sure that eveything went right, I want to redirect the output produced in the command line to my controller. How do I do that? With the previous code the string output is always null.

I wasn't sure if you wanted to actually return the output of the command as a response to the Publish, but the altered 'ExecuteCommand' method should help to obtain the standard output from the process. From there, you can do what you like based upon expected values that may be contained in that output.
[HttpPost]
public ActionResult Publish()
{
try
{
string cmdResult = "";
new Thread(new ParameterizedThreadStart(x =>
{
cmdResult = ExecuteCommand("casperjs test.js");
})).Start();
var response = Request.CreateResponse<string>(System.Net.HttpStatusCode.OK, cmdResult);
return response;
}
catch (Exception e)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, e.Message);
}
}
private string ExecuteCommand(string Command)
{
string result = "";
try
{
ProcessStartInfo processInfo = new ProcessStartInfo("cmd.exe", "/K " + Command);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardError = true;
Process process = Process.Start(processInfo);
process.WaitForExit();
while (!process.StandardOutput.EndOfStream)
{
result += process.StandardOutput.ReadLine();
}
while (!process.StandardError.EndOfStream)
{
result += process.StandardError.ReadLine();
}
}
catch (Exception e)
{
Trace.Listeners.Add(new TextWriterTraceListener(e.ToString()));
throw;
}
return result;
}

Related

How to make ffmpeg to throw exception on try/catch for working directory and for arguments?

If for example i'm setting the FileName(outputDirectory) to null it will throw exception.
The class when i'm using process of the ffmpeg.exe :
public void Start()
{
process = new Process();
process.StartInfo.FileName = outputDirectory; // Change the directory where ffmpeg.exe is.
process.EnableRaisingEvents = false;
process.StartInfo.WorkingDirectory = workingDirectory; // The output directory
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true; //Redirect stdin
process.StartInfo.CreateNoWindow = true;
process.Start();
errorMessage = false;
startRecord = true;
}
but if i'm setting the WorkingDirectory to null or entering some not valid Arguments like "sadasdasd" it will not throw exception not on the WorkingDirectory and not on the Arguments.
In Form1 :
private void recordStripMenuItem_Click(object sender, EventArgs e)
{
recordToggle = !recordToggle;
if (recordToggle)
{
try
{
record.workingDirectory = settingsForm.workingDirectory;
record.outputDirectory = settingsForm.outputDirectory;
record.arguments = settingsForm.arguments;
record.Start();
}
catch(Exception ex)
{
recordStripMenuItem.Text = "Record";
Icon = iconGreen;
TextInfo("Waiting");
recordToggle = false;
MessageBox.Show("Arguments are not valid : " + ex.Message);
}
if (!FFmpeg_Capture.errorMessage)
{
settingsForm.Close();
recordStripMenuItem.Text = "Stop";
Icon = iconRed;
TextInfo("Recording");
}
}
else
{
recordStripMenuItem.Text = "Record";
Icon = iconGreen;
TextInfo("Waiting");
record.Stop();
}
}
The variable record is instance of the FFmpeg_Capture class with the process of the ffmpeg.
It's throwing exception only on the FileName(outputDirectory).
In your start() method add the following code that throws an exception if the working directory doesn't exist:
public void Start(){
if(!System.IO.Directory.Exists(this.workingDirectory){
throw new Exception("workingDirectory doesn't exist");
}
//.. the rest of the code
}

Execute another EXE from an application with parameters, as admin

I have created two projects under the same solution. ProjectA is a Windows Form Application and ProjectB is a simple console application.ProjectB will be executed from ProjectA with admin privileges.
Sample from ProjectA
private void btnFinish_Click(object sender, EventArgs e)
{
ipAddress = txtIP.Text;
bindingPort = txtPort.Text;
if (!fileChosen)
{
CreateCertificate();
//
}
//After this step i want to execute ProjectB with admin provileges with 3 parameters
ExecuteB_AsAdminWithPrivileges(ipAddress, bindingPort, serverCert);
}
}
So when i click the button name Finish i want the ProjectB.exe to be executed with parameters that i will give from ProjectA.
And ProjectB will look sth like:
public static void StoreAndBindCertificate(string pfxFileServerCert, string ipAddress, string ipPort)
{
//
}
This is the method which will be using the parameters from ProjectA.
How can i get the Parameters from ProjectA to this method in ProjectB?
You could use this method:
public static int RunProcessAsAdmin(string exeName, string parameters)
{
try {
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = CurrentDirectory;
startInfo.FileName = Path.Combine(CurrentDirectory, exeName);
startInfo.Verb = "runas";
//MLHIDE
startInfo.Arguments = parameters;
startInfo.ErrorDialog = true;
Process process = System.Diagnostics.Process.Start(startInfo);
process.WaitForExit();
return process.ExitCode;
} catch (Win32Exception ex) {
WriteLog(ex);
switch (ex.NativeErrorCode) {
case 1223:
return ex.NativeErrorCode;
default:
return ErrorReturnInteger;
}
} catch (Exception ex) {
WriteLog(ex);
return ErrorReturnInteger;
}
}
The first parameter will be your .exe file and the second one will be the parameters you want to give to your .exe file
After this you should make changes in your .exe file in the main section.
Something like:
static void Main(string[] args)
{
if (args.Length <= 1) return;
try
{
if (args.Length == 2)
{
_IpAddress = args[0];
_IpPort = args[1];
FunctionName(_IpAddress, _IpPort);
}
else
{
_return
}
}
catch (Exception)
{
throw new Exception("Invalid number of parameters!");
}
}
I hope this helps.
Update
ProgramA{
string ip ="123.123.123";
File.WriteAllText("c://MtDataFromA.txt","ip="+ip);
}
private void btnFinish_Click(object sender, EventArgs e)
{
ipAddress = File.WriteAllText("c://MtDataFromA.txt");//some algorithem to find the ip from text file
}
public static void StoreAndBindCertificate(string pfxFileServerCert, string ipAddress, string ipPort){
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "YourFile.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "ipAddress"+" " +"ipPort";
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
// Log error.
}
}
link

Process.Start takes long time to start outside application

I have a method that launches a second exe. The issue I'm having is that if I'm in debug mode in Visual Studio and I put a breakpoint directly after the Process.Start call my second application launches immediately but if I have no break points in VS or run my main C# application outside of VS the launching of my second application via Process.Start can take up to two minutes. My method is below and where I put my breakpoint to see an immediate launch of the 2nd app is at line "if(null != _ProcessMine)". I put the launch of the second exe in a worker thread because when I close my main exe I want the second exe to close also.
public static void RunBtnProcessThread(string processName, String sArgs, Button btn)
{
// disable the button until we release the newly launched process
btn.Enabled = false;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (doWorkSender, doWorkArgs) =>
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = processName;
startInfo.Arguments = sArgs;
try
{
using ( _ProcessMine = Process.Start(startInfo))
{
if(null != _ProcessMine)
_ProcessMine.WaitForExit();
}
}
catch (Exception ex)
{
string _Funk = ReflectionHelper.GetMethodFullName(MethodBase.GetCurrentMethod());
// error
Debug.Assert(false, "Error: " + ex.Message);
// Log error.
TraceUtil.LogException(_Funk, ex);
}
System.Threading.Thread.Sleep(500);
};
worker.RunWorkerCompleted += (completedSender, completedArgs) =>
{
btn.Enabled = true;
_ProcessMine)= null;
};
worker.RunWorkerAsync();
}
You don't actually need a separate thread for your scenario. You can accomplish the same thing by subscribing to the Process.Exited() event:
public static void RunBtnProcessThread(string processName, String sArgs, Button btn)
{
// disable the button until we release the newly launched process
btn.Enabled = false;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = processName;
startInfo.Arguments = sArgs;
try
{
_ProcessMine = Process.Start(startInfo);
_ProcessMine.EnableRaisingEvents = true;
_ProcessMine.Exited += (sender, e) =>
{
btn.Invoke((MethodInvoker)delegate {
btn.Enabled = true;
});
_ProcessMine = null;
};
}
catch (Exception ex)
{
string _Funk = ReflectionHelper.GetMethodFullName(MethodBase.GetCurrentMethod());
// error
Debug.Assert(false, "Error: " + ex.Message);
// Log error.
TraceUtil.LogException(_Funk, ex);
}
}
You could close it using something like:
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_ProcessMine != null && !_ProcessMine.HasExited)
{
// Depending on the type of app:
_ProcessMine.CloseMainWindow();
// ... or ...
_ProcessMine.Kill();
}
}

RedirectStandard Output/Error wont call my event for Standard Output/Error

hi i'm trying to build a parser for my System to Manage my Tekkit Server i am using C# but i have RedirectStandardOutput on my Tekkit Server process and there is a method set-up to then send that output to my console after adding to a List but it's not adding to a List<string>
Here is my code:
public void StartServer(string maxMem, string minMem, string path)
{
ThreadStart server = new ThreadStart(delegate() { StartServerThread(maxMem, minMem, path); });
server.Invoke();
}
private void StartServerThread(string maxMem, string minMem, string TekkitPath)
{
try
{
TekkitServer.StartInfo.FileName = "java";
TekkitServer.StartInfo.Arguments = String.Format("-Xmx{0} -Xms{1} -jar \"" + TekkitPath + "\" -nojline nogui", maxMem, minMem);
TekkitServer.StartInfo.UseShellExecute = false;
TekkitServer.StartInfo.RedirectStandardInput = true;
TekkitServer.StartInfo.RedirectStandardOutput = true;
TekkitServer.OutputDataReceived += new DataReceivedEventHandler(TekkitServer_OutputDataReceived);
IsStarted = TekkitServer.Start();
TekkitServerInput = TekkitServer.StandardInput;
}
catch (Exception)
{
}
}
void TekkitServer_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
/*B*/recordedData.Add(e.Data);
Console.Out.WriteLine(e.Data);
}
Where /*B*/ is a break point, the breakpoint is never activating
By default, the standard output is directed at the console window.
If you need to do something with it, you need to redirect it, hence, you need to set RedirectStandardOutput = true; for the event to be fired.
Edit: This is my working code (with error handling and logging omitted):
public int ExecuteCommand(CommandParameters parameters)
{
Process process = new Process();
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += StdOutputHandler;
process.ErrorDataReceived += StdErrorHandler;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = ...;
process.StartInfo.Arguments = ...;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit(parameters.Timeout);
return process.ExitCode;
}
private void StdOutputHandler(object sendingProcess, DataReceivedEventArgs outdata)
{
if (!string.IsNullOrEmpty(outdata.Data))
{
OutputMessages.Add(outdata.Data);
}
}
Most likely the missing link in your code is the BeginOutputReadLine that actually gets the handler method on it's way.
Also, I use a fresh Process object and that I wait on it to finish it's job, so no interference with previous calls is possible.

Why doesnt this method redirect my output from .exe [ffmpeg]?

I have the method:
public static string StartProcess(string exePathArg, string argumentsArg, int timeToWaitForProcessToExit)
{
string retMessage = "";
using (Process p = new Process())
{
p.StartInfo.FileName = exePathArg;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.Arguments = argumentsArg;
p.StartInfo.UseShellExecute = false;
try
{
p.Start();
StreamReader myOutput = p.StandardOutput;
retMessage = "STANDARD OUTPUT: " + myOutput.ReadToEnd();
p.WaitForExit(timeToWaitForProcessToExit);
}
catch (Exception ex)
{
retMessage = "EXCEPTION THROWN: " + ex.ToString();
}
finally
{
try
{
p.Kill();
}
catch { }
}
}
return retMessage;
}
But it doesnt redirect my output to retMessage. Anyone any ideas? I tested the arguments in a bat file and output is definitely output.
Cheers,
Pete
My guess (agree with dtb's comment):
AFAIK ffmpeg uses stdout to pipe out binary data(multimedia, snapshots, etc.) and stderr is used for logging purposes. In your example you use stdout.
So, change you code to:
p.StartInfo.RedirectStandardError = true;
...
string log = p.StandardError.ReadToEnd();
and it should solve your problem.

Categories