Execute another EXE from an application with parameters, as admin - c#

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

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
}

How to wait for the remote exe to complete - c#

The code looks lengthy but it's a simple program.
I have built a console app (TakeScreenshots) that will take website screenshots from firefox, chrome & ie in that order & save them in a folder. When I manually run TakeScreenshots.exe, all 3 screenshots are saved.
Now, I have built another console app (MyApp) that will execute TakeScreenshots.exe. But in this way, only the firefox screenshot is saved and not of the other 2. There are no exceptions. It just says "Process Complete". I guess, MyApp is not waiting for the TakeScreenshots to complete.
How can I fix this.
[TakeScreenshots will later be placed in few remote computers & run by MyApp]
TakeScreenshots code:
private static string[] WebDriversList = ["firefox","chrome","internetexplorer"];
private static void TakeAPic()
{
string url = "http://www.google.com";
string fileNamePrefix = "Test";
string snapSavePath = "D:\\Pics\\";
foreach (string wd in WebDriversList)
{
IWebDriver NewDriver = null;
switch (wd.ToLower())
{
case "firefox":
using (NewDriver = new FirefoxDriver())
{
if (NewDriver != null)
{
CaptureScreenshot(NewDriver, url, fileNamePrefix, snapSavePath);
}
}
break;
case "chrome":
using (NewDriver = new ChromeDriver(WebDriversPath))
{
if (NewDriver != null)
{
CaptureScreenshot(NewDriver, url, fileNamePrefix, snapSavePath);
}
}
break;
case "internetexplorer":
using (NewDriver = new InternetExplorerDriver(WebDriversPath))
{
if (NewDriver != null)
{
CaptureScreenshot(NewDriver, url, fileNamePrefix, snapSavePath);
}
}
break;
}
if (NewDriver != null)
{
NewDriver.Quit();
}
}
}
private static void CaptureScreenshot(IWebDriver driver,string url,string fileNamePrefix,
string snapSavePath)
{
driver.Navigate().GoToUrl(url);
Screenshot ss = ((ITakesScreenshot)driver).GetScreenshot();
ICapabilities capabilities = ((RemoteWebDriver)driver).Capabilities;
ss.SaveAsFile(snapSavePath + fileNamePrefix + "_" + capabilities.BrowserName + ".png",
ImageFormat.Png);
}
MyApp code:
static void Main(string[] args)
{
ExecuteTakeScreenshot();
Console.WriteLine("PROCESS COMPLETE");
Console.ReadKey();
}
private static void ExecuteTakeScreenshot()
{
ProcessStartInfo Psi = new ProcessStartInfo("D:\\PsTools\\");
Psi.FileName = "D:\\PsTools\\PsExec.exe";
Psi.Arguments = "/C \\DESK101 D:\\Release\\TakeScreenshots.exe";
Psi.UseShellExecute = false;
Psi.RedirectStandardOutput = true;
Psi.RedirectStandardInput = true;
Process.Start(Psi).WaitForExit();
}
Update:
It was my mistake. Initially WebDriversPath was assigned "WebDrivers/". When I changed it to the actual path "D:\WebDrivers\", it worked. But I still dont understand how it worked when TakeScreenshots.exe was run manually and it doesn't when run from another console
In similar problems I have had success with waiting for input idle first. Like this:
Process process = Process.Start(Psi);
process.WaitForInputIdle();
process.WaitForExit();
You could try this. For me it was needed to print a pdf using Adobe Reader and not close it to early afterwards.
Example:
Process process = new Process();
process.StartInfo.FileName = DestinationFile;
process.StartInfo.Verb = "print";
process.Start();
// In case of Adobe Reader the following statement is needed:
process.WaitForInputIdle();
process.WaitForExit(2000);
process.WaitForInputIdle();
process.Kill();

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();
}
}

Execute multiple command lines with output

