Subversion Repositories fastphp

Rev

Rev 36 | Rev 41 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
27 daniel-mar 1
unit RunPHP;
2
 
3
interface
4
 
5
uses
6
  SysUtils, Forms, Classes, Windows;
7
 
8
type
40 daniel-mar 9
  TInputRequestCallback = function(var data: AnsiString): boolean of object;
10
  TOutputNotifyCallback = function(const output: AnsiString): boolean of object;
27 daniel-mar 11
  TRunCodeExplorer = class(TThread)
12
  private
13
    FInputRequestCallback: TInputRequestCallback;
14
    FOutputNotifyCallback: TOutputNotifyCallback;
31 daniel-mar 15
    FInputWaiting: AnsiString;
40 daniel-mar 16
    FInputWasSuccessful: boolean;
31 daniel-mar 17
    FOutputWaiting: AnsiString;
40 daniel-mar 18
    FOutputWasSuccessful: boolean;
27 daniel-mar 19
  protected
20
    procedure CallInputRequestCallback;
21
    procedure CallOutputNotifyCallback;
22
  public
23
    PhpExe: string;
24
    PhpFile: string;
25
    WorkDir: string;
26
    property InputRequestCallback: TInputRequestCallback read FInputRequestCallback write FInputRequestCallback;
27
    property OutputNotifyCallback: TOutputNotifyCallback read FOutputNotifyCallback write FOutputNotifyCallback;
28
    procedure Execute; override;
29
  end;
30
 
31
implementation
32
 
33
procedure TRunCodeExplorer.Execute;
34
 
35
  function ProcessRunning(PI: TProcessInformation): boolean; inline;
36
  var
37
    exitcode: Cardinal;
38
  begin
39
    result := GetExitCodeProcess(PI.hProcess, exitcode) and (exitcode = STILL_ACTIVE);
40
  end;
41
 
42
var
43
  SA: TSecurityAttributes;
44
  SI: TStartupInfo;
45
  PI: TProcessInformation;
46
  StdOutPipeRead, StdOutPipeWrite: THandle;
47
  StdInPipeRead, StdInPipeWrite: THandle;
48
  WasOK: Boolean;
49
  Buffer: array[0..255] of AnsiChar;
50
  BytesRead, BytesWritten: Cardinal;
51
  WorkDir: string;
52
  Handle: Boolean;
53
  testString: AnsiString;
54
  CommandLine: string;
40 daniel-mar 55
  Output, OutputLastCache: string;
27 daniel-mar 56
begin
57
  if Self.WorkDir = '' then
58
    WorkDir := ExtractFilePath(ParamStr(0))
59
  else
60
    WorkDir := Self.WorkDir;
61
 
62
  if not FileExists(Self.PhpExe) then exit;
63
  if not FileExists(Self.PhpFile) then exit;
64
 
65
  CommandLine := '"'+Self.PhpExe+'" "'+Self.PhpFile+'"';
66
 
40 daniel-mar 67
  Output := '';
68
  OutputLastCache := '';
27 daniel-mar 69
  with SA do begin
70
    nLength := SizeOf(SA);
71
    bInheritHandle := True;
72
    lpSecurityDescriptor := nil;
73
  end;
74
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
75
  CreatePipe(StdInPipeRead, StdInPipeWrite, @SA, 0);
76
  try
77
    with SI do
78
    begin
79
      FillChar(SI, SizeOf(SI), 0);
80
      cb := SizeOf(SI);
81
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
82
      wShowWindow := SW_HIDE;
83
      hStdInput := StdInPipeRead;
84
      hStdOutput := StdOutPipeWrite;
85
      hStdError := StdOutPipeWrite;
86
    end;
87
 
88
    Handle := CreateProcess(nil, PChar('cmd.exe /C "' + CommandLine + '"'),
89
                            nil, nil, True, 0, nil, PChar(WorkDir), SI, PI);
90
    CloseHandle(StdOutPipeWrite);
91
 
92
    sleep(100);
93
 
94
    if Handle then
95
      try
96
        while not Self.Terminated and ProcessRunning(PI) do
97
        begin
98
          {$REGION 'Get input from mainthread'}
99
          if Assigned(FInputRequestCallback) then
100
          begin
101
            Synchronize(CallInputRequestCallback);
102
 
40 daniel-mar 103
            if not FInputWasSuccessful then
104
            begin
105
              Sleep(100);
106
              continue;
107
            end;
108
 
27 daniel-mar 109
            // Attention: This call will block if the process exited
110
            WriteFile(StdInPipeWrite, FInputWaiting[1], Length(FInputWaiting), BytesWritten, nil);
111
          end;
112
          {$ENDREGION}
113
 
114
          {$REGION 'Terminate input sequence'}
115
          testString := #13#10#1#2#3#4#5#6#7#8#13#10;
116
          WriteFile(StdInPipeWrite, testString[1], Length(testString), BytesWritten, nil);
117
          {$ENDREGION}
118
 
119
          {$REGION 'Gather output'}
40 daniel-mar 120
          Output := '';
27 daniel-mar 121
          repeat
122
            WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
123
            if BytesRead > 0 then
124
            begin
125
              Buffer[BytesRead] := #0;
40 daniel-mar 126
              Output := Output + Buffer;
127
              if Pos(#1#2#3#4#5#6#7#8, Output) >= 1 then
27 daniel-mar 128
              begin
40 daniel-mar 129
                Output := StringReplace(Output, #1#2#3#4#5#6#7#8, '', []);
27 daniel-mar 130
                break;
131
              end;
132
            end;
133
          until not WasOK or (BytesRead = 0) or Self.Terminated or not ProcessRunning(PI);
134
          {$ENDREGION}
135
 
136
          {$REGION 'Notify main thread about output'}
40 daniel-mar 137
          if Assigned(FOutputNotifyCallback) and (OutputLastCache <> Output) and not Self.Terminated and ProcessRunning(PI) then
27 daniel-mar 138
          begin
40 daniel-mar 139
            FOutputWaiting := Output;
27 daniel-mar 140
            Synchronize(CallOutputNotifyCallback);
40 daniel-mar 141
            if FOutputWasSuccessful then
142
            begin
143
              OutputLastCache := Output;
144
            end;
27 daniel-mar 145
          end;
146
          {$ENDREGION}
147
        end;
148
 
149
        CloseHandle(StdInPipeWrite);
150
        TerminateProcess(pi.hProcess, 0);  // TODO: for some reason, after closing the editor, php.exe keeps running in the task list
151
        WaitForSingleObject(PI.hProcess, INFINITE);
152
      finally
153
        CloseHandle(PI.hThread);
154
        CloseHandle(PI.hProcess);
155
      end;
156
  finally
157
    CloseHandle(StdOutPipeRead);
158
    CloseHandle(StdInPipeRead);
159
  end;
160
end;
161
 
40 daniel-mar 162
{synchron} procedure TRunCodeExplorer.CallInputRequestCallback;
27 daniel-mar 163
begin
40 daniel-mar 164
  FInputWasSuccessful := FInputRequestCallback(FInputWaiting);
27 daniel-mar 165
end;
166
 
40 daniel-mar 167
{synchron} procedure TRunCodeExplorer.CallOutputNotifyCallback;
27 daniel-mar 168
begin
40 daniel-mar 169
  FOutputWasSuccessful := FOutputNotifyCallback(FOutputWaiting);
27 daniel-mar 170
end;
171
 
172
end.