How do I run a Python script from C#? - c#

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

I had the same issue and used answers on here to solve it using a process. I had conflicts between my .NET and IronPython so wasn't successful there. This works well with my python 3.10.
public void Run_cmd2(string exe, string args, string output )
{
var outputStream = new StreamWriter(output);
// create a process with the name provided by the 'exe' variable
Process cmd = new Process();
cmd.StartInfo.FileName = exe;
//define you preference on the window and input/output
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
// write the output to file created
cmd.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
outputStream.WriteLine(e.Data);
}
});
cmd.Start();
// write to the console you opened. In this case for example the python console
cmd.StandardInput.WriteLine(args);
//Read the output and close everything. make sure you wait till the end of the process
cmd.BeginOutputReadLine();
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
//close the process. writing to debug helps when coding
outputStream.Close();
//Console.WriteLine(cmd.StandardOutput.ReadToEnd());
cmd.Close();
Debug.WriteLine("\n\n Process done!");
//Console.ReadLine();
}
Example call:
string pythonEngine = "C:\ProgramData\Anaconda3\envs\compVision\python.exe";
string pythonArguements = "import os ; os.chdir('C:\YourPath\excelWorkbooks') ; import testSearch ; testSearch.performAdd(2, 3)";
// here a function in testSearch.py is called. To run the .py directly do this:
string pythonArguements = "import os ; os.chdir('C:\YourPath\excelWorkbooks') ; import testSearch ; testSearch.py";
outFile = "C:\YourPath\output.txt";
_yourModuleName.Run_cmd2(pythonEngine, pythonArguements, outFile);

Related

Embed Python in C# Windows Application [duplicate]

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

Execute Python From C# Without IronPython [duplicate]

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
I had the same issue and used answers on here to solve it using a process. I had conflicts between my .NET and IronPython so wasn't successful there. This works well with my python 3.10.
public void Run_cmd2(string exe, string args, string output )
{
var outputStream = new StreamWriter(output);
// create a process with the name provided by the 'exe' variable
Process cmd = new Process();
cmd.StartInfo.FileName = exe;
//define you preference on the window and input/output
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
// write the output to file created
cmd.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
outputStream.WriteLine(e.Data);
}
});
cmd.Start();
// write to the console you opened. In this case for example the python console
cmd.StandardInput.WriteLine(args);
//Read the output and close everything. make sure you wait till the end of the process
cmd.BeginOutputReadLine();
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
//close the process. writing to debug helps when coding
outputStream.Close();
//Console.WriteLine(cmd.StandardOutput.ReadToEnd());
cmd.Close();
Debug.WriteLine("\n\n Process done!");
//Console.ReadLine();
}
Example call:
string pythonEngine = "C:\ProgramData\Anaconda3\envs\compVision\python.exe";
string pythonArguements = "import os ; os.chdir('C:\YourPath\excelWorkbooks') ; import testSearch ; testSearch.performAdd(2, 3)";
// here a function in testSearch.py is called. To run the .py directly do this:
string pythonArguements = "import os ; os.chdir('C:\YourPath\excelWorkbooks') ; import testSearch ; testSearch.py";
outFile = "C:\YourPath\output.txt";
_yourModuleName.Run_cmd2(pythonEngine, pythonArguements, outFile);

Executing powershell .ps1 file on server and viewing results C# asp.net