I want to perform some command lines to display the result after each input.
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine("ftp");
//output
sw.WriteLine("open ftp.server.com");
//output
sw.WriteLine("username");
//output
sw.WriteLine("password");
//output
}
}
Help me to understand how to make the output result after each sw.WriteLine(...)?
Updated
It is not working with ftp. Why?
Initialization:
Test test = new Test();
test.start();
Console.ReadKey();
Class Test:
class Test
{
static StringBuilder StdOutput = new StringBuilder();
Process p = null;
Queue<string> cmdQueue = new Queue<string>();
public void start(){
cmdQueue = new Queue<string>();
cmdQueue.Enqueue("cd c:\\");
cmdQueue.Enqueue("dir");
cmdQueue.Enqueue("ftp");
cmdQueue.Enqueue("open us1.hostedftp.com");
cmdQueue.Enqueue("z3r9#ya.ru");
cmdQueue.Enqueue("123456");
cmdQueue.Enqueue("dir");
setupProcess();
startProcess();
}
private void setupProcess()
{
p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
StdOutput = new StringBuilder();
p.StartInfo = info;
}
private async void startProcess()
{
p.Start();
p.BeginOutputReadLine();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
while (cmdQueue.Count > 0)
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd != "")
{
await sw.WriteLineAsync(cmd);
Thread.Sleep(100);
//System.Console.WriteLine(StdOutput);
}
else
{
break;
}
}
Console.WriteLine(StdOutput);
}
p.WaitForExit();
}
}
private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
StdOutput.Append(Environment.NewLine + outLine.Data);
//System.Console.WriteLine(Environment.NewLine + outLine.Data);
}
}
}
I assume that you are actually asking about how to catch outputs from all the commands you want to have executed in the (one) process.
Here is a version of a solution I came up with a long time ago, when I was a rookie here..
The trick is to collect the output as is comes along by listening to events the Process will trigger whenever output gets created: OutputDataReceived and ErrorDataReceived. We need to run things async for this to work, so it will look a little more complicated than the usual examples, which only have one process executing one command..:
First a few variables:
Queue<string> cmdQueue = new Queue<string>();
static StringBuilder StdOutput = new StringBuilder();
static StringBuilder ErrOutput = new StringBuilder();
Process p = null;
Task processTask = null;
bool processIsRunning = false;
Here is a button click event that starts processing all commands from a multiline TextBox. Output gets collected in the two StringBuilders; when the queue is empty, I wait a little longer..:
private void button1_Click(object sender, EventArgs e)
{
cmdQueue = new Queue<string>(tb_commands.Lines.ToList());
setupProcess();
startProcessTask();
while (cmdQueue.Count > 0) Thread.Sleep(100);
Thread.Sleep(500);
tb_out.AppendText(StdOutput + "\r\n" + ErrOutput + "\r\n");
}
Here is the routine that set up the Process. Here we register two events that will notify us when there are lines in the output streams..:
private void setupProcess()
{
p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
p.ErrorDataReceived += new DataReceivedEventHandler(ErrorDataHandler);
StdOutput = new StringBuilder();
ErrOutput = new StringBuilder();
p.StartInfo = info;
}
After the setup we can start a Task that will start our Process asynchonously..:
private void startProcessTask()
{
var task = Task.Factory.StartNew(() => startProcess());
processTask = task;
}
..and finally here is the async method that after starting the Process and beginning with the asynchronous read operations on the redirected streams, keeps feeding it all lines from the command queue.
private async void startProcess()
{
try { p.Start(); processIsRunning = true; } catch
{
ErrOutput.Append("\r\nError starting cmd process.");
processIsRunning = false;
}
p.BeginOutputReadLine();
p.BeginErrorReadLine();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
do
{
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd != "") await sw.WriteLineAsync(cmd);
} catch { }
} while (processIsRunning);
try { p.WaitForExit(); } catch { ErrOutput.Append("WaitForExit Error.\r\n"); }
}
}
The last pieces are the two events we have registered for reading the output from the two streams and adding them to the StringBuilders:
private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
StdOutput.Append(Environment.NewLine + outLine.Data);
}
}
private static void ErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
ErrOutput.Append(Environment.NewLine + outLine.Data);
}
}
Note that this works fine for all sorts of commands you can feed into the process, including FTP. Here I change my codepage, show the images I have before, log in to an FTP server, call up the help page, cd and dir, download an image, close the connection and check the images I have now..:
One Caveat: There must be something wrong in the way I wrote this, as VS keeps complaining about a System.InvalidOperationException and the exe file hogs ~10% cpu. Helping me out would be very much appreciated..

Redirect command line output to MVC controller

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;
}

Categories