Calling Python script from Winform C# Application - c#

I want to call a Python script from my winform C# application. I checked for some solutions and followed following approaches. One using Inter-Process communication and one using IronPython
Approach 1 : Using Inter-Process Communication
private void BtnSumPy_Click(object sender, EventArgs e)
{
string python = #"C:\Programs\Python\Python37-32\python.exe";
// python app to call
string myPythonApp = #"C:\mypath\\SamplePy\SamplePy2\SamplePy2.py";
// dummy parameters to send Python script
int x = 3;
int y = 4;
// Create new process start info
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(python);
// make sure we can read the output from stdout
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardOutput = true;
// start python app with 3 arguments
// 1st arguments is pointer to itself,
// 2nd and 3rd are actual arguments we want to send
myProcessStartInfo.Arguments = myPythonApp + " " + x + " " + y;
Process myProcess = new Process();
// assign start information to the process
myProcess.StartInfo = myProcessStartInfo;
// start the process
myProcess.Start();
// Read the standard output of the app we called.
// in order to avoid deadlock we will read output first
// and then wait for process terminate:
StreamReader myStreamReader = myProcess.StandardOutput;
string myString = myStreamReader.ReadLine();
/*if you need to read multiple lines, you might use:
string myString = myStreamReader.ReadToEnd() */
// wait exit signal from the app we called and then close it.
myProcess.WaitForExit();
myProcess.Close();
lblAns.Text = myString;
}
The issue with above approach is that Python.exe will have to installed on the local machines as well, as winform app is going to run locally on the system.
Approach 2: Using IronPython
private void BtnJsonPy_Click(object sender, EventArgs e)
{
// 1. Create Engine
var engine = Python.CreateEngine();
//2. Provide script and arguments
var script = #"C:\Users\simeh\source\HDFC\repos\SamplePy\SamplePy2\SamplePy2.py"; // provide full path
var source = engine.CreateScriptSourceFromFile(script);
// dummy parameters to send Python script
int x = 3;
int y = 4;
var argv = new List<string>();
argv.Add("");
argv.Add(x.ToString());
argv.Add(y.ToString());
engine.GetSysModule().SetVariable("argv", argv);
//3. redirect output
var eIO = engine.Runtime.IO;
var errors = new MemoryStream();
eIO.SetErrorOutput(errors, Encoding.Default);
var results = new MemoryStream();
eIO.SetOutput(results, Encoding.Default);
//4. Execute script
var scope = engine.CreateScope();
var lib = new[]
{
"C:\\path\\SamplePy\\packages\\IronPython.2.7.9\\lib",
"C:\\path\\SamplePy\\packages\\IronPython.2.7.9",
};
engine.SetSearchPaths(lib);
engine.ExecuteFile(script, scope);
//source.Execute(scope);
//5. Display output
string str(byte[] x1) => Encoding.Default.GetString(x1);
Console.WriteLine("Errrors");
Console.WriteLine(str(errors.ToArray()));
Console.WriteLine();
Console.WriteLine("Results");
Console.WriteLine(str(results.ToArray()));
}
The issue I get here is that I keep getting errors like 'Json module error' or 'PIL module error'
I read somewhere that PIL won't currently work with IronPython because it uses a native C library.
The python script has ML logic and uses OCR etc., for image processing and hence requires PIL, which cannot be done in IronPython.
So any better approach or ways or suggestions on how to call Python script from Winform C# application.
Thanks In Advance!!!..

The solution is here for 'import error' open the cmd and go to the AppData>Local>Programs>Python>Python37-32 then write this
pip3 install json
if u wanna run .py file from c# the import modules must be in the directory of python.exe
for example ı imported the cv2 and the others libs to Python37-32 dir. After this my program worked fine.
This is my code:
timer1.Enabled = true;
progressBar1.Value += 10;
string myPythonApp = "C://Users//giris//Desktop//staj_proje_son//main.py";
string cmdArguments = "/c \"python " + myPythonApp + " " + "--ogrencioptik " + textBox2.Text + " " + "--cevapkagidi " + textBox1.Text + " " + "--sonuckayit " + textBox3.Text + "\"";
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.UseShellExecute = false;
start.WorkingDirectory = "C://Users//giris//Desktop//staj_proje_son//";
start.Arguments = cmdArguments;
start.RedirectStandardOutput = false;
start.RedirectStandardError = true;
start.CreateNoWindow = true;
Process process = Process.Start(start);
timer1.Start();
process.WaitForExit();
timer1.Stop();
timer1.Enabled = false;
progressBar1.Value = 100;
MessageBox.Show("İşlem Bitti");
button3.Enabled = true;
Note:
All the textbox.text is the path for a folder.

