Delphi og PHP (et dejligt ægteskab :-)

Tags:    delphi

User
Bruger #322 @ 12.08.01 23:51
Hejsa

Et lille spørgsmål der efterhånden har drevet mig til vanvid.

Jeg arbejder en del med php (kørende på en apache webserver på WIN95 -
forbavsende stabilt endda :-), og syntes at det kunne være praktisk, lynhurtigt at se
den html-kode som et php-script genererer, uden først at skulle skifte til browseren,
reloade browseren...osv, osv. PHP er en consol-applikation, og jeg ved, at hvis man
sender en fil direkte til php.exe, så starter applikationen, parser filen, viser outputten på
skærmen, og lukker... Altsammen inden jeg nå at se en flyvende fis. (Meget kan man sige,
men php "sparker for meget røv" mht. hastighed :-) .

Nu er mit spørgsmål så.... Hvordan får min applikation (lille notepad erstatning) fat i php´s
(consol-applikationen php.exe) output?????

Jeg håber at en eller anden :-) forstår hvad jeg mener, og har et guldkorn klar.

Bjarne





6 svar postet i denne tråd vises herunder
1 indlæg har modtaget i alt 2 karma
Sorter efter stemmer Sorter efter dato
<quote>Hej Kasper.
Tak for dit råd. Jeg har tænkt samme tanke, men så skal jeg stadigvæk reloade, osv. osv.

>>> Data.
Er det ikke hårdt at være så klog :-) Nå, men efter at kigget lidt i WIN32 hjælpefilen, tror jeg nok at jeg har fatte princippet....meeen. Der er forskel på teori og praksis. Jeg kan ikke få det til at virke. Hvis du har tid/lyst kunne jeg godt bruge en hurtig demo-app.

Bjarne</quote>


Ok, jeg har kørt flg. med held på en Win98. Det tager output fra Attrib kommandoen og smider i en memo. Start en ny App, smid en button og en memo på formen og flg. kode i Unit1:
<pre>
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
hChildStdoutRd: THandle;
hChildStdoutWr: THandle;
hChildStdoutRdDup: THandle;
hInputFile: THandle;
hSaveStdout: THandle;
function CreateChildProcess: boolean;
procedure ReadFromPipe;
procedure ErrorExit(code: string);
public
{ Public declarations }
procedure Main;
end;

var
Form1: TForm1;

implementation

const
BUFSIZE = 409600;

{$R *.DFM}

{ TForm1 }

function TForm1.CreateChildProcess: boolean;
var
piProcInfo: PROCESS_INFORMATION;
siStartInfo: STARTUPINFO;
begin
// Set up members of STARTUPINFO structure.
ZeroMemory( @siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb := sizeof(STARTUPINFO);
siStartInfo.dwFlags := STARTF_USESHOWWINDOW;
siStartInfo.wShowWindow := 0; //Betyde
// Create the child process.
chdir('c:\\');
result := CreateProcess(nil,
'attrib', // command line
nil, // process security attributes
nil, // primary thread security attributes

TRUE, // handles are inherited
0, // creation flags
nil, // use parent's environment
nil, // use parent's current directory
siStartInfo, // STARTUPINFO pointer
piProcInfo); // receives PROCESS_INFORMATION
//Wait for program to finish
WaitForSingleObject(piProcInfo.hProcess, INFINITE);
end;

procedure TForm1.ErrorExit(code: string);
begin
ShowMessage(code);
halt;
end;

procedure TForm1.Main;
var
saAttr: SECURITY_ATTRIBUTES;
fSuccess: boolean;
begin
// Set the bInheritHandle flag so pipe handles are inherited.


saAttr.nLength := sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle := TRUE;
saAttr.lpSecurityDescriptor := nil;
// Save the handle to the current STDOUT.
hSaveStdout := GetStdHandle(STD_OUTPUT_HANDLE);
// Create a pipe for the child process's STDOUT.
if (not CreatePipe(hChildStdoutRd, hChildStdoutWr, @saAttr, 0)) then
ErrorExit('Stdout pipe creation failed');
// Set a write handle to the pipe to be STDOUT.
if (not SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) then
ErrorExit('Redirecting STDOUT failed');
// Create noninheritable read handle and close the inheritable read
// handle.
fSuccess := DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
GetCurrentProcess(), @hChildStdoutRdDup , 0,
FALSE,
DUPLICATE_SAME_ACCESS);

if( not fSuccess ) then
ErrorExit('DuplicateHandle failed');

CloseHandle(hChildStdoutRd);
// Now create the child process.
if (not CreateChildProcess()) then
ErrorExit('Create process failed');
// Read from pipe that is the standard output for child process.
ReadFromPipe();
end;

procedure TForm1.ReadFromPipe;
var
dwRead, dwWritten: DWORD;
chBuf: array[0..BUFSIZE-1] of char;
hStdout: THandle;
begin
hStdout := GetStdHandle(STD_OUTPUT_HANDLE);
// Close the write end of the pipe before reading from the
// read end of the pipe.
if (not CloseHandle(hChildStdoutWr)) then
ErrorExit('Closing handle failed');
// Read output from the child process, and write to parent's STDOUT.
ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, dwRead,nil);
if dwRead > 0 then
memo1.lines.add(chBuf);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
main;
end;

end.
</pre>



Hej Bjarne...

