C# PHP-CGI - Calling phpinfo crashes webserver - c#

I created a simple and small webserver for only handling GET requests. I also wanted to add PHP support and also managed it. But there is one problem:
Everytime I try to call phpinfo() inside a .php file my server stops at "WaitForExit" Process.
class FastPHP
{
private string _phpPath = #"C:\\Program Files (x86)\\PHP\\php-cgi.exe";
Process p;
public FastPHP(string filename)
{
p = new Process();
p.StartInfo.FileName = this._phpPath;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Arguments = "-q \""+filename+"\"";
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
}
public string getPHPOutput()
{
p.Start();
p.WaitForExit();
string sOutput = p.StandardOutput.ReadToEnd();
p.Close();
return sOutput;
}
}
my PHP.ini settings should be fine, I adapted everything for fastcgi use. Any Ideas how to fix this problem?

The problem is that the StandardOutput has a certain buffer size. If this buffer is full then any write() to stdout will block. Now if you call p.WaitForExit() you are waiting indefinitely.
The solution is to first read everything from the StandardOutput and then call WaitForExit.

Related

How to Wait Until an Operation Finishes?

I have C# code which included Python code in which it is run through CMD codes in C#. When the Python code is run the operations are done, a JSON file is created and then it will be opened in C#. In this situation, how the C# code can wait to check if the output of Python (data.json) is created or not, and just when the output is created, the rest of C# code is allowed to be run:
Process process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.Start();
process.StandardInput.WriteLine("F:\\");
process.StandardInput.WriteLine("cd F:\\Path");
process.StandardInput.WriteLine("python Python_Code.py");
process.StandardInput.Flush();
process.StandardInput.Close();
process.WaitForExit();
Then, the generated data with Python will be retrieved:
string Output_Python = File.ReadAllText(#"Data.json");
JavaScriptSerializer Ser = new JavaScriptSerializer();
Predicted Output = Ser.Deserialize<Predicted>(Output_Python);
You don't need to go through cmd.exe. The Python interpreter itself is an executable; in other words, it can be started and executed directly. The arguments for the Python interpreter (like the path+name of the script to be executed) and desired working directory can be set through the appropriate Process.StartInfo properties:
Process process = new Process();
process.StartInfo.FileName = "python.exe";
process.StartInfo.Arguments = "Python_Code.py";
process.StartInfo.WorkingDirectory = #"F:\Path";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.Start();
Now you only need to wait for the Python interpreter to exit (which means it finished executing the python script)
process.WaitForExit();
and after the Python process has exited, simply check if the json file exist/has been written:
if (System.IO.File.Exists(pathToJsonFile))
{
... do stuff with json file ...
}
else
{
... json file does not exist, something went wrong...
}
Side note: I kept process.StartInfo.RedirectStandardOutput = true; in my code example here, since i don't know what your program will really do. However, unless your program wants to process the output of the script that normally appears in a console window, setting RedirectStandardOutput to true is not necessary.
You should have a look at the FileSystemWatcher class. Documentation here.
Then you can do something like this:
using (FileSystemWatcher watcher = new FileSystemWatcher())
{
watcher.Path = YourDirectory;
// Watch for changes in LastWrite time
watcher.NotifyFilter = NotifyFilters.LastWrite;
// Watch for the wanted file
watcher.Filter = "data.json";
// Add event handlers.
watcher.Created += WhateverYouWantToDo;
}
You can check to see if the file data.json is finished being written to its output folder (Code from this answer):
private bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null) stream.Close();
}
//file is not locked return
return false;
}

C#: Calling a php script and beeing able to stop it

I am currently working on a C# Program which needs to call a local PHP script and write its output to a file. The problem is, that I need to be able to stop the execution of the script.
First, I tried to call cmd.exe and let cmd write the output to the file which worked fine. But I found out, that killing the cmd process does not stop the php cli.
So I tried to call php directly, redirect its output and write it from the C# code to a file. But here the problem seems to be, that the php cli does not terminate when the script is done. process.WaitForExit() does not return, even when I am sure that the script has been fully executed.
I cannot set a timeout to the WaitForExit(), because depending on the arguments, the script may take 3 minutes or eg. 10 hours.
I do not want to kill just a random php cli, there may be others currently running.
What is the best way to call a local php script from C#, writing its output to a file and beeing able to stop the execution?
Here is my current code:
// Create the process
var process = new System.Diagnostics.Process();
process.EnableRaisingEvents = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "php.exe";
// CreateExportScriptArgument returns something like "file.php arg1 arg2 ..."
process.StartInfo.Arguments = CreateExportScriptArgument(code, this.content, this.options);
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
// Start the process or cancel, if the process should not run
if (!this.isRunning) { return; }
this.currentProcess = process;
process.Start();
// Get the output
var output = process.StandardOutput;
// Wait for the process to finish
process.WaitForExit();
this.currentProcess = null;
To kill the process I am using:
// Mark as not running to prevent starting new
this.isRunning = false;
// Kill the process
if (this.currentProcess != null)
{
this.currentProcess.Kill();
}
Thanks for reading!
EDIT
That the cli does not return seems to be not reproducible. When I test a different script (without arguments) it works, probably its the script or the passing of the arguments.
Running my script from cmd works just fine, so the script should not be the problem
EDIT 2
When disabling RedirectStandardOutput, the cli quits. could it be, that I need to read the output, before the process finishes? Or does the process wait, when some kind of buffer is full?
EDIT 3: Problem solved
Thanks to VolkerK, I / we found a solution. The problem was, that WaitForExit() did not get called, when the output is not read (probably due to a full buffer in the standard output). My script wrote much output.
What works for me:
process.Start();
// Get the output
var output = process.StandardOutput;
// Read the input and write to file, live to avoid reading / writing to much at once
using (var file = new StreamWriter("path\\file", false, new UTF8Encoding()))
{
// Read each line
while (!process.HasExited)
{
file.WriteLine(output.ReadLine());
}
// Read the rest
file.Write(output.ReadToEnd());
// flush to file
file.Flush();
}
Since the problem was that the output buffer was full and therefore the php process stalled while waiting to send its output, asynchronously reading the output in the c# program is the solution.
class Program {
protected static /* yeah, yeah, it's only an example */ StringBuilder output;
static void Main(string[] args)
{
// Create the process
var process = new System.Diagnostics.Process();
process.EnableRaisingEvents = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "php.exe";
process.StartInfo.Arguments = "-f path\\test.php mu b 0 0 pgsql://user:pass#x.x.x.x:5432/nominatim";
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
output = new StringBuilder();
process.OutputDataReceived += process_OutputDataReceived;
// Start the process
process.Start();
process.BeginOutputReadLine();
// Wait for the process to finish
process.WaitForExit();
Console.WriteLine("test");
// <-- do something with Program.output here -->
Console.ReadKey();
}
static void process_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data)) {
// edit: oops the new-line/carriage-return characters are not "in" e.Data.....
// this _might_ be a problem depending on the actual output.
output.Append(e.Data);
output.Append(Environment.NewLine);
}
}
}
see also: https://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline%28v=vs.110%29.aspx

