Rev 70 | 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; |
||
40 | daniel-mar | 54 | Output, OutputLastCache: string; |
41 | daniel-mar | 55 | const |
56 | SIGNAL_END_OF_TRANSMISSION = #1#2#3#4#5#6#7#8; |
||
57 | SIGNAL_TERMINATE = #8#7#6#5#4#3#2#1; |
||
27 | daniel-mar | 58 | begin |
59 | if Self.WorkDir = '' then |
||
60 | WorkDir := ExtractFilePath(ParamStr(0)) |
||
61 | else |
||
62 | WorkDir := Self.WorkDir; |
||
63 | |||
64 | if not FileExists(Self.PhpExe) then exit; |
||
65 | if not FileExists(Self.PhpFile) then exit; |
||
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 | |||
42 | daniel-mar | 88 | Handle := CreateProcess(nil, PChar('"'+Self.PhpExe+'" -f "'+Self.PhpFile+'"'), |
27 | daniel-mar | 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'} |
||
41 | daniel-mar | 115 | testString := #13#10+SIGNAL_END_OF_TRANSMISSION+#13#10; |
27 | daniel-mar | 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; |
||
91 | daniel-mar | 126 | Output := Output + string(Buffer); |
41 | daniel-mar | 127 | if Pos(SIGNAL_END_OF_TRANSMISSION, Output) >= 1 then |
27 | daniel-mar | 128 | begin |
41 | daniel-mar | 129 | Output := StringReplace(Output, SIGNAL_END_OF_TRANSMISSION, '', []); |
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 | |||
41 | daniel-mar | 149 | // Signal the code explorer to terminate |
70 | daniel-mar | 150 | (* |
41 | daniel-mar | 151 | testString := #13#10+SIGNAL_TERMINATE+#13#10; |
152 | WriteFile(StdInPipeWrite, testString[1], Length(testString), BytesWritten, nil); |
||
153 | WaitForSingleObject(PI.hProcess, INFINITE); |
||
70 | daniel-mar | 154 | *) |
155 | TerminateProcess(Pi.hProcess, 0); |
||
41 | daniel-mar | 156 | |
27 | daniel-mar | 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 | |||
40 | daniel-mar | 168 | {synchron} procedure TRunCodeExplorer.CallInputRequestCallback; |
27 | daniel-mar | 169 | begin |
40 | daniel-mar | 170 | FInputWasSuccessful := FInputRequestCallback(FInputWaiting); |
27 | daniel-mar | 171 | end; |
172 | |||
40 | daniel-mar | 173 | {synchron} procedure TRunCodeExplorer.CallOutputNotifyCallback; |
27 | daniel-mar | 174 | begin |
40 | daniel-mar | 175 | FOutputWasSuccessful := FOutputNotifyCallback(FOutputWaiting); |
27 | daniel-mar | 176 | end; |
177 | |||
178 | end. |