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. |