>Nu er mit spørgsmål så.... Hvordan får min applikation (lille notepad erstatning) fat i php´s
(consol-applikationen php.exe) output?????

Det ved jeg ikke lige (faktisk ved jeg slet ikke om det er muligt), men der er jo en anden løsning! Du kan jo indbygge en lille HTML browser i dit program, der så i stedet for at kalde filen direkte, henter den gennem din Apache server. Det har jeg gjort i mine seneste programmer, og jeg synes faktisk at det er en rigtig god løsning!


--
Mvh.

Kasper (TSW)
Webmaster



<quote>Hejsa


Nu er mit spørgsmål så.... Hvordan får min applikation (lille notepad erstatning) fat i php´s
(consol-applikationen php.exe) output?????

Jeg håber at en eller anden :-) forstår hvad jeg mener, og har et guldkorn klar.

Bjarne

</quote>


Hej

Jammen det er sådan set simpelt nok (not!).

Når en console-application skal snakke med omverdenen sker det igennem såkaldte pipes. Alt input kommer via en pipe der hedder STDIN (Standard In) og Output til STDOUT. Det vi skal have gjort er, at have redirected STDOUT til en ny pipe der "hælder" dataene ind i dit program.

DISCLAIMER:
Følgende er testet under 98, jeg ved ikke om det virker under 95!


var
saAttr: SECURITY_ATTRIBUTES;
hSaveSTDOUT: THandle;
hChildStdoutRd: THandle;
hChildStdoutWr: THandle;
hChildStdoutRdDup: THandle;

saAttr.nLength := sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle := TRUE; //Sættes til true for at nedarve handles
saAttr.lpSecurityDescriptor := nil;

//Først gemmes den gamle STDOUT handle:

hSaveSTDOUT := GetStdHandle(STD_OUTPUT_HANDLE);

//Så skal vi have lavet en pipe

if (not CreatePipe(hChildStdoutRd, hChildStdoutWr, @saAttr, 0)) then
ErrorExit('Stdout pipe creation failed'); //en lille procedure der laver en showmessage efterfulgt af en halt.


//En pipe har et Write og et Read handle (så vi kan hælde ting i, og få dem ud igen. Sæt STDOUT til Writehandle (så output kommer ned i pipen

if (not SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) then
ErrorExit('Redirecting STDOUT failed');

//Nu skal vi have lavet en non-inheritable readhandle og nedlagt den inheritable handle
fSuccess := DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
GetCurrentProcess(), @hChildStdoutRdDup , 0,
FALSE,
DUPLICATE_SAME_ACCESS);

if( not fSuccess ) then
ErrorExit('DuplicateHandle failed');

CloseHandle(hChildStdoutRd);


Nu har vi sat en pipe op. Nu skal vi have startet vores console app, og sørge for at den arver vores Write-handle, så den kan hælde data i røret

var
piProcInfo: PROCESS_INFORMATION;
siStartInfo: STARTUPINFO;

// Set up members of STARTUPINFO structure.

ZeroMemory( @siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb := sizeof(STARTUPINFO);

//De næste to linier gør, at der ikke kommer noget console-vindue
siStartInfo.dwFlags := STARTF_USESHOWWINDOW;
siStartInfo.wShowWindow := 0;

// Create the child process.

result := CreateProcess(nil,
's3270', // command line (det program jeg skulle bruge hed s3270)
nil, // process security attributes
nil, // primary thread security attributes

TRUE, // handles are inherited
0, // creation flags
nil, // use parent's environment
nil, // use parent's current directory
siStartInfo, // STARTUPINFO pointer
piProcInfo); // receives PROCESS_INFORMATION


Nu skal vi så have læst indholdet af pipen.


var
dwRead, dwWritten: DWORD;
chBuf: array[0..BUFSIZE-1] of char;
hStdout: THandle;
begin

hStdout := GetStdHandle(STD_OUTPUT_HANDLE);

// Close the write end of the pipe before reading from the
// read end of the pipe.

if (not CloseHandle(hChildStdoutWr)) then
ErrorExit('Closing handle failed');

// Read output from the child process, and write to parent's STDOUT.

ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, dwRead,nil);

if dwRead > 0 then
showmessage(chBuf); //chBuf indeholder output. Vis i en message box.

end;



Og det var så vist det. Jeg håber det giver en smule mening. Hvis ikke skal jeg prøve at stykke en demo sammen til dig.

/Data




Husk i øvrigt at vente med at læse fra din pipe til programmet er afsluttet. Brug WaitForSingleObject til at vente med

/Data



User
Bruger #322 @ 13.08.01 23:05
Hej Kasper.
Tak for dit råd. Jeg har tænkt samme tanke, men så skal jeg stadigvæk reloade, osv. osv.

>>> Data.
Er det ikke hårdt at være så klog :-) Nå, men efter at kigget lidt i WIN32 hjælpefilen, tror jeg nok at jeg har fatte princippet....meeen. Der er forskel på teori og praksis. Jeg kan ikke få det til at virke. Hvis du har tid/lyst kunne jeg godt bruge en hurtig demo-app.

Bjarne



Det var dog irriterende at formatering fjernes. Jeg forsøgte ellers at smide pre /pre tags omkring, men det er den tilsyneladende ret kold overfor. Hvordan får man koden til at se pæn ud her i forumet?

/data



t