Subversion Repositories fastphp

Rev

Rev 40 | Rev 42 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit RunPHP;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, Forms, Classes, Windows;
  7.  
  8. type
  9.   TInputRequestCallback = function(var data: AnsiString): boolean of object;
  10.   TOutputNotifyCallback = function(const output: AnsiString): boolean of object;
  11.   TRunCodeExplorer = class(TThread)
  12.   private
  13.     FInputRequestCallback: TInputRequestCallback;
  14.     FOutputNotifyCallback: TOutputNotifyCallback;
  15.     FInputWaiting: AnsiString;
  16.     FInputWasSuccessful: boolean;
  17.     FOutputWaiting: AnsiString;
  18.     FOutputWasSuccessful: boolean;
  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;
  55.   Output, OutputLastCache: string;
  56. const
  57.   SIGNAL_END_OF_TRANSMISSION = #1#2#3#4#5#6#7#8;
  58.   SIGNAL_TERMINATE           = #8#7#6#5#4#3#2#1;
  59. begin
  60.   if Self.WorkDir = '' then
  61.     WorkDir := ExtractFilePath(ParamStr(0))
  62.   else
  63.     WorkDir := Self.WorkDir;
  64.  
  65.   if not FileExists(Self.PhpExe) then exit;
  66.   if not FileExists(Self.PhpFile) then exit;
  67.  
  68.   CommandLine := '"'+Self.PhpExe+'" "'+Self.PhpFile+'"';
  69.  
  70.   Output := '';
  71.   OutputLastCache := '';
  72.   with SA do begin
  73.     nLength := SizeOf(SA);
  74.     bInheritHandle := True;
  75.     lpSecurityDescriptor := nil;
  76.   end;
  77.   CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
  78.   CreatePipe(StdInPipeRead, StdInPipeWrite, @SA, 0);
  79.   try
  80.     with SI do
  81.     begin
  82.       FillChar(SI, SizeOf(SI), 0);
  83.       cb := SizeOf(SI);
  84.       dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
  85.       wShowWindow := SW_HIDE;
  86.       hStdInput := StdInPipeRead;
  87.       hStdOutput := StdOutPipeWrite;
  88.       hStdError := StdOutPipeWrite;
  89.     end;
  90.  
  91.     Handle := CreateProcess(nil, PChar('cmd.exe /C "' + CommandLine + '"'),
  92.                             nil, nil, True, 0, nil, PChar(WorkDir), SI, PI);
  93.     CloseHandle(StdOutPipeWrite);
  94.  
  95.     sleep(100);
  96.  
  97.     if Handle then
  98.       try
  99.         while not Self.Terminated and ProcessRunning(PI) do
  100.         begin
  101.           {$REGION 'Get input from mainthread'}
  102.           if Assigned(FInputRequestCallback) then
  103.           begin
  104.             Synchronize(CallInputRequestCallback);
  105.  
  106.             if not FInputWasSuccessful then
  107.             begin
  108.               Sleep(100);
  109.               continue;
  110.             end;
  111.  
  112.             // Attention: This call will block if the process exited
  113.             WriteFile(StdInPipeWrite, FInputWaiting[1], Length(FInputWaiting), BytesWritten, nil);
  114.           end;
  115.           {$ENDREGION}
  116.  
  117.           {$REGION 'Terminate input sequence'}
  118.           testString := #13#10+SIGNAL_END_OF_TRANSMISSION+#13#10;
  119.           WriteFile(StdInPipeWrite, testString[1], Length(testString), BytesWritten, nil);
  120.           {$ENDREGION}
  121.  
  122.           {$REGION 'Gather output'}
  123.           Output := '';
  124.           repeat
  125.             WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
  126.             if BytesRead > 0 then
  127.             begin
  128.               Buffer[BytesRead] := #0;
  129.               Output := Output + Buffer;
  130.               if Pos(SIGNAL_END_OF_TRANSMISSION, Output) >= 1 then
  131.               begin
  132.                 Output := StringReplace(Output, SIGNAL_END_OF_TRANSMISSION, '', []);
  133.                 break;
  134.               end;
  135.             end;
  136.           until not WasOK or (BytesRead = 0) or Self.Terminated or not ProcessRunning(PI);
  137.           {$ENDREGION}
  138.  
  139.           {$REGION 'Notify main thread about output'}
  140.           if Assigned(FOutputNotifyCallback) and (OutputLastCache <> Output) and not Self.Terminated and ProcessRunning(PI) then
  141.           begin
  142.             FOutputWaiting := Output;
  143.             Synchronize(CallOutputNotifyCallback);
  144.             if FOutputWasSuccessful then
  145.             begin
  146.               OutputLastCache := Output;
  147.             end;
  148.           end;
  149.           {$ENDREGION}
  150.         end;
  151.  
  152.         // Signal the code explorer to terminate
  153.         testString := #13#10+SIGNAL_TERMINATE+#13#10;
  154.         WriteFile(StdInPipeWrite, testString[1], Length(testString), BytesWritten, nil);
  155.         WaitForSingleObject(PI.hProcess, INFINITE);
  156.  
  157.         CloseHandle(StdInPipeWrite);
  158.       finally
  159.         CloseHandle(PI.hThread);
  160.         CloseHandle(PI.hProcess);
  161.       end;
  162.   finally
  163.     CloseHandle(StdOutPipeRead);
  164.     CloseHandle(StdInPipeRead);
  165.   end;
  166. end;
  167.  
  168. {synchron} procedure TRunCodeExplorer.CallInputRequestCallback;
  169. begin
  170.   FInputWasSuccessful := FInputRequestCallback(FInputWaiting);
  171. end;
  172.  
  173. {synchron} procedure TRunCodeExplorer.CallOutputNotifyCallback;
  174. begin
  175.   FOutputWasSuccessful := FOutputNotifyCallback(FOutputWaiting);
  176. end;
  177.  
  178. end.
  179.