I am starting a process using Process.Start(ProcessStartInfo). It currently brings up a console window and the output of the process is displayed there until the process completes, in which case the console window closes automatically.
The process outputs a lot of text, so I do not just want to redirect this output to a string, like examples I have found so far.
How can I get the text of the console output to go into a text log file?
ProcessStartInfo myPSI = new ProcessStartInfo();
myPSI.FileName = myFileName;
myPSI.Arguments = myArgs;
myPSI.CreateNoWindow = false;
myPSI.UseShellExecute = false;
myPSI.WindowStyle = ProcessWindowStyle.Hidden;
try
{
using (Process exeProcess = Process.Start(myPSI))
{
exeProcess.WaitForExit();
}
}
catch
{
}
You need to use output redirection. See here: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
You can redirect the output to whatever you want... for example a stream... you can even process the output in a separate thread if you want to - for source code and details see http://www.codeproject.com/KB/threads/ReadProcessStdoutStderr.aspx
Related
I am currently writing an app to start the screensaver on Windows 10 and show the screen instead of a black background. So that Bubbles and relatives can act like in older OS version.
Here is my full code:
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
public class DrawOverMyScreen {
public static void Main(string[] CommandLine) {
switch (CommandLine[0]) {
case "/c":
DialogResult Answer = MessageBox.Show("What do you want to do?\n\n - Press \"Yes\" to configure the screensaver\n - Press \"No\" to change the screensaver\n - Press \"Cancel\" to do nothing", "DrawOverMyScreen Configuration", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3);
switch (Answer) {
case DialogResult.Yes:
Screensaver("/c");
break;
case DialogResult.No:
throw new NotImplementedException();
break;
default:
break;
}
break;
default:
Screensaver("/s");
break;
}
}
public static void Screensaver(string CommandLine) {
RegistryKey Settings = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\DrawOverMyScreen");
if (Settings != null) {
string ScreensaverLocation = Settings.GetValue("Screensaver", string.Empty).ToString();
if (!string.IsNullOrEmpty(ScreensaverLocation) && File.Exists(ScreensaverLocation)) {
Process Screensaver = Process.Start(new ProcessStartInfo(ScreensaverLocation, CommandLine));
Screensaver.WaitForExit();
}
}
}
}
Notice the Screensaver method. It uses Process.Start(new ProcessStartInfo(ScreensaverLocation, CommandLine)); to start the screensaver. But whenever I do Screensaver("/c"); to run the screensaver's config utility, I only get the normal screensaver view (The one you get when idle after a certain time). Using the run prompt like this: C:\Windows\SysWOW64\SCREEN~1.SCR /c also gives the same result, but command line prompt actually opens the config utility.
Why won't it work, and how can I make it so it works?
Just from what you have provided, I can't tell you why it won't work. I do not have a screensaver to test that with (that I know of). But I'm able to do all four of these with Notepad opening a text file:
Separate ProcessStartInfo
ProcessStartInfo procInfo = new ProcessStartInfo("notepad.exe", "c:\\test.txt");
Process proc = Process.Start(procInfo);
proc.WaitForExit();
Separate ProcessStartInfo with Properties
ProcessStartInfo procInfo = new ProcessStartInfo();
procInfo.Arguments = "c:\\test.txt";
procInfo.FileName = "notepad.exe";
Process proc = Process.Start(procInfo);
proc.WaitForExit();
Inline ProcessStartInfo
Process proc = Process.Start(new ProcessStartInfo("notepad.exe", "c:\\test.txt"));
proc.WaitForExit();
No PSI, Just Process
Process proc = Process.Start("notepad.exe", "c:\\test.txt");
proc.WaitForExit();
You may want to go with the first one so that you can breakpoint on the "Process proc..." line and examine the properties of procInfo. The Arguments property should show the 2nd value (in my case, c:\\test.txt), and the FileName property should be the path to what you are executing (mine is notepad.exe).
EDIT: I added the separate one with properties so you can really see explicit setting.
CONFIGURE A SCREENSAVER
I have worked out an example using the 3D Text screensaver:
string scrPath = #"C:\Windows\System32\ssText3d.scr";
ProcessStartInfo procInfo = new ProcessStartInfo();
procInfo.FileName = scrPath;
procInfo.Verb = "config";
procInfo.UseShellExecute = false;
Process proc = Process.Start(procInfo);
proc.WaitForExit();
I didn't use the Arguments. Instead, I used the Verb. This requires UseShellExecute to be set to false. I got the expected configuration dialog instead of the screensaver running.
More About Verbs
This is where the verbs are for screen savers.
You can also define custom verbs: Register an Application to Handle Arbitrary File Types
I am making an application with visual c# in the front end and Python executing the scripts in the back end. I wanted to pass one value from Visual c# form as an argument to a Python script. The Python script should process the value and should return the processed value to Visual c#. The value should be displayed in the visual c# form.
For the first thing, I wrote a dirty code where python script is executed when the form is loaded and store the value in a text file. Python script will use the value to calculate. But I am not able to return the value to c#.
The code which I wrote for the first logic is:
Process process = new Process();
process.StartInfo.CreateNoWindow = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
process.StartInfo.FileName = #"C:\c#\Work\RulesValidator\RulesValidator\Asset_Id.py";
try
{
process.Start();
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}
asset_id.Text = System.IO.File.ReadAllText(#"C:\c#\Work\RulesValidator\RulesValidator\Asset_Id.txt");
The problem is that you start the child Python script and immediately try to read the sequential file without waiting for the end of the child.
You should try :
Process process = new Process();
process.StartInfo.CreateNoWindow = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
process.StartInfo.FileName = #"C:\c#\Work\RulesValidator\RulesValidator\Asset_Id.py";
try
{
process.Start();
process.WaitForExit(); // Now child should have done its job and closed file
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}
asset_id.Text = System.IO.File.ReadAllText(#"C:\c#\Work\RulesValidator\RulesValidator\Asset_Id.txt");
But you should investigate the way proposed by e-nouri. The only attention point is : To use StandardOutput, you must set ProcessStartInfo.UseShellExecute to false. On the Python part, you would only write the result to stdout, every other output going to stderr.
It could be (adapted from this page on MSDN) :
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = #"C:\c#\Path\To\python.exe";
p.StartInfo.Arguments = #"C:\c#\Work\RulesValidator\RulesValidator\Asset_Id.py";
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();
BEWARE : I've no C# developpement environment and above is untested even if I honestly think it should not be far from what you want
You can spawn a process and capture its stdout, here is an SO question for this: link.
In your python program/script you can just use prints that will print to the stdout, then capture it from your C# code. Depends the complexity of that data you want to exchange, you can use plain text, json, etc.
Cheers,
On the server, I'm attempting to open the command prompt and call an executable which converts a file to PDF. For this I am using the PDFCreator open source program.
In C# I am calling with this code:
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe");
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.UseShellExecute = false;
Process process = Process.Start(processStartInfo);
process.StandardInput.WriteLine(#"cd c:\program files (x86)\pdfcreator");
process.StandardInput.WriteLine(#"PDFCreator.exe /PF""c:\dwf\dwf.dwf""");
It runs with no error, yet yields no result. What this PDFCreator.exe does is call another program, Autodesk Design Review which opens, uses the PDF driver to print to PDF, and saves the file. The command you see works fine being running standalone by me.
From scouring other threads it seems security could be my issue. So I have gone to the PDFCreator and Design Review folders/executables and granted full access to NETWORK, NETWORK SERVICE, IIS_WPG, IIS_IUSRS, and ASP.NET Machine account (realize this is probably a security thread but will disable once i figure out source of the issue). This has not helped.
It should be noted than i can change directory using the first command above, and then create a "test123" folder in both PDFCreator and Design Review folders. Seems I am getting close here, any ideas?
SteveCalPoly and Val Akkapeddi comments are very interesting.
Anyway, I use the following methods to run executable with command prompt
/// <summary>
/// Executes a shell command synchronously.
/// </summary>
/// <param name="command">string command</param>
/// <returns>string, as output of the command.</returns>
public void ExecuteCommandSync(object command)
{
try
{
// create the ProcessStartInfo using "cmd" as the program to be run,
// and "/c " as the parameters.
// Incidentally, /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", "/c " + command);
// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
// Display the command output.
Console.WriteLine(result);
}
catch (Exception objException)
{
// Log the exception
}
}
/// <summary>
/// Execute the command Asynchronously.
/// </summary>
/// <param name="command">string command.</param>
public void ExecuteCommandAsync(string command)
{
try
{
//Asynchronously start the Thread to process the Execute command request.
Thread objThread = new Thread(new ParameterizedThreadStart(ExecuteCommandSync));
//Make the thread as background thread.
objThread.IsBackground = true;
//Set the Priority of the thread.
objThread.Priority = ThreadPriority.AboveNormal;
//Start the thread.
objThread.Start(command);
}
catch (ThreadStartException objException)
{
// Log the exception
}
catch (ThreadAbortException objException)
{
// Log the exception
}
catch (Exception objException)
{
// Log the exception
}
}
The System.Diagnostics.Process class has a method called WaitForExit() which will wait for its launched process to exit before continuing and then will return its return code.
Try creating a batch file with your commands and then running the batch file via the Process class. What happens if you use Process.WaitForExit(); after you call Process.Start(processInfo); ? Is there a return code from process.WaitForExit() at all?
Perhaps the errors are going to the StandardError stream and so you never see them?
Also, why not call PDFCreator.exe directly instead of via cmd.exe?
Try something like this
ProcessStartInfo processStartInfo = new ProcessStartInfo(#"c:\program files (x86)\pdfcreator");
processStartInfo.Arguments = #"/PF""c:\dwf\dwf.dwf"""
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.UseShellExecute = false;
Process process = Process.Start(processStartInfo);
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
string errors = p.StandardError.ReadToEnd();
Found out the issue over at serverfault. The app I was calling needs to open another app on the desktop and then print to PDF. IIS cannot open programs on the desktop unless set in the services --> service name --> log on tab.
Unfortunately, the app I am calling isn't in the services panel so I'm currently stuck again, but at least I know it's not a C# problem.
Is there a way to execute the following command through C#?
.\binary.exe < input > output
I am trying to use System.Diagnostics.Process but I was wondering if there is a direct exec style command in C#. Any suggestions?
Not directly, but you can redirect output from a console stream (as you may have figured out, considering you're trying to use the Process class), as noted here on MSDN: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
There is also an example here on SO: Redirect console output to textbox in separate program
Wrap that into your own class and it will basically become an "exec" style command.
Basically you need to redirect the standard input and output to your program and write them to the files you want
ProcessStartInfo info = new ProcessStartInfo("binary.exe");
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
Process p = Process.Start(info);
string Input;
// Read input file into Input here
StreamWriter w = new StreamWriter(p.StandardInput);
w.Write(Input);
w.Dispose();
StreamReader r = new StreamReader(p.StandardOutput);
string Output = r.ReadToEnd();
r.Dispose();
// Write Output to the output file
p.WaitForExit();
I am new to C# so please sorry if i make no sense in my question. In my application which is C# DLL need to open command prompt, give a plink command for Linux system to get a system related string and set that string as environment variable. I am able to do this when i create C# console application, using plink command to get the string on command prompt and use to set it environment variable using process class in C# to open plink as separate console process. But, in C# DLL i have to open cmd.exe 1st and then give this command which i don't know how can i achieve? I tried through opening cmd.exe as process and then trying to redirect input and output to process and give command and get string reply, but no luck. Please let me know any other way to solve this.
Thanks for answers,
Ashutosh
Thanks for your quick replys. It was my mistake in writing code sequence. Now few changes and the code is working like charm. Here is code,
string strOutput;
//Starting Information for process like its path, use system shell i.e. control process by system etc.
ProcessStartInfo psi = new ProcessStartInfo(#"C:\WINDOWS\system32\cmd.exe");
// its states that system shell will not be used to control the process instead program will handle the process
psi.UseShellExecute = false;
psi.ErrorDialog = false;
// Do not show command prompt window separately
psi.CreateNoWindow = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
//redirect all standard inout to program
psi.RedirectStandardError = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
//create the process with above infor and start it
Process plinkProcess = new Process();
plinkProcess.StartInfo = psi;
plinkProcess.Start();
//link the streams to standard inout of process
StreamWriter inputWriter = plinkProcess.StandardInput;
StreamReader outputReader = plinkProcess.StandardOutput;
StreamReader errorReader = plinkProcess.StandardError;
//send command to cmd prompt and wait for command to execute with thread sleep
inputWriter.WriteLine("C:\\PLINK -ssh root#susehost -pw opensuselinux echo $SHELL\r\n");
Thread.Sleep(2000);
// flush the input stream before sending exit command to end process for any unwanted characters
inputWriter.Flush();
inputWriter.WriteLine("exit\r\n");
// read till end the stream into string
strOutput = outputReader.ReadToEnd();
//remove the part of string which is not needed
int val = strOutput.IndexOf("-type\r\n");
strOutput = strOutput.Substring(val + 7);
val = strOutput.IndexOf("\r\n");
strOutput = strOutput.Substring(0, val);
MessageBox.Show(strOutput);
I explained the code so far..., thanks a lot