Subversion Repositories fastphp

Compare Revisions

Regard whitespace Rev 26 → Rev 27

/trunk/RunPHP.pas
0,0 → 1,159
unit RunPHP;
 
interface
 
uses
SysUtils, Forms, Classes, Windows;
 
type
TInputRequestCallback = function: string of object;
TOutputNotifyCallback = procedure(const output: string) of object;
TRunCodeExplorer = class(TThread)
private
FInputRequestCallback: TInputRequestCallback;
FOutputNotifyCallback: TOutputNotifyCallback;
FInputWaiting: string;
FOutputWaiting: string;
protected
procedure CallInputRequestCallback;
procedure CallOutputNotifyCallback;
public
PhpExe: string;
PhpFile: string;
WorkDir: string;
property InputRequestCallback: TInputRequestCallback read FInputRequestCallback write FInputRequestCallback;
property OutputNotifyCallback: TOutputNotifyCallback read FOutputNotifyCallback write FOutputNotifyCallback;
procedure Execute; override;
end;
 
implementation
 
procedure TRunCodeExplorer.Execute;
 
function ProcessRunning(PI: TProcessInformation): boolean; inline;
var
exitcode: Cardinal;
begin
result := GetExitCodeProcess(PI.hProcess, exitcode) and (exitcode = STILL_ACTIVE);
end;
 
var
SA: TSecurityAttributes;
SI: TStartupInfo;
PI: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
StdInPipeRead, StdInPipeWrite: THandle;
WasOK: Boolean;
Buffer: array[0..255] of AnsiChar;
BytesRead, BytesWritten: Cardinal;
WorkDir: string;
Handle: Boolean;
testString: AnsiString;
CommandLine: string;
result: string;
begin
if Self.WorkDir = '' then
WorkDir := ExtractFilePath(ParamStr(0))
else
WorkDir := Self.WorkDir;
 
if not FileExists(Self.PhpExe) then exit;
if not FileExists(Self.PhpFile) then exit;
 
CommandLine := '"'+Self.PhpExe+'" "'+Self.PhpFile+'"';
 
Result := '';
with SA do begin
nLength := SizeOf(SA);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
CreatePipe(StdInPipeRead, StdInPipeWrite, @SA, 0);
try
with SI do
begin
FillChar(SI, SizeOf(SI), 0);
cb := SizeOf(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := StdInPipeRead;
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
 
Handle := CreateProcess(nil, PChar('cmd.exe /C "' + CommandLine + '"'),
nil, nil, True, 0, nil, PChar(WorkDir), SI, PI);
CloseHandle(StdOutPipeWrite);
 
sleep(100);
 
if Handle then
try
while not Self.Terminated and ProcessRunning(PI) do
begin
{$REGION 'Get input from mainthread'}
if Assigned(FInputRequestCallback) then
begin
Synchronize(CallInputRequestCallback);
 
// Attention: This call will block if the process exited
WriteFile(StdInPipeWrite, FInputWaiting[1], Length(FInputWaiting), BytesWritten, nil);
end;
{$ENDREGION}
 
{$REGION 'Terminate input sequence'}
testString := #13#10#1#2#3#4#5#6#7#8#13#10;
WriteFile(StdInPipeWrite, testString[1], Length(testString), BytesWritten, nil);
{$ENDREGION}
 
{$REGION 'Gather output'}
result := '';
repeat
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
if BytesRead > 0 then
begin
Buffer[BytesRead] := #0;
Result := Result + Buffer;
if Pos(#1#2#3#4#5#6#7#8, result) >= 1 then
begin
result := StringReplace(result, #1#2#3#4#5#6#7#8, '', []);
break;
end;
end;
until not WasOK or (BytesRead = 0) or Self.Terminated or not ProcessRunning(PI);
{$ENDREGION}
 
{$REGION 'Notify main thread about output'}
if Assigned(FOutputNotifyCallback) and not Self.Terminated and ProcessRunning(PI) then
begin
FOutputWaiting := result;
Synchronize(CallOutputNotifyCallback);
end;
{$ENDREGION}
end;
 
CloseHandle(StdInPipeWrite);
TerminateProcess(pi.hProcess, 0); // TODO: for some reason, after closing the editor, php.exe keeps running in the task list
WaitForSingleObject(PI.hProcess, INFINITE);
finally
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
finally
CloseHandle(StdOutPipeRead);
CloseHandle(StdInPipeRead);
end;
end;
 
procedure TRunCodeExplorer.CallInputRequestCallback;
begin
FInputWaiting := FInputRequestCallback;
end;
 
procedure TRunCodeExplorer.CallOutputNotifyCallback;
begin
FOutputNotifyCallback(FOutputWaiting);
end;
 
end.