Related

Process.Start() is not releasing file handles

I have some code that is creating a PNG file of a graph of Nodes using GraphViz's dot program. This first time through the code works perfectly. However, if I try to run the method again it fails because the file is still considered in use by windows.
Here is the relevant code to start the dot.exe process:
private void MakePng()
{
string args = "-o" + graphPath + " -Tpng " + dotPath;
Process process = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = VizGraphPath;
info.Arguments = args;
info.UseShellExecute = false;
info.CreateNoWindow = true;
process.StartInfo = info;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(Process_Exited);
process.Start();
}
private void Process_Exited(object sender, EventArgs e)
{
UpdateCanvas();
}
The various string variables named xxxPath are just static strings to the correct file paths. When the program starts up and I run this code everything works perfectly. But if I reset my graph and try to run this set of code again it fails to create a new PNG. The old one is still there. To test something out I added this line:
private void MakePng()
{
string args = "-o" + graphPath + " -Tpng " + dotPath;
File.Delete(graphPath);
Which works the first time through. But the second time it throws a Exception stating File is still in use. So I'm guessing somehow when the Process I start exits, the File Handle it created is still in use even though it exited? Any suggestions about how to figure out why its still in use or how to fix it?
My UpdateCanvas function is also accessing the file that is locked:
public void UpdateCanvas()
{
Bitmap image = new Bitmap(graphPath);
pbCanvas.Image = image;
}
Do i need to something when loading a bitmap from a file to release the file?
It was indeed the Bitmap locking the file. I had to dispose of it before the file would be unlocked.
Your file is locked by new Bitmap.
Rewrite it with:
public void UpdateCanvas()
{
Image img;
using (var bmpTemp = new Bitmap(graphPath))
{
img = new Bitmap(bmpTemp);
}
pbCanvas.Image = img;
}
Process class implements IDisposable, so you need to despose resources. Also, you can add WatForExit method to be sure, your process finished:
private void MakePng()
{
string args = "-o" + graphPath + " -Tpng " + dotPath;
using(Process process = new Process())
{
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = VizGraphPath;
info.Arguments = args;
info.UseShellExecute = false;
info.CreateNoWindow = true;
process.StartInfo = info;
process.EnableRaisingEvents = true;
process.Start();
process.WaitForExit(10*1000); //10 seconds
}
UpdateCanvas();
}

C# Mono Linux Process Output

Running a c# console application compiled in visual studio 2013 on a Raspberry Pi(Linux) with Mono installed, the following code...
System.Diagnostics.Process syncProc = new System.Diagnostics.Process();
syncProc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
syncProc.StartInfo.UseShellExecute = false;
syncProc.StartInfo.FileName = "bash";
syncProc.StartInfo.Arguments = "-c sync --help";
syncProc.StartInfo.RedirectStandardError = true;
syncProc.StartInfo.RedirectStandardOutput = true;
syncProc.Start();
String stdOutput = syncProc.StandardOutput.ReadToEnd();
String errorOutput = syncProc.StandardError.ReadToEnd();
syncProc.WaitForExit();
if (syncProc.ExitCode == 0)
{
Console.WriteLine("SOUTPUT: " + stdOutput);
}
else
{
Console.WriteLine("EOUTPUT: " + errorOutput);
throw new Exception("Failed to sync with exit code: " + syncProc.ExitCode);
}
...outputs "SOUTPUT: "
Why is stdOutput always empty?
You could try redirecting the whole output asynchronously like this:
syncProc.ErrorDataReceived += (s, e) => Console.WriteLine("EOUTPUT:{0}", e.Data);
syncProc.OutputDataReceived += (s, e) => Console.WriteLine("SOUTPUT:{0}", e.Data);
syncProc.Start();
syncProc.BeginErrorReadLine();
syncProc.BeginOutputReadLine();
Why is stdOutput always empty?
The arguments are passed wrongly. The -c option's commands must be contained in a single argument command_string:
syncProc.StartInfo.Arguments = "-c 'sync --help'";

How do I allow a c# process to create a file as if it were run on the command line?

I am trying to execute a process exactly as if it were executed on the window's command line but the process class won't allow the executable to create a file similar to what it would do if it were run from the command line. The Processes will when deployed run asynchronously on a server at timed intervals. The command line would calls would look like this:
curl.exe url -o data
wgrib2.exe data -csv output.csv
In the code below I found a workaround for curl.exe but when it is read as StandardOutput the file cuts off some of the critical stopping characters for this file type.
public static void httpQuery(string queryString)
{
Process myProcess = new Process();
try
{
string basepath = AppDomain.CurrentDomain.BaseDirectory;
myProcess.StartInfo.UseShellExecute = false; // allows us to redirect the output
myProcess.StartInfo.CreateNoWindow = true; // stop's command window from being made
myProcess.StartInfo.Verb = "runas";
myProcess.StartInfo.FileName = #"C:\Users\Ian's\Desktop\wgrib2\curl.exe";
myProcess.StartInfo.Arguments = queryString; // queryString is http://nomads.noaa.gov/....
myProcess.StartInfo.RedirectStandardError = true; // redirect error to stream reader
myProcess.StartInfo.RedirectStandardOutput = true; // redirect output to strem reader
myProcess.Start();
if (myProcess.WaitForExit(10000) == false)
{
myProcess.Kill();
}
using(StreamReader reader = myProcess.StandardError) // capture error output if any
{
string result = reader.ReadToEnd();
reader.Close();
StreamReader message = myProcess.StandardOutput;
string output = message.ReadToEnd();
StreamWriter writer = new StreamWriter(basepath + "wgrib2\\data");
writer.Write(output);
writer.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
myProcess.Close();
}
Additionally, I cannot seem to find a way to allow the second process to write a new file, or if it has been I cannot find the directory that it is writing to. I am working with VS2012 on the IIS development server. This is the second processes code.
public static void callWgrib2()
{
Process wgrib2 = new Process();
try
{
// call on command line: wgrib2.exe data -csv data.csv
string basepath = AppDomain.CurrentDomain.BaseDirectory; // path to project directory
string arg1 = basepath + "wgrib2\\data"; // filename of operate on
string arg2 =" -csv " + basepath + "wgrib2\\data.csv"; // filename to write to
wgrib2.StartInfo.UseShellExecute = false; // parameters
wgrib2.StartInfo.CreateNoWindow = true;
wgrib2.StartInfo.Verb = "runas";
wgrib2.StartInfo.FileName = #"C:\Users\Ian's\Desktop\wgrib2\wgrib2.exe";
wgrib2.StartInfo.Arguments = "\""+ arg1 + arg2 + "\"";
wgrib2.StartInfo.RedirectStandardError = true;
wgrib2.Start();
if (wgrib2.WaitForExit(10000) == false)
{
wgrib2.Kill();
}
using (StreamReader reader = wgrib2.StandardOutput)
{
string result = reader.ReadToEnd();
reader.Close();
StreamReader err = wgrib2.StandardError;
err.ReadToEnd();
err.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
wgrib2.Close();
}
Can someone point me in the write direction I have put considerable effort into this problem but I'm sure there exits a simple workaround I'm not aware of.
If you want to use the actual command line process, you can use cmd and pass the /c flag to it in the arguements
also, the line
wgrib2.StartInfo.Arguments = "\""+ arg1 + arg2 + "\"";
looks odd
do you really need all your arguments such as -csv to be in the same quotes block?
it's like calling on the command line
C:\Users\Ian's\Desktop\wgrib2\wgrib2.exe "c:\base\wgrib2\data -csv c:\base\wgrib2\data.csv"

How to compile Java from C#?

I'm trying to use the following C# code to compile Java using javac:
Process p = new Process();
p.StartInfo.FileName = "javac";
Directory.CreateDirectory(Application.StartupPath + #"/TempJava");
p.StartInfo.Arguments = "-d "Application.StartupPath + #"/TempJava" + files;
p.Start();
"files" represents a string variable containing the name(s) of the *.java files.
All in all, I want to create a new folder, and then take the Java files (from where ever they may be located) and compile it into a class file(s) in TempJava.
For some reason, the code doesn't work, no errors, no warnings, but when I run it and check TempJava, there's no files in it.
Just because your child process ends with a possible error, it doesn't mean your parent process must be aware of it.
Inspect the process' exit code and standard output stream, and especially the standard error stream. Your answer lies in there...
here i have 2 buttons run and compile here is some code to help.
private void comp_Click(object sender, EventArgs e)
{
string text = "javac " + label1.Text + file + "#pause" + "#stop";
text = text.Replace("#", System.Environment.NewLine);
File.WriteAllText(label1.Text + "Compile.bat", text);
Process proc = null;
try
{
proc = new Process();
proc.StartInfo.FileName = label1.Text + "Compile.bat";
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
}
catch
{
}
}
private void runp_Click(object sender, EventArgs e)
{
string news = file.Remove(file.Length - 5);
string text = "java " + news + "#pause";
text = text.Replace("#", System.Environment.NewLine);
File.WriteAllText(label1.Text + "Run.bat", text);
Process proc = null;
try
{
proc = new Process();
proc.StartInfo.FileName = label1.Text + "Run.bat";
proc.StartInfo.WorkingDirectory = label1.Text.Remove(label1.Text.Length - 1);
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.WaitForExit();
}
catch
{
}
}
all i really do is create a batch and run it using c#.

C# Keeping Sub-Process Running

I wrote a small program in C# NET a while back to keep a Java process running. I am about to deploy it to a bunch of servers and I am working on fixing up some of the code now. As it stands, I don't think I have this setup right.
What would be the best way to keep the process running that I am creating in my LaunchMinecraft() function? I want to have it so as long as my process is running, it continues to restart this process if it crashes.
static void Main(string[] args)
{
// Launch the Application
LaunchMinecraft("minecraft_server.jar", "512");
}
public static void LaunchMinecraft(string file, string memory)
{
string memParams = "-Xms" + memory + "M" + " -Xmx" + memory + "M ";
string args = memParams + "-jar " + file + " nogui";
ProcessStartInfo processInfo = new ProcessStartInfo("java.exe", args);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardInput = true;
processInfo.RedirectStandardError = true;
try
{
using (Process minecraftProcess = Process.Start(processInfo))
{
// Store Process Globally to access elsewhere
JavaProcess = minecraftProcess;
// Creates a Repeating Poll Timer (30 Seconds)
// Use this to keep the Minecraft's Process Affinity/Priority the same as the Daemon
PollTimer = new System.Timers.Timer();
PollTimer.Elapsed += new ElapsedEventHandler(Event_PollProcess);
PollTimer.Interval = 5000;
PollTimer.Start();
Console.WriteLine("Minecraft Process Started.");
// Setup Callback for Redirected Output/Error Streams
minecraftProcess.OutputDataReceived += new DataReceivedEventHandler(Handler_ProcessOutput);
minecraftProcess.ErrorDataReceived += new DataReceivedEventHandler(Handler_ProcessError);
// Steam Writer
streamWriter = minecraftProcess.StandardInput;
minecraftProcess.BeginOutputReadLine();
minecraftProcess.BeginErrorReadLine();
while (minecraftProcess.HasExited == false)
{
String strInput = Console.ReadLine();
SendProcessCmd(strInput);
}
minecraftProcess.WaitForExit();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
The Process class itself cant prevent your external program from shutting down, you have to use the Exited event as #Jalal mentiones or poll the HasExited property of the process.

Categories