I'm trying to execute a .ps1 PowerShell file on my server using a C# asp.net webpage.
The script takes one parameter, and I've verified that it works by using the command prompt on the server. After it runs, I need to display the results on the webpage.
Currently, I'm using:
protected void btnClickCmdLine(object sender, EventArgs e)
{
lblResults.Text = "Please wait...";
try
{
string tempGETCMD = null;
Process CMDprocess = new Process();
System.Diagnostics.ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo();
StartInfo.FileName = "cmd"; //starts cmd window
StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
StartInfo.CreateNoWindow = true;
StartInfo.RedirectStandardInput = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.UseShellExecute = false; //required to redirect
CMDprocess.StartInfo = StartInfo;
CMDprocess.Start();
lblResults.Text = "Starting....";
System.IO.StreamReader SR = CMDprocess.StandardOutput;
System.IO.StreamWriter SW = CMDprocess.StandardInput;
SW.WriteLine("#echo on");
SW.WriteLine("cd C:\\Tools\\PowerShell\\");
SW.WriteLine("powershell .\\poweron.ps1 **parameter**");
SW.WriteLine("exit"); //exits command prompt window
tempGETCMD = SR.ReadToEnd(); //returns results of the command window
lblResults.Text = tempGETCMD;
SW.Close();
SR.Close();
}
catch (Exception ex)
{
lblErrorMEssage.Text = ex.ToString();
showError();
}
}
However, it won't even display the initial "Please wait.." if I include the line where it calls powershell. It will just eventually timeout, even though I have increased the AsyncPostBackTimeout on the ScriptManager.
Can anyone please tell me what I'm doing wrong?
Thanks
A little dated; however, to those in search of a similar solution, I would not create a cmd and pass powershell to it but leverage the System.Management.Automation namespace and create a PowerShell console object server-side without the middle man of cmd. You can pass commands or .ps1 files to the AddScript() function - both with arguments - to it for execution. Much cleaner than a separate shell that will have to then call the powershell.exe.
Ensure appropriate identity of the application pool and that principal has the appropriate level of rights required to perform the PowerShell commands and/or scripts. Also, make sure you have the execution policy configured via Set-ExecutionPolicy to the appropriate level (unrestricted/ or remote signed, unless you're signing), in the event you're still going to go the way of executing a .ps1 file server side.
Here's some starter code that is executing commands submitted by a TextBox web form as if it were a a PowerShell console using these objects - should illustrate the approach:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Management.Automation;
using System.Text;
namespace PowerShellExecution
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void ExecuteCode_Click(object sender, EventArgs e)
{
// Clean the Result TextBox
ResultBox.Text = string.Empty;
// Initialize PowerShell engine
var shell = PowerShell.Create();
// Add the script to the PowerShell object
shell.Commands.AddScript(Input.Text);
// Execute the script
var results = shell.Invoke();
// display results, with BaseObject converted to string
// Note : use |out-string for console-like output
if (results.Count > 0)
{
// We use a string builder ton create our result text
var builder = new StringBuilder();
foreach (var psObject in results)
{
// Convert the Base Object to a string and append it to the string builder.
// Add \r\n for line breaks
builder.Append(psObject.BaseObject.ToString() + "\r\n");
}
// Encode the string in HTML (prevent security issue with 'dangerous' caracters like < >
ResultBox.Text = Server.HtmlEncode(builder.ToString());
}
}
}
}
Here's is a write up for you that covers how to create a page from start to finish with Visual Studio and get this done, http://grokgarble.com/blog/?p=142.
I would think you can not run powershell script from aspx page directly like this, since it may related with security reason. in order to run and capture the output:
create a remote runspace:
http://social.msdn.microsoft.com/Forums/hu/sharepointgeneralprevious/thread/88b11fe3-c218-49a3-ac4b-d1a04939980c
http://msdn.microsoft.com/en-us/library/windows/desktop/ee706560%28v=vs.85%29.aspx
PSHost:
Capturing Powershell output in C# after Pipeline.Invoke throws
1 works very well for me.
BTW, the label is not updated because the event is not finished. you may need use ajax to show the label while waiting for powershell.
When I tried this I found the standard input needs to be flushed or closed before SR.ReadToEnd() can complete. Try this:
lblResults.Text = "Please wait...";
try
{
string tempGETCMD = null;
Process CMDprocess = new Process();
System.Diagnostics.ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo();
StartInfo.FileName = "cmd"; //starts cmd window
StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
StartInfo.CreateNoWindow = true;
StartInfo.RedirectStandardInput = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.UseShellExecute = false; //required to redirect
CMDprocess.StartInfo = StartInfo;
CMDprocess.Start();
lblResults.Text = "Starting....";
using (System.IO.StreamReader SR = CMDprocess.StandardOutput)
{
using (System.IO.StreamWriter SW = CMDprocess.StandardInput)
{
SW.WriteLine("#echo on");
SW.WriteLine("cd C:\\Tools\\PowerShell\\");
SW.WriteLine("powershell .\\poweron.ps1 **parameter**");
SW.WriteLine("exit"); //exits command prompt window
}
tempGETCMD = SR.ReadToEnd(); //returns results of the command window
}
lblResults.Text = tempGETCMD;
}
catch (Exception ex)
{
lblErrorMessage.Text = ex.ToString();
showError();
}
}

