Sorry if this is something obvious, I'm super new to C#.
I'm working on a program that runs a python script to check something online, that then writes it back to a file that C# reads and puts on a form. Everything works if I manually run them both at the same time, but I really want to start the script from the C# program.
Here's the function that should start the python script:
private void RunPython()
{
Process p = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "python " + path + "\\foo.py";
p.StartInfo = startInfo;
p.Start();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// get things set up... etc.
RunPython();
}
I've tried running it without the window hidden, and it just brings up an empty cmd line in the 'path' directory. Is it never running the python script? It doesn't seem like it is, but it may also be running it and immediately closing it. I can't tell.
I need it to stay open for the duration of the C# program's run, so if the problem is it exiting when the RunPython() method returns, is there some different way I could go about this?
Thanks!!
If you want to run a program in the command line using arguments to cmd.exe you need the /c flag. Alternatively you can use /k but that keeps the command process running which you probably don't want.
/c Carries out the command specified by String and then stops.
So it's usage is cmd.exe /c [string]
Try changing your arguments line to:
startInfo.Arguments = "/c \"python " + path + "\\foo.py\"";
See here for more information about running cmd.exe and dealing with quotes in the string section of the command: https://technet.microsoft.com/en-us/library/cc771320(v=ws.11).aspx
Related
I would like to be able to open cmd and execute two commands from the window. First I would like to navigate to a particular directory where I can then run the second command from. Running a single command is pretty easy as this is all I have to do:
string path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + #"\Cisco Systems\VPN Client\";
Process process = new Process();
ProcessStartInfo processInfo = new ProcessStartInfo("cmd.exe", #"/c cd " + path );
process.StartInfo = processInfo;
process.Start();
However am not sure of the way to add the second argument so it runs after cmd runs the first command. Some research led me to this code snippet. Am unsure if this works since my aim is to start cisco vpn client from cmd and this seems not to start it. Here is the code:
string path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + #"\Cisco Systems\VPN Client\";
Process process = new Process();
ProcessStartInfo processInfo = new ProcessStartInfo("cmd.exe", #"/c cd " + path + "-t vpnclient connect user validuser pwd validpassword nocertpwd validconnectionentry ");
process.StartInfo = processInfo;
process.Start();
I once started the vpn client from cmd with the credentials just to make sure they were valid and it worked but I cant pull it off via C# programmatically.
Regards.
There three things you can do to achieve what you want. The easiest is to set the working directory of the process through ProcessStartInfo. This way you will only have to execute the command to start the VPN client.
The second option is redirecting the input and output of the process. (Also done through the ProcessStartInfo) This is something you want to do if you need to send more input to the process, or when you want to retrieve the output of the process you just started.
The third option is to combine the two commands with the & symbol. Using the & symbol makes cmd.exe execute the two commands sequentially (See here for an overview of the available symbols). Using this option will result in a command like this: /c cd path & vpnclient.
However because you just want to change the working directory of the process using the first option makes your code more readable. Because people reading your code do not need to know the & symbol in bash to understand what your code does. Changing the working directoy is done with the WorkingDirectory (MSDN) property of ProcessStartInfo (MSDN). See the following code:
var processInfo = new ProcessStartInfo("cmd.exe", #"/c vpnclient connect user validuser pwd validpassword nocertpwd validconnectionentry ");
processInfo.UseShellExecute = false;
processInfo.WorkingDirectory = path;
You can use & to execute next command or && to execute following command only if the previous one succeeded.
Examples:
dir /b & cls
and
taskkill /f /im explorer.exe && start explorer
Am a Newbie in C# and I have 3 commands(command2, command3 and command4) I need to execute in the elevated command prompt and I will also like to view the execution process as it happens. Currently, the problem is that the code below just opens the elevated command prompt and without executing the commands. I also seek better interpretations of the lines if wrong.
My code and Interpretation/Understanding of each line based on reviews of similar cases: ConsoleApp1
class Program
{
static void Main(string[] args)
{
string command2 = #"netsh wlan";
string command3 = #" set hostednetwork mode=true ssid=egghead key=beanhead keyusage=persistent";
string command4 = #" start hostednetwork";
string maincomm = command2.Replace(#"\", #"\\") + " " + command3.Replace(#"\", #"\\") ; //I merged commands 2 and 3
ProcessStartInfo newstartInfo = new ProcessStartInfo();
newstartInfo.FileName = "cmd"; //Intend to open cmd. without this the newProcess hits an error saying - Cannot run process without a filename.
newstartInfo.Verb = "runas"; //Opens cmd in elevated mode
newstartInfo.Arguments = maincomm; //I intend to pass in the merged commands.
newstartInfo.UseShellExecute = true; //
newstartInfo.CreateNoWindow = true; // I intend to see the cmd window
Process newProcess = new Process(); //
newProcess.StartInfo = newstartInfo; //Assigns my newstartInfo to the process object that will execute
newProcess.Start(); // Begin process and Execute newstartInfo
newProcess.StartInfo.Arguments = command4; //I intend to overwrite the initial command argument hereby passing the another command to execute.
newProcess.WaitForExit(); //
}
}
This is what I did to overcome the challenge and It gave me exactly what I wanted. I modified my code to use the System.IO to write directly to the elevated command prompt.
ProcessStartInfo newstartInfo = new ProcessStartInfo();
newstartInfo.FileName = "cmd";
newstartInfo.Verb = "runas";
newstartInfo.RedirectStandardInput = true;
newstartInfo.UseShellExecute = false; //The Process object must have the UseShellExecute property set to false in order to redirect IO streams.
Process newProcess = new Process();
newProcess.StartInfo = newstartInfo;
newProcess.Start();
StreamWriter write = newProcess.StandardInput ; //Using the Streamwriter to write to the elevated command prompt.
write.WriteLine(maincomm); //First command executes in elevated command prompt
write.WriteLine(command4); //Second command executes and Everything works fine
newProcess.WaitForExit();
Referrence: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardinput(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo(v=vs.110).aspx
I think an understanding of some properties of the ProcessStartInfo might clear things.
The verb - Gets or sets the verb to use when opening the application or document specified by the FileName property.,
+The UseShellExecute - Gets or sets a value indicating whether to use the operating system shell to start the process.
+The FileName - Gets or sets the application or document to start MSDN Docs
When you use the operating system shell to start processes, you can start any document (which is any registered file type associated with an executable that has a default open action) and perform operations on the file, such as printing, by using the Process object. When UseShellExecute is false, you can start only executables by using the Process object Documentation from MSDN.
In my case, cmd is an executable. the verb property is some thing that answers the question "How should my I run my FileName(for executables e.g cmd or any application)?" for which I answered - "runas" i.e run as administrator. When the FileName is a document (e.g `someFile.txt), the verb answers the question "What should I do with the file for which answer(verb) could be -"Edit","print" etc. also?"
use true if the shell should be used when starting the process; false if the process should be created directly from the executable file. The default is true MSDN Docs - UserShellInfo.
Another thing worth noting is knowing what you are trying to achieve. In my case, I want to be able to run commands via an executable(cmd prompt) with the same process - i.e starting the cmd as a process I can keep track of.
i have searched and read about issues with psexec.exe from sysinternals not working properly with c# and stdout. i am now trying to figure out how to just call a batch file that has the following instead of using System.Diagnostics.Process to call psexec:
test.bat contains the following line:
psexec.exe \\hostname -u user -p password ipconfig /all >c:\test.txt
test.txt will be saved on the host where i am running my c sharp app and executing psexec.
when i execute the following:
System.Diagnostics.Process psexec_run = new System.Diagnostics.Process();
psexec_run.StartInfo.FileName = "cmd.exe";
psexec_run.StartInfo.Arguments = #"/c """ + cur_dir + #"\test\test.bat""";
psexec_run.StartInfo.UseShellExecute = false;
psexec_run.Start();
psexec_run.WaitForExit();
i see the cmd window pop up and it runs something but not sure what and goes away.
if i execute the following:
System.Diagnostics.Process psexec_run = new System.Diagnostics.Process();
psexec_run.StartInfo.FileName = cur_dir + "\\test\\psexec.exe";
psexec_run.StartInfo.Arguments = #"\\hostname -u user -p password ipconfig /all";
psexec_run.StartInfo.UseShellExecute = false;
psexec_run.Start();
psexec_run.WaitForExit();
then i see the command window open and it runs psexec which takes quite a few secs and i quickly see my output i need, but i have no way of capturing the output or writing it to a file.
i guess my issue now is since psexec will not work with stdout how can i capture the output from the psexec command to write it to a file???
see the following link for the issues with psexec, the last reply on this url mentioned a way to write the process output to a file without using stdout, i'm newbie to c# i can't figure out how to write process output without use stdout :(
http://forum.sysinternals.com/psexec-fails-to-capture-stdout-when-launched-in-c_topic19333.html
based on response below i tried the following:
ProcessStartInfo psi = new ProcessStartInfo(cur_dir + "\\test\\psexec.exe", #"\\hostname -u user -p password ipconfig /all");
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
StreamReader myStreamReader = p.StandardOutput;
// Read the standard output of the spawned process.
string sOutput = myStreamReader.ReadToEnd();
i did ReadToEnd so i would make sure it got all the output, it DID NOT!! for some reason it only go the first line of ipconfig output that was it. Also the cmd window it opened up never closed for some reason. even with CreateNoWindow=true the code just hangs. so again something is wrong with psexec and stdout i think?? as i can run this code just fine using ipconfig /all command on the local host and not use psexec...
again i am looking to avoid stdout and somehow find a way to get the output from this command or unless there is something else i'm over looking? also, not to make more work for anyone, but if you d/l psexec.exe from sysinternals and test it with a command on a remote host you will see. i have spent 2 days on this one :( trying to figure out how to use psexec or find some other quick method to execute remote command on a host and get ouptput.
UPDATE:
i gave up on psexec in c# code, i saw many posts about psexec eating the output, having a child window process ,etcc
until my head hurt :) so i am trying to run a batch file and output to a file and it's not making sense...
i have a batch file test.bat with the following
psexec.exe \\\hostname -u name -p password ipconfig /all >c:\test.txt
when i run the following code:
ProcessStartInfo psi = new ProcessStartInfo(cur_dir + #"\test\test.bat");
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
p.WaitForExit();
the cmd windows comes and goes really quickly and the test.txt file is created but is 0 bytes no info in it.
so if i run the batch file in a windows cmd line with the psexec command it works perfectly fine!!???
so then to verify psexec was the issue i changed the batch file to:
ipconfig /all >c:\test.txt
i execute my code above and it works fine creates the output in the test.txt file..???!!!!
why is not working with psexec am i missing something? if it's psexec, does anyone have
any recommendations for how i can execute a command on a remote windows host and get me the
output???
I have an answer to this problem that has worked for me.
Hopefully someone else will find it useful.
I have literally just spent the last two hours tearing my hair out with this. The psexec tool runs completely fine using a normal command prompt but when attempting to redirect the streams it truncates the output and you only get half output back.
In the end how I fixed my issue was a little bit of a hack. I piped the output of the command to a text file and read it back in to return it from the function.
I also has to set UseShellExecute to true. Without this it still wouldn't work. This had the unfortunate side effect of showing the console window. To get around that I set the window style to be hidden and hey presto it works!!!
Heres my code:
string ExecutePSExec(string command)
{
string result = "";
try
{
string location = AppDomain.CurrentDomain.BaseDirectory;
// append output to file at the end of this string:
string cmdWithFileOutput = string.Format("{0} >{1}temp.log 2>&1", command,location );
// The flag /c tells "cmd" to execute what follows and exit
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + cmdWithFileOutput);
procStartInfo.UseShellExecute = true; // have to set shell execute to true to
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden; // as a window will be created set the window style to be hiddem
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
proc.WaitForExit();
// now read file back.
string filePath = string.Format("{0}temp.log", location);
result = System.IO.File.ReadAllText(filePath);
}
catch (Exception objException)
{
// Log the exception
}
return result;
}
and its usage:
string command = #"psexec.exe -l -u domain\username -p password /accepteula \\192.168.1.3 netstat -a -n";
ExecutePSExec(command);
I had exactly same problem. i was getting "Windows ip config. " as first line when i run with psexec. i tried with paexec it worked well. I used Marius's code.
Note: if you dont use first cmd / c in arguments command runs only on local computer even if you define target as \\remoteserver
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe");
psi.Arguments = #"cmd/c C:\paexec.exe \\\192.168.2.5 -s -u test.local\administrator -p Password1 cmd /c ipconfig";
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
System.IO.StreamReader myStreamReader1 = p.StandardOutput;
p.WaitForExit();
string sOutput = myStreamReader1.ReadToEnd();
Are you sure your sourcecode is correct? that link is quite a bit old.. maybe its fixed!
Heres an example how to redirect the standard-output and put whole output in a string via streamreader:
ProcessStartInfo psi = new ProcessStartInfo("tftp.exe");
// preferences for tftp process
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
StreamReader myStreamReader = p.StandardOutput;
p.WaitForExit();
// Read the standard output of the spawned process.
string sOutput = myStreamReader.ReadToEnd();
i found a solution. apparently psexec is NOT going to work in c sharp. so i came up with some wmi code to connect to a remote host and it's working PERFECTLY!!! :)
i used microsoft's WMICodeCreator.exe to create wmi code for C# for the process method on a remote host, wow that tool is amazing because wmi code is little confusing to me.
psexec's output goes to StandardError and not StandardOutput. I don't know why it is that way. Following code snippet access it.
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.RedirectStandardError = true;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
errors = process.StandardError.ReadToEnd();
process.Close();
We have a few commands(batch files/executables) on our network path which we have to call to initialize our 'development environment' for that command window. It sets some environmental variables, adds stuff to the Path etc. (Then only whatever working commands we type will be recognized & I don't know what goes inside those initializing commands)
Now my problem is, I want to call a series of those 'working commands' using a C# program, and certainly, they will work only if the initial setup is done. How can I do that? Currently, I'm creating a batch file by scratch from the program like this for example:
file.Writeline("InitializationStep1.bat")
file.Writeline("InitializeStep2.exe")
file.Writeline("InitializeStep3.exe")
Then the actual commands
file.Writeline("Dowork -arguments -flags -blah -blah")
file.Writeline("DoMoreWork -arguments -flags -blah -blah")
Then finally close the file writer, and run this batch file.
Now if I directly execute this using Process.<strike>Run</strike>Start("cmd.exe","Dowork -arguments"); it won't run.
How can I achieve this in a cleaner way, so that I have to run the initialization commands only once? (I could run cmd.exe each time with all three initializers, but they take a lot of time so I want to do it only once)
As #Hakeem has pointed out, System.Diagnostic.Process does not have a static Run method. I think you are referring to the method Start.
Once you have completed building the batch file, then simply execute it using the following code,
Process p = new Process();
p.StartInfo.FileName = batchFilePath;
p.StartInfo.Arguments = #"-a arg1 -b arg2";
p.Start();
Note that the # symbol is required to be prefixed to the argument string so that escape sequence characters like \ are treated as literals.
Alternative code
Process.Start(batchFilePath, #"-a arg1 -b arg2");
or
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = batchFilePath;
processStartInfo.Arguments = #"-a arg1 -b arg2";
Process.Start(processStartInfo);
More information
Process.Start method
Example of multi command batch file
dir /O
pause
dir
pause
Save this file as .bat and then execute using the Start method. In this case you can specify the argument with the command in the batch file itself (in the above example, the /O option is specified for the dir command.
I suppose you already have done the batch file creation part, now just append the arguments to the commands in the batch file.
Redirecting Input to a process
Since you want to send multiple commands to the same cmd process, you can redirect the standard input of the process to the take the input from your program rather than the keyboard.
Code is inspired from a similar question at: Execute multiple command lines with the same process using C#
private string ProcessRunner()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe");
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.UseShellExecute = false;
Process process = Process.Start(processStartInfo);
if (process != null)
{
process.StandardInput.WriteLine("dir");
process.StandardInput.WriteLine("mkdir testDir");
process.StandardInput.WriteLine("echo hello");
//process.StandardInput.WriteLine("yourCommand.exe arg1 arg2");
process.StandardInput.Close(); // line added to stop process from hanging on ReadToEnd()
string outputString = process.StandardOutput.ReadToEnd();
return outputString;
}
return string.Empty;
}
The method returns the output of the command execution. In a similar fashion, you could also redirect and read the StandardOuput stream of the process.
The Process.Run method that you mentioned, is that from the Process class in System.Diagnostics namespace? AFAIK, the Process type doesn't have either a static or instance method named Run. If you haven't already I'd try with the Start method on Process, either instance or static
Before you say its a duplicate question, please let me explain (as I've read all similar threads).
My application has both of these settings:
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
and is also has WindowsApplication as the output type.
The black window STILL comes up when I call a command line command. Is there anything else I can do to hide the window? It doesn't happen for all commands, XCOPY is a situation where it the black window does flash up. This only happens though when the destination I'm XCOPYing too already contains the file and it's prompting me if I want to replace it. Even if I pass in /Y it will still flash briefly.
I'm open to using vbscript if that will help, but any other ideas?
The client will call my executable and then pass in a command line command ie:
C:\MyProgram.exe start XCOPY c:\Test.txt c:\ProgramFiles\
Here's the full code of the application:
class Program
{
static void Main(string[] args)
{
string command = GetCommandLineArugments(args);
// /c tells cmd that we want it to execute the command that follows and then exit.
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd.exe", "/c " + command);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo = procStartInfo;
process.Start();
}
private static string GetCommandLineArugments(string[] args)
{
string retVal = string.Empty;
foreach (string arg in args)
retVal += " " + arg;
return retVal;
}
}
The problem is that you're using cmd.exe. Only its console window will be hidden, not the console window for the process you ask it to start. There's little point in using cmd.exe, unless you are trying to execute some of the commands it implements itself. Like COPY.
You can still suppress the window if you need cmd.exe, you'll have to use the /B option for Start. Type start /? at the command prompt to see options. Not that it helps, you can't use START COPY.
There's a specific quirk in xcopy.exe that might throw you off as well. It does not execute if you don't also redirect the input. It just fails to run without diagnostic.
i see that you are calling cmd and then passing the command as parameters. Instead call the command directly
e.g.
System.Diagnostics.ProcessStartInfo procStartInfo = new System.DiagnosticsProcessStartInfo("xcopy", "<sourcedir> <destdir> <other parameters>");
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
You can try adding
process.StartInfo.UseShellExecute = false;
to your process
If you are calling cmd.exe in your C# code and passing the commands to it via standard input.WriteLine and you don't want your CMD window to pop up every time you run your code, you can simply write this command:
test.StartInfo.FileName = "cmd.exe";
test.StartInfo.CreateNoWindow = true;
By setting create no window to false, we are running the command sent to the CMD in the background and the output is not being displayed to the user. By setting it to false, the CMD window pops up.
I had a similar task - It is possible to hide the window after creation via an API call.
(In your case you maybe need a helper thread.)
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
If you know the handle of the new Window you can call
ShowWindow(hWnd, 0);
0 hides the window, 1 shows the window
To get the handle of the Window take a look at:
pinvoke.net enumwindows(user32)