Process() get blocked even with WaitForExit()?

Working on a project. In the code, we need to run powershell script and then get its output. In order to do this, I use the Process():
private int RunProcess(string FileName, string Arguments, out string result)
{
int exitCode = -1;
result = string.Empty;
// 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 = FileName;
p.StartInfo.Arguments = Arguments;
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// Read the output stream first and then wait.
result = p.StandardOutput.ReadToEnd();
// Wait at most 10 minutes
p.WaitForExit(10 * 60 * 1000);
exitCode = p.ExitCode;
return exitCode;
}
and call it like this:
RunProcess("Powershell.exe", arguments, out sPSResult);
This works fine on most computers. However, on some, for some unknow reason, the RunProcess() never return, even we use p.WaitForExit(10 * 60 * 1000) .
Anyone knows why? or see this before? Is it because somewhere is blocked in the windows even WaitForExit is used?
Thanks
Are you sure your code even reaches WaitForExit and is not hanging on ReadToEnd? My guess it's getting stuck there because it can't read all of the output. Also for details on how to deal with reading standard and error outputs from child processes correctly, see:
ProcessStartInfo hanging on "WaitForExit"? Why?

Is there any cmd callback?

Obviously I can execute something with cmd console using Process.Start();
Is there any way to get output of that process? For example, I could have something like...
Process.Start("sample.bat");
... in my C# winforms application and sample.bat will contain something like:
echo sample loaded
First Question: is there any way to retrieve that sample loaded, after bat execution?
Second question: is there a way to use it without popped up console window?
There is an example of exactly how to do this in the Process documentation:
// 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 = "Write500Lines.exe";
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();
Yes, you can use
Process.Start(ProcessStartInfo)
There are a few ways to hook into I/O including ProcessStartInfo.RedirectStandardOutput available. You can use these overloads to read output from your batch files. You can also hook into the Exited event to know when execution is complete.
Use CreateNoWindow for no window.
Set process.StartInfo.RedirectStandardOutput to true and subscribe to process.OutputDataReceived
using (var process = new Process())
{
process.StartInfo = new ProcessStartInfo("exename");
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (s, ev) =>
{
string output = ev.Data;
};
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}

Command line program/app + C# class method

When working with a command line program, via a c# class method.
How do you determine if the commandline program was successfully executed and the operation it has performed is ok or has failed?
Also how do you get the screen commandline output into the c# class method?
You can use the Process class to execute a command line command.
The following code captures the standard output to output, and assigns the processes exit code to exitCode.
using (Process p = new Process())
{
p.StartInfo.FileName = exeName;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
int exitCode = p.ExitCode;
}
Something like:
Process mycommand = new Process();
mycommand.StartInfo.FileName = "myexe.exe";
mycommand.StartInfo.Arguments = "param1";
mycommand.StartInfo.UseShellExecute = false;
mycommand.StartInfo.RedirectStandardOutput = true;
mycommand.Start();
Console.WriteLine(mycommand.StandardOutput.ReadToEnd());
mycommand.WaitForExit();
You usually determine an exe's state wether the exit code is 0, but that is arguably down to the writer of the exe
I assume you're using the Process class to call the command line app.
You can find the exit code of the process using Process.ExitCode. You can redirect its standard output by setting ProcessStartInfo.RedirectStandardOutput before starting it, and then either using Process.StandardOutput or the Process.OutputDataReceived event.
Take a look at this questionenter link description here.
The additional information you might need is process.ExitCode to see if it was sucessful. Of course, the Main method of the console app must return an exit code when it is unsuccessful, which many do not.
For this, you use the Process.Start method. You can control how the process runs with the passed in ProcessStartInfo:
var myProcess = Process.Start(new ProcessStartInfo {
FileName = "process.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
});
if (!myProcess.WaitForExit(5000)) { // give it 5 seconds to exit
myProcess.Kill();
}
if (myProcess.ExitCode != 0) {
// error!
}
var output = myProcess.StandardOutput.ReadToEnd(); // access output

Categories

Resources