UPDATE
** STILL LOOKING FOR A CORRECT ANSWER **
I have the following code in my windows service and I want to run a batch file. I want the command prompt window up so I can see progress
here is my code but my batch file code doesnt work
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.IO;
namespace Watcher
{
public partial class Watcher : ServiceBase
{
public Watcher()
{
InitializeComponent();
FolderWatcher.Created += FolderWatcher_Created;
FolderWatcher.Deleted += FolderWatcher_Deleted;
FolderWatcher.Renamed += FolderWatcher_Renamed;
}
protected override void OnStart(string[] args)
{
// 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:\\myFile.bat";
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();
}
protected override void OnStop()
{
}
private void FolderWatcher_Created(object sender, System.IO.FileSystemEventArgs e)
{
TextWriter writer = new StreamWriter("C:\\folder\\FolderLog.txt", true);
writer.WriteLine(DateTime.Now + " A new folder/file with name " + e.Name + " has been created. ");
writer.Close();
}
private void FolderWatcher_Deleted(object sender, System.IO.FileSystemEventArgs e)
{
TextWriter writer = new StreamWriter("C:\\folder\\FolderLog.txt", true);
writer.WriteLine(DateTime.Now + " A new folder/file with name " + e.Name + " has been deleted. ");
writer.Close();
}
private void FolderWatcher_Renamed(object sender, System.IO.RenamedEventArgs e)
{
TextWriter writer = new StreamWriter("C:\\folder\\log.txt", true);
writer.WriteLine(DateTime.Now + " A new folder/file with name " + e.Name + " has been renamed. ");
writer.Close();
}
}
}
It does not execute the batch file. I am a newbie in .net and C# and I am not sure what to do from here.
thanks
How to run console application from Windows Service?
You will want to the set the p.StartInfo with FileName="cmd.exe" and Arguments="c:\\thebatfile.bat" i believe
The problem is that you have UseShellExecute as false, but you aren't passing the name of an executable.
When ShellExecute is being used its similar to double clicking on a file in explorer - it knows that .doc files need to be opened with Word, and that .bat files need to be opened with cmd.exe. When you have this disabled however it knows none of these things and you need to pass an executable in order for anything to be run successfully.
As you are setting RedirectStandardOutput to true you need to instead run the batch file via cmd.exe by setting FileName to cmd.exe and the arguments to /C "c:\myFile.bat":
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C \"c:\\myFile.bat\"";
It looks like it's running the batch script when the service is first run and then it quits (p.WaitForExit();) before the other functions get the ability to be called. Is that the intended behavior? That would explain why you can see it do the folder operations and not see the script being run.
Try this code to bring up the console window. It should give you an idea of when the batch script is running.
protected override void OnStart(string[] args)
{
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
/*
This is commented out so we can see what the script is doing
inside the cmd console.
*/
//p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "C:\\myFile.bat";
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.
/*
Since we aren't redirecting the output, we have to comment out
this line or we get an error
*/
//string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
i am doubting your service or the bat file. modify the source code to open a notepad! check if notepad shows up!! if yes then we can investigate further!
What is your batch file doing? Assume you've confirmed that this IS running OK.
Windows services run under a desktopless user account. To see the cmd window you must impersonate the current logged user and start the cmd window on this user's desktop. See this:
Windows Impersonation from C#
Related
This sort of question has been asked before in varying degrees, but I feel it has not been answered in a concise way and so I ask it again.
I want to run a script in Python. Let's say it's this:
if __name__ == '__main__':
with open(sys.argv[1], 'r') as f:
s = f.read()
print s
Which gets a file location, reads it, then prints its contents. Not so complicated.
Okay, so how do I run this in C#?
This is what I have now:
private void run_cmd(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = cmd;
start.Arguments = args;
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
When I pass the code.py location as cmd and the filename location as args it doesn't work. I was told I should pass python.exe as the cmd, and then code.py filename as the args.
I have been looking for a while now and can only find people suggesting to use IronPython or such. But there must be a way to call a Python script from C#.
Some clarification:
I need to run it from C#, I need to capture the output, and I can't use IronPython or anything else. Whatever hack you have will be fine.
P.S.: The actual Python code I'm running is much more complex than this, and it returns output which I need in C#, and the C# code will be constantly calling the Python code.
Pretend this is my code:
private void get_vals()
{
for (int i = 0; i < 100; i++)
{
run_cmd("code.py", i);
}
}
The reason it isn't working is because you have UseShellExecute = false.
If you don't use the shell, you will have to supply the complete path to the python executable as FileName, and build the Arguments string to supply both your script and the file you want to read.
Also note, that you can't RedirectStandardOutput unless UseShellExecute = false.
I'm not quite sure how the argument string should be formatted for python, but you will need something like this:
private void run_cmd(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "my/full/path/to/python.exe";
start.Arguments = string.Format("{0} {1}", cmd, args);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using(Process process = Process.Start(start))
{
using(StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
If you're willing to use IronPython, you can execute scripts directly in C#:
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
private static void doPython()
{
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile(#"test.py");
}
Get IronPython here.
Execute Python script from C
Create a C# project and write the following code.
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
run_cmd();
}
private void run_cmd()
{
string fileName = #"C:\sample_script.py";
Process p = new Process();
p.StartInfo = new ProcessStartInfo(#"C:\Python27\python.exe", fileName)
{
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
Console.ReadLine();
}
}
}
Python sample_script
print "Python C# Test"
You will see the 'Python C# Test' in the console of C#.
I ran into the same problem and Master Morality's answer didn't do it for me. The following, which is based on the previous answer, worked:
private void run_cmd(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = cmd;//cmd is full path to python.exe
start.Arguments = args;//args is path to .py file and any cmd line args
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using(Process process = Process.Start(start))
{
using(StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
As an example, cmd would be #C:/Python26/python.exe and args would be C://Python26//test.py 100 if you wanted to execute test.py with cmd line argument 100. Note that the path the .py file does not have the # symbol.
Actually its pretty easy to make integration between Csharp (VS) and Python with IronPython. It's not that much complex... As Chris Dunaway already said in answer section I started to build this inegration for my own project. N its pretty simple.
Just follow these steps N you will get your results.
step 1 : Open VS and create new empty ConsoleApp project.
step 2 : Go to tools --> NuGet Package Manager --> Package Manager Console.
step 3 : After this open this link in your browser and copy the NuGet Command.
Link: https://www.nuget.org/packages/IronPython/2.7.9
step 4 : After opening the above link copy the PM>Install-Package IronPython -Version 2.7.9
command and paste it in NuGet Console in VS.
It will install the supportive packages.
step 5 : This is my code that I have used to run a .py file stored in my Python.exe
directory.
using IronPython.Hosting;//for DLHE
using Microsoft.Scripting.Hosting;//provides scripting abilities comparable to batch files
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
class Hi
{
private static void Main(string []args)
{
Process process = new Process(); //to make a process call
ScriptEngine engine = Python.CreateEngine(); //For Engine to initiate the script
engine.ExecuteFile(#"C:\Users\daulmalik\AppData\Local\Programs\Python\Python37\p1.py");//Path of my .py file that I would like to see running in console after running my .cs file from VS.//process.StandardInput.Flush();
process.StandardInput.Close();//to close
process.WaitForExit();//to hold the process i.e. cmd screen as output
}
}
step 6 : save and execute the code
Set WorkingDirectory or specify the full path of the python script in the Argument
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "C:\\Python27\\python.exe";
//start.WorkingDirectory = #"D:\script";
start.Arguments = string.Format("D:\\script\\test.py -a {0} -b {1} ", "some param", "some other param");
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
I am having problems with stdin/stout - when payload size exceeds several kilobytes it hangs. I need to call Python functions not only with some short arguments, but with a custom payload that could be big.
A while ago, I wrote a virtual actor library that allows to distribute task on different machines via Redis. To call Python code, I added functionality to listen for messages from Python, process them and return results back to .NET.
Here is a brief description of how it works.
It works on a single machine as well, but requires a Redis instance. Redis adds some reliability guarantees - payload is stored until a worked acknowledges completion. If a worked dies, the payload is returned to a job queue and then is reprocessed by another worker.
had same issure and this worked for me:
using IronPython.Hosting;
var engine = Python.CreateEngine();
engine.ExecuteFile("") //put the directory of the program in the quote marks
Good day guys.
Currently, I am developing a code to execute a Teraterm macro which I had saved as a *.ttl file. The name of the file is "new.ttl" and the content is as below:
showtt 0
filedelete 'a.txt'
pause 5
:Close
closett
So, the logic is just to delete "a.txt" file, wait for 5 seconds and close Teraterm. This new.ttl works perfectly when I run it manually using Teraterm, where I load the macro in the tab control>macro. This simple .ttl file is just for some trial for me before I start to write a more complex code.
Now, I tried to launch the .ttl file using C#. The code is as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Threading;
using System.Diagnostics;
namespace TeraTermConnect
{
class Program
{
static void Main(string[] args)
{
//Declare process for .ttl
Process process = new Process();
ProcessStartInfo start = new ProcessStartInfo();
//variables
string ttlpath = #"C:\TeraTermConnect\TeraTermConnect";
string ttl = "new.ttl";
string ttpHidden = #"/V";
//start the .ttl file
start.FileName = ttlpath;
start.Arguments = ttpHidden + ttl;
start.UseShellExecute = false;
//Tried a lot of thing here, not sure how to run the .ttl
Process.Start(start);
Thread.Sleep(5000);
Console.WriteLine("The process is over");
Console.WriteLine();
Console.WriteLine("Check the text file...");
Console.WriteLine();
Console.WriteLine("Hit enter to exit...");
Console.ReadKey();
}
}
}
The execution runs without any error, but, the result is not as expected. After the execution, I can see "a.txt" is still inside the mentioned path as in the code. I am not sure where I went wrong. This is just a starting step for me before I develop a more complex .ttl file and execute it through c#.
Your help is deeply appreciated. Thank you very much.
Good day guys,
After 2 days of struggle, I managed to get the answer.
using System;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
namespace TeraTermConnect
{
class Program
{
static void Main(string[] args)
{
//Declare process for .ttl
Process process = new Process();
ProcessStartInfo start = new ProcessStartInfo();
//variables
string ttlpath = #"C:\Program Files (x86)\teraterm\" + #"TTPMACRO";
string ttl = "new.ttl";
string ttpHidden = #"/V ";
ProcessStartInfo start = new ProcessStartInfo();
//start the .ttl file
start.FileName = ttlpath;
start.Arguments = ttpHidden + ttl;
start.UseShellExecute = false;
process.StartInfo = start;
try
{
Process.Start(start);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Console.WriteLine("The process is over");
Console.WriteLine();
Console.WriteLine("Check the text file...");
Console.WriteLine();
Console.WriteLine("Hit enter to exit...");
Console.ReadKey();
}
}
}
The version of Teraterm that i am currently using is 4.94 and I had also installed TTLEditor version 1.5 to create the .TTL file. It seems that the problem was,
1) To execute a .TTL file programmatically from C#, I need to place the .TTL file in the same folder where TTPMACRO.EXE and TTERMPRO.EXE is located in my system. This is shown by the string value ttlpath in my code.
2) In the ttlpath, the string value #"TTPMACRO" need to be added to the folder as this will make the .TTL file to be executable.
And, for your info, in my system, the text file a.txt that will be deleted if the logic of the .TTL file is executed is located at:
C:\Users\Admin\AppData\Local\VirtualStore\Program Files (x86)\teraterm
For more info on how to run teraterm macro files, refer to this link;
https://ttssh2.osdn.jp/manual/en/macro/howtorun.html
have a nice day..
Hari
I am trying to execute a batch file which runs on its own. I am now trying to automate this by deploying it as a windows service which listens for a folder and invokes the batch file using file watcher event. Here is the code -
void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
ServiceEventLog.WriteEntry(TheServiceName + " Inside fileSystemWatcher_Created() - ");
if (e.Name.Trim().ToUpper().Contains("FU4DGF_TRADES"))
{
try
{
Utilities.SendEmail("IAMLDNSMTP", 25, "desmond.quilty#investecmail.com", "IAMITDevelopmentServices#investecmail.com", "Ben.Howard#investecmail.com", "prasad.matkar#investecmail.com", "StatPro BatchFile Execution Started ", "");
int exitCode;
// ProcessStartInfo processInfo;
ServiceEventLog.WriteEntry(TheServiceName + " Before creation of instance of Batch process - ");
Process process = new Process();
process.StartInfo.FileName = #"C:\Program Files (x86)\StatPro Suite\MonthlyUpload.bat";
process.StartInfo.RedirectStandardOutput = false;
process.StartInfo.RedirectStandardError = false;
process.StartInfo.CreateNoWindow = false;
process.StartInfo.WorkingDirectory = #"C:\Program Files (x86)\StatPro Suite";
process.StartInfo.UseShellExecute = false;
ServiceEventLog.WriteEntry(TheServiceName + " Before start of Batch process - ");
process.Start();
ServiceEventLog.WriteEntry(TheServiceName + " After start of Batch process - ");
process.WaitForExit();
//while (!process.HasExited)
//{
// System.Threading.Thread.Sleep(100);
//}
ServiceEventLog.WriteEntry(TheServiceName + " After process.close - ");
System.Environment.ExitCode = process.ExitCode;
}
I can see from my event log that it goes as far as logging - Before start of Batch process. Presumably after that the process starts by invoking process.Start() but then nothing happens. Nothing in the event log, the service is still running i.e. not crashed. No errors. I can see from task manager that it does invoke the exe that it is supposed to invoke via the batch file but the exe simply remains in memory with constant memory and 0 CPU usage suggesting the exe is not doing anything. If I run the batch file manually it works fine. Any idea what could be going wrong?
You disabled UseShellExecute. This means that you can't use the shell to execute the file. bat files are not executables, they are shell scripts.
Since you're not redirecting standard I/O anyway, just enable UseShellExecute and you should be fine.
What I intend to do is build an application which, among other things, will have a command line embedded in it just like some IDEs do (something I find extremely useful).
This is the code that I have so far, do note that it's a Winforms project:
public partial class Form1 : Form
{
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
p.StartInfo = info;
p.Start();
}
private void button1_Click(object sender, EventArgs e) {
using(StreamWriter sw = p.StandardInput) {
if(sw.BaseStream.CanWrite) {
sw.WriteLine(textBox1.Text);
}
}
textBox2.Text = p.StandardOutput.ReadToEnd();
textBox3.Text = p.StandardError.ReadToEnd();
p.WaitForExit();
}
}
}
As you can see there are 3 textboxes and one button:
textbox1 is for entering the command
textbox2 is for stdout
textbox3 is for stderr
On to my problem:
I can only input one command because after executing it, my CMD window vanishes. I know it dies off because I've set info.CreateNoWindow = false; and it indeed vanishes and if I try to enter another command I get an exception.
How would I go on about keeping my CMD window 'alive' so that I can use it as much as I please? In short I want to truly mimic CMD behavior.
Feel free to ask for more information if something is not clear.
Extra info/What I tried:
I've tried adding info.Attributes = "/K"; since I know that /K should keep the CMD alive. I've also read that p.WaitForExit(); should keep the CMD alive, but from what I figured this is only for the purpose of reading the output. Needless to say, I do not need that since I'm already redirecting its output. Neither of these solutions work but it is entirely possible that I'm using them the wrong way.
I need that process alive so I can easily navigate using cd and executing a sequence of commands when needed, such as when accessing ftp or mysql. I know I can work around these two examples with parameters, but not for every application. In short, spawning a new process every time is not something I want. I want that CMD interface to be up at all times.
The cmd process dies after
using(StreamWriter sw = p.StandardInput) {
if(sw.BaseStream.CanWrite) {
sw.WriteLine(textBox1.Text);
}
}
But I cannot pinpoint why.
What CMD console provides is an interface to execute predefined functions (in System32 or in %PATH%). Process class also have same capabilities ,what you can do is as the user enters command text and presses return key in textbox2 (which can be multi-lined, black-background, white text) you can pass the command text to Process p = new Process();and append the result so it looks like single cmd session. Now before passing the whole command text we need to separate arguments (if any) which is text appearing after first space. Example:
SHUTDOWN /S /T 10
where Shutdown will be filename and /S /T 10 will be arguments.
Before executing set default directory of ProcessStartInfo:-
_processStartInfo.WorkingDirectory = #"%Path%";
Otherwise default will be System32 folder.
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.