A Program I have to work with opens a CMD-window with a message after processing some data.
The message is something like "finished processing" and I don't have access to the code behind the other Programm.
The goals I want to achieve is:
Get the message from the CMD-window and write it to a text box (or similar) in my own programm.
Close the CMD-window
I managed to attach the process to my programm using Process[] localByName = Process.GetProcessesByName("cmd"); but I can't manage to get the output.
Thank you for your help.
Edit:
To elaborate a bit further about the circumstances of the problem:
I start the other programm with a command line command. I pass some parameters and after a bit, the other programm opens a cmd window with the message.
If i open the other programm normally, then the message is displayed within the application.
None of the parameters give me the option to manipulate the output and i am not able to pipeline the output to another file.
As for why i have to do this: I was given the task to add quality of life improvements to the other programm and using the command line is the easiest way to access the functionality of the other programm. The other solution available would be to reverse engeneer the processing of the data.
The Process class has a StandardOutput property of type StreamReader that you can use to read the output of the process. Using your example:
Process[] localByName = Process.GetProcessesByName("cmd");
if (localByName.Length > 0)
{
var cmdProcess = localByName[0];
var reader = cmdProcess.StandardOutput;
var output = reader.ReadToEnd();
Console.WriteLine($"The output from the cmd process is: {output}");
}
Once you are done with your message processing, you can use the CloseMainWindow() method of the Process class to close the cmd window:
cmdProcess.CloseMainWindow();
This answer is assuming that you want to capture the output of cmd after it has been attached to your process.
Related
I often find myself using different commands on pc's to check stuff in the system and I wanted to basically create a small standalone exe that will execute all my common commands and give me the output of each of them without me entering them manually...
I want to note that I know this isn't the first question on google on the subject of executing a cmd command with c# but non fit my command requirements, for example many of them execute commands such as copy or move or make and non of them have a complex output, I want to execute for example the "sfc /scannow" command, which outputs a progress bar which measures the progress of course and a final output, the issue with that is I have tried many ways to attempt that but all failed, a shell execution works very well with opening another cmd windows and even requiring elevation but as soon as the progress bar ends and it displays the final output it crashes (and thus I can't see the final output), with shell execution off (executing via the main window) it either doesn't show the progress bar at all and just shows the final output, or shows nothing, or it does show the progress bar, but with each increment it's a new line which obviously doesn't look right...
this is my code:
public static void ExecuteCommand(string command) {
Process prc = new Process();
ProcessStartInfo info = new ProcessStartInfo
{
Verb = "runas",
FileName = "cmd.exe",
UseShellExecute = true,
Arguments = "/c" + command
};
prc.StartInfo = info;
prc.Start();
prc.WaitForExit();
prc.Close();
}
if anyone can think of a fix please do tell me, I have been stuck on this for about 3 days...
Turns out the culprit was "/c", if I change it to "/K" the windows stays open until I close it manually like I wanted.
I already know that I can open and close an exist text file by notepad in c#
Like this:
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
// open by notepad
Process.Start("notepad.exe", #"myfile.txt");
// automatically save this file by notepad
// kill notepad process
Process[] proc = Process.GetProcessesByName("notepad");
proc[0].Kill();
}
}
But I need to automatically save this file by notepad before I close this file. I tried to send keystrokes ctrl+s but in vain.
Is there .net code for this? Thanks.
You could do something like this i guess
SendKeys.SendWait Method (String)
Sends the given keys to the active application, and then waits for the
messages to be processed.
Remarks
Use SendWait to send keystrokes or combinations of keystrokes to the
active application and wait for the keystroke messages to be
processed. You can use this method to send keystrokes to an
application and wait for any processes that are started by the
keystrokes to be completed. This can be important if the other
application must finish before your application can continue.
Imports
[DllImport("User32.dll")]
public static extern Int32 SetForegroundWindow(int hWnd);
Code
var process = Process.Start("notepad.exe", #"myfile.txt"));
process.WaitForInputIdle();
var handle = process.MainWindowHandle;
SetForegroundWindow(handle);
// if the window is still in the foreground
SendKeys.SendWait("^(s)"); // Ctrl+S
If your need is to modify file, better way to do that is open, modify and save text file contents with operations which are currently present in System.IO.File-class. When file is opened in code, you can i.e change it's encodings with classes found in System.Text-namespace (System.Text.Encoding).
You can start notepad.exe process, but after that there is not very much to do.
I'm attempting to run a series of commands programmatically, read the error codes, and detect if these codes indicate success or failure so I can respond accordingly.
Currently my commands are using psexec which then launches robocopy. I've noted that while most commands return an error code of 0 if the program is successful, robocopy is odd in that it returns values in the range of 0-8 even if the operation is successful, so I am adding some extra logic in my error detection to note when robocopy returns an error code which otherwise suggests a failure.
The problem is that in this same set of commands I'm using PSExec to launch various other executables and batch files, so I need an error detection solution that allows me to know when robocopy is the one returning these error codes or if it's PSExec because an error code of 5 in robocopy is fine usually whereas an error code of 5 in PSExec says that access is denied.
So my question is, how do I know which program has returned the error code? I'm using c# .NET 4.0, and I'm using the Process class to programmatically launch these programs. I set the program name as psexec, and the arguments include the robocopy or other programs. I then run, wait for the exit, and store the error code, then attempt to parse it.
What do you all suggest?
Here is a code snippet:
foreach (var command in commands)
{
// TODO: Add exception handling
string processName = command.Split(delimiters).ToList().ElementAt(0); // split up command into pieces, select first "token" as the process name
string commandArguments = command.Replace(processName + " ", ""); // remove the process name and following whitespace from the command itself, storing it in a new variable
Process commandProcess = new Process(); // declare a new process to be used
commandProcess.StartInfo.FileName = processName; // add file start info for filename to process
commandProcess.StartInfo.Arguments = commandArguments; // add file start info for arguments to process
commandProcess.StartInfo.UseShellExecute = false; // skip permissions request
commandProcess.Start(); // start process according to command's data
commandProcess.WaitForExit(); // wait for the process to exit before continuing
bool commandSuccessful = ParseCommandErrorCode(commandProcess, commandProcess.ExitCode); // grab error code
if (!commandSuccessful)
{
// ERROR! abort operation and inform the user of the last completed operation, and how many commands have not been run
} // end if
Console.WriteLine("Error code: {0}", commandProcess.ExitCode); // print error code
commandProcess.Close(); // close process
} // end foreach
Haven't you already answered your own question? You will have to match the error code to success or failure for each utility. commandProcess.StartInfo.FileName will tell you if the utility launched was robocopy or PSExe. When parsing the error code, map the value to success or failure depending on the name of the file.
I do backup of large database, so there is a command file .cmd. in that I have mny Start ROBOCOPY cource destination and arguments each process script line for a* b* c* c* etc.
example as below: in sample.cmd file(acts as btach file),
I have following lines of scripts in a sample.cmd file as:
Start ROBOCOY src dest a* b* c* /z /w:1 r:1
Start ROBOCOY src dest d* e* f* g* /z /w:1 r:1
Start ROBOCOY src dest h* K* P* Y* /z /w:1 r:1
Start ROBOCOY src dest xry* srp* /z /w:1 r:1
When I rum > Robocopy sample.cmd I starts with cmd.exe and another 4 multiple console windows robocoy.exe copying files simultaneously as per above commands, it waits for another file, as it has wait time, if file is being used by another process. Is is more faster as it do job simultaneously.
Now I am devloping GUI using C# windows to run the process instead going to command console and
start
main()
{
process.start( "path of sample.cmd" )
process.waitforexit()
label.text=" sucessful copy"
}
However, if it takes control of oneprocess, i.e cmd.exe and and there are 4 robocopy processes in taskmanager. when cmd.exe process completes, it returns the cursor to label.text "Sucesssfully completed". While there are robocopy processes still running. you can see the robocopy windows doing the copying process.
Here is the question: I want to take contrl of all the processes (cmd.exe and robocopy.exe) programatically in C#, so that when the label.text should display "sucessfully completed" only when all commands are successfully completed", if one fails, then there is no point in the GUI.
option 2 (similar to Biju has written above): is it better to remove robocoy command scripts from sample.cmd(batch file) file and write code to run the 4 robocopy lines in C#, but how to run the robocooy script line written .cmd file, as they have arguments as well. I code runs each robocopy process then each will return to the next line of code and if it fails, we can catch the error and display in the message box.
Hope this will help... However, Iam looking for more better way, if somebody can improve on the same. Thanks :)
I can run a command line process using process.start().
I can provide input using standard input. After that when the process demands user input again, how can my program know and pass the input to that exe?
There's an example here that sounds similar to what you need, using Process.StandardInput and a StreamWriter.
Process sortProcess;
sortProcess = new Process();
sortProcess.StartInfo.FileName = "Sort.exe";
// Set UseShellExecute to false for redirection.
sortProcess.StartInfo.UseShellExecute = false;
// Redirect the standard output of the sort command.
// This stream is read asynchronously using an event handler.
sortProcess.StartInfo.RedirectStandardOutput = true;
sortOutput = new StringBuilder("");
// Set our event handler to asynchronously read the sort output.
sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
// Redirect standard input as well. This stream
// is used synchronously.
sortProcess.StartInfo.RedirectStandardInput = true;
sortProcess.Start();
// Use a stream writer to synchronously write the sort input.
StreamWriter sortStreamWriter = sortProcess.StandardInput;
// Start the asynchronous read of the sort output stream.
sortProcess.BeginOutputReadLine();
Console.WriteLine("Ready to sort up to 50 lines of text");
String inputText;
int numInputLines = 0;
do
{
Console.WriteLine("Enter a text line (or press the Enter key to stop):");
inputText = Console.ReadLine();
if (!String.IsNullOrEmpty(inputText))
{
numInputLines ++;
sortStreamWriter.WriteLine(inputText);
}
}
hope that helps
If I understand correctly I think you should be able to do this by redirecting the Standard Input/Output using RedirectStandardOutput and RedirectStandardInput.
The WinSCP project has a C# sample for how to communicate with it using this way. You can find it here: SFTP file transfers in .NET (though this sample only collects the output without using it at all, but the technique should be the same.)
You want to look up IPC. As robert showed above, the Process class in .NET will help you. But specifically for your problem (how to know when to write data): You can't. Not generically.
If you know the required input (e.g. "yyy"), you can supply it in the STDIN of the created process. You don't have to wait for the program to request this information: It will just read from STDIN when it wants data.
If you need to process the programs output to decide what to write to STDIN, try reading the processes STDOUT. You might run into problems with flushing, though...
I want to run a console application (eg app.exe) from a windows form load event.
I'v tried System.Diagnostics.Process.Start(), But after it opens app.exe, it closes it immidiately.
Is there any way that I can run app.exe and leave it open?
If you are just wanting the console window to stay open, you could run it with something like this command:
System.Diagnostics.Process.Start( #"cmd.exe", #"/k c:\path\my.exe" );
Try doing this:
string cmdexePath = #"C:\Windows\System32\cmd.exe";
//notice the quotes around the below string...
string myApplication = "\"C:\\Windows\\System32\\ftp.exe\"";
//the /K keeps the CMD window open - even if your windows app closes
string cmdArguments = String.Format("/K {0}", myApplication);
ProcessStartInfo psi = new ProcessStartInfo(cmdexePath, cmdArguments);
Process p = new Process();
p.StartInfo = psi;
p.Start();
I think this will get you the behavior you are trying for. Assuming you weren't just trying to see the output in the command window. If you just want to see the output, you have several versions of that answer already. This is just how you can run your app and keep the console open.
Hope this helps. Good luck.
If app.exe does nothing, or finishes its work quickly (i.e. simply prints "Hello World" and returns), it will behave the way you just explained. If you want app.exe to stay open after its work is done, put some sort of completion message followed by Console.ReadKey(); in the console application.
If you can change the code of app.exe, just add Console.In.Read() to make it wait for a key press.
app.exe can end with Console.ReadLine() assuming it too is a C# application where you control the source code.
You have one of two problems, given your master/slave application setup:
Your master app is opening, displaying a form, that form runs the slave app and closes immediately, even though the slave app is still running.
Your master app is opening, displaying a form, that form runs the slave app which closes immediately.
For the first problem, you need to wait/block for the process to complete (i.e. Process.WaitForExit().
For the second problem, it sounds like the slave app has done what it needs to (or thrown an exception) and is closing immediately. Try running it with the same parameters from a command prompt and check the output.
If you have control over app.exe, you should be aware of how it functions so I will assume that you do not have control over it's inner workings. In that case, you can try passing a help flag which may or may not give you more info on how to call app.exe. Try something like this:
private startApp()
{
string command = " -h"; //common help flag for console apps
System.Diagnostics.Process pRun;
pRun = new System.Diagnostics.Process();
pRun.EnableRaisingEvents = true;
pRun.Exited += new EventHandler(pRun_Exited);
pRun.StartInfo.FileName = "app.exe";
pRun.StartInfo.Arguments = command;
pRun.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal
pRun.Start();
pRun.WaitForExit();
}
private void pRun_Exited(object sender, EventArgs e)
{
//Do Something Here
}
Create a new text file, name it app.bat and put this in there:
app.exe
pause
Now have your form point to that bat file.
In your console application, type:
Console.ReadLine(); - Use this piece of code to wait until you press enter
Console.ReadKey(); - Use this code to wait until you press a key