Process.StartInfo.UseShellExecute related error

i have 1 c# console appln,it executes any script file by using Process.Start() method. i provide script file path to Process1.StartInfo.FileName.My script file can be of any type (.vbs ,ps1 etc) . I m also passing String to script by using instruction p.StartInfo.Arguments. When script file executed it should retrun string back to c# application. This returned string can be read by setting Process1.StartInfo.RedirectStandardOutput = true,But for using this instruction i need to set Process1.StartInfo.UseShellExecute = false.
When i run this i m getting error as "The specified executable is not a valid Win32 application".
i think this may be because of, when i set Process1.StartInfo.UseShellExecute = false, my appln dont know which .exe to be used to execute script file.
on the other hand if i provide exe path to StartInfo.FileName and script file path to StartInfo.Argument then i m not getting error.
For Example:i want to execute powershell script and i set following properties as P1.StartInfo.FileName = "location of powershell.exe" and p1.startInfo.Argument =".ps1 script file path", in this case i m not getting error.
problem is i dont know in advance,which type of script i m going to execute.also cant find out locations of .exe file for executing script file on different diffrent m/c. So is it possible to execute diffrent type of script files from same common c# appln and also read output returned by scripts?
here is my code
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Collections;
namespace process_csharp
{
class Program
{
static void Main(string[] args)
{
String path = null;
//this will read script file path
path = Console.ReadLine();
//this string is passed as argument to script
string s = "xyz";
Process p = new Process();
p.StartInfo.FileName= path;
p.StartInfo.Arguments = s;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
p.BeginOutputReadLine();
Console.WriteLine(p.StandardOutput.ReadToEnd());
}
}
}
You can check for the script type and read output from their own engine;
static void Main(string[] args)
{
string path = Console.ReadLine();
string parameter = Console.ReadLine();
string enginePath;
switch (Path.GetExtension(path).ToLowerInvariant())
{
case ".ps1":
enginePath = "powershell.exe";
break;
case ".vbs":
enginePath = "cscript.exe";
break;
default:
throw new ApplicationException("Unknown script type");
}
string scriptPath = path;
Process process = new Process();
process.StartInfo.FileName = enginePath;
process.StartInfo.Arguments = string.Format("{0} {1}", scriptPath, parameter);
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
Console.WriteLine(process.StandardOutput.ReadToEnd());
Console.ReadKey();
}
Try this code instead:
string path = #"C:\mypsscript.bat";
Process p = new Process();
p.StartInfo.FileName = path;
p.StartInfo.Arguments = "xyz";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
Console.WriteLine(p.StandardOutput.ReadToEnd());
Console.ReadKey();
For the sakes of debugging I have created a batch file with the code:
echo %1
When I run the above code I get:
xyz
So that seems to work fine. Try using a batch file like this and see if it works, if it does it may be an association with powershell scripts that isn't working, which we can fix later.

C#.Net: Why is my Process.Start() hanging?

I'm trying to run a batch file, as another user, from my web app. For some reason, the batch file hangs! I can see "cmd.exe" running in the task manager, but it just sits there forever, unable to be killed, and the batch file is not running. Here's my code:
SecureString password = new SecureString();
foreach (char c in "mypassword".ToCharArray())
password.AppendChar(c);
ProcessStartInfo psi = new ProcessStartInfo();
psi.WorkingDirectory = #"c:\build";
psi.FileName = Environment.SystemDirectory + #"\cmd.exe";
psi.Arguments = "/q /c build.cmd";
psi.UseShellExecute = false;
psi.UserName = "builder";
psi.Password = password;
Process.Start(psi);
If you didn't guess, this batch file builds my application (a different application than the one that is executing this command).
The Process.Start(psi); line returns immediately, as it should, but the batch file just seems to hang, without executing. Any ideas?
EDIT: See my answer below for the contents of the batch file.
The output.txt never gets created.
I added these lines:
psi.RedirectStandardOutput = true;
Process p = Process.Start(psi);
String outp = p.StandardOutput.ReadLine();
and stepped through them in debug mode. The code hangs on the ReadLine(). I'm stumped!
I believe I've found the answer. It seems that Microsoft, in all their infinite wisdom, has blocked batch files from being executed by IIS in Windows Server 2003. Brenden Tompkins has a work-around here:
http://codebetter.com/blogs/brendan.tompkins/archive/2004/05/13/13484.aspx
That won't work for me, because my batch file uses IF and GOTO, but it would definitely work for simple batch files.
Why not just do all the work in C# instead of using batch files?
I was bored so i wrote this real quick, it's just an outline of how I would do it since I don't know what the command line switches do or the file paths.
using System;
using System.IO;
using System.Text;
using System.Security;
using System.Diagnostics;
namespace asdf
{
class StackoverflowQuestion
{
private const string MSBUILD = #"path\to\msbuild.exe";
private const string BMAIL = #"path\to\bmail.exe";
private const string WORKING_DIR = #"path\to\working_directory";
private string stdout;
private Process p;
public void DoWork()
{
// build project
StartProcess(MSBUILD, "myproject.csproj /t:Build", true);
}
public void StartProcess(string file, string args, bool redirectStdout)
{
SecureString password = new SecureString();
foreach (char c in "mypassword".ToCharArray())
password.AppendChar(c);
ProcessStartInfo psi = new ProcessStartInfo();
p = new Process();
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.WorkingDirectory = WORKING_DIR;
psi.FileName = file;
psi.UseShellExecute = false;
psi.RedirectStandardOutput = redirectStdout;
psi.UserName = "builder";
psi.Password = password;
p.StartInfo = psi;
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(p_Exited);
p.Start();
if (redirectStdout)
{
stdout = p.StandardOutput.ReadToEnd();
}
}
void p_Exited(object sender, EventArgs e)
{
if (p.ExitCode != 0)
{
// failed
StringBuilder args = new StringBuilder();
args.Append("-s k2smtpout.secureserver.net ");
args.Append("-f build#example.com ");
args.Append("-t josh#example.com ");
args.Append("-a \"Build failed.\" ");
args.AppendFormat("-m {0} -h", stdout);
// send email
StartProcess(BMAIL, args.ToString(), false);
}
}
}
}
Without seeing the build.cmd it's hard to tell what is going on, however, you should build the path using Path.Combine(arg1, arg2); It's the correct way to build a path.
Path.Combine( Environment.SystemDirectory, "cmd.exe" );
I don't remember now but don't you have to set UseShellExecute = true ?
Another possibility to "debug" it is to use standardoutput and then read from it:
psi.RedirectStandardOutput = True;
Process proc = Process.Start(psi);
String whatever = proc.StandardOutput.ReadLine();
In order to "see" what's going on, I'd suggest you transform the process into something more interactive (turn off Echo off) and put some "prints" to see if anything is actually happening. What is in the output.txt file after you run this?
Does the bmail actually executes?
Put some prints after/before to see what's going on.
Also add "#" to the arguments, just in case:
psi.Arguments = #"/q /c build.cmd";
It has to be something very simple :)
My guess would be that the build.cmd is waiting for some sort of user-interaction/reply. If you log the output of the command with the "> logfile.txt" operator at the end, it might help you find the problem.
Here's the contents of build.cmd:
#echo off
set path=C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;%path%
msbuild myproject.csproj /t:Build > output.txt
IF NOT ERRORLEVEL 1 goto :end
:error
bmail -s k2smtpout.secureserver.net -f build#example.com -t josh#example.com -a "Build failed." -m output.txt -h
:end
del output.txt
As you can see, I'm careful not to output anything. It all goes to a file that gets emailed to me if the build happens to fail. I've actually been running this file as a scheduled task nightly for quite a while now. I'm trying to build a web app that allows me to run it on demand.
Thanks for everyone's help so far! The Path.Combine tip was particularly useful.
I think cmd.exe hangs if the parameters are incorrect.
If the batch executes correctly then I would just shell execute it like this instead.
ProcessStartInfo psi = new ProcessStartInfo();
Process p = new Process();
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.WorkingDirectory = #"c:\build";
psi.FileName = #"C:\build\build.cmd";
psi.UseShellExecute = true;
psi.UserName = "builder";
psi.Password = password;
p.StartInfo = psi;
p.Start();
Also it could be that cmd.exe just can't find build.cmd so why not give the full path to the file?
What are the endlines of you batch? If the code hangs on ReadLine, then the problem might be that it's unable to read the batch fileā€¦

Categories

Resources