1 | unit RunProgramUnit;
|
---|
2 |
|
---|
3 | interface
|
---|
4 |
|
---|
5 | Uses
|
---|
6 | {$ifdef os2}
|
---|
7 | OS2Def, BseDos, Dos, BseErr,
|
---|
8 | {$endif}
|
---|
9 | {$ifdef win32}
|
---|
10 | Windows,
|
---|
11 | {$endif}
|
---|
12 | Forms,
|
---|
13 | ACLUtility;
|
---|
14 |
|
---|
15 | type
|
---|
16 | {$ifdef os2}
|
---|
17 | TProgramHandle = ULONG;
|
---|
18 | {$endif}
|
---|
19 | {$ifdef win32}
|
---|
20 | APIRET = DWORD;
|
---|
21 | TProgramHandle = THandle;
|
---|
22 | {$endif}
|
---|
23 |
|
---|
24 | // Runs a program in the given working directory.
|
---|
25 | // If PrintOutput is assigned, StdOut (and StdErr) will be piped to it.
|
---|
26 |
|
---|
27 | // If CheckTerminateCallback is assigned, it will be called regularly and if the
|
---|
28 | // process should be terminated, it should return true
|
---|
29 |
|
---|
30 | // Function returns 0 if the program was started OK.
|
---|
31 | // Otherwise an OS error code.
|
---|
32 | // ResultCode is set to 1 if the program did not start, otherwise
|
---|
33 | // the exit code of the process
|
---|
34 | Function RunProgram( ProgramName: string;
|
---|
35 | Parameters: string;
|
---|
36 | WorkingDir: string;
|
---|
37 | Var ResultCode: APIRET;
|
---|
38 | TerminateCheck: TTerminateCheck;
|
---|
39 | PrintOutput: TPrintOutput
|
---|
40 | ): APIRET;
|
---|
41 |
|
---|
42 | {$ifdef os2}
|
---|
43 | // Lauches given program and immediately returns.
|
---|
44 | // If successful in launching, returns 0 otherwise OS error code.
|
---|
45 | // If successful, ProgramHandle can be used to operate on the program.
|
---|
46 | Function LaunchProgram( ProgramName: string;
|
---|
47 | Parameters: string;
|
---|
48 | WorkingDir: string ): APIRET;
|
---|
49 | {$endif}
|
---|
50 |
|
---|
51 | implementation
|
---|
52 |
|
---|
53 | Uses
|
---|
54 | SysUtils;
|
---|
55 |
|
---|
56 | {$ifdef os2}
|
---|
57 | type
|
---|
58 | TermQResults=record
|
---|
59 | SessionID: WORD;
|
---|
60 | ResultCode: WORD;
|
---|
61 | end;
|
---|
62 | {$endif}
|
---|
63 |
|
---|
64 | Function RunProgram( ProgramName: string;
|
---|
65 | Parameters: string;
|
---|
66 | WorkingDir: string;
|
---|
67 | Var ResultCode: APIRET;
|
---|
68 | TerminateCheck: TTerminateCheck;
|
---|
69 | PrintOutput: TPrintOutput
|
---|
70 | ): APIRET;
|
---|
71 | {$ifdef win32}
|
---|
72 | Const
|
---|
73 | PipeBufferSize = 10000;
|
---|
74 | PipeName = '\\.\pipe\myoutputpipe';
|
---|
75 | Var
|
---|
76 | StartupInfo: TStartupInfo;
|
---|
77 | ProcessInfo: TProcessInformation;
|
---|
78 | rc: DWORD;
|
---|
79 | NameAndArgs: string;
|
---|
80 |
|
---|
81 | pipeServer: hFile;
|
---|
82 | buffer: array[ 0..PipeBufferSize ] of char;
|
---|
83 | bytesRead: DWORD;
|
---|
84 | SecAttrs: TSecurityAttributes;
|
---|
85 | pipeClient: hFile;
|
---|
86 | Begin
|
---|
87 |
|
---|
88 | pipeServer := 0;
|
---|
89 | pipeClient := 0;
|
---|
90 | try
|
---|
91 | NameAndArgs := ProgramName+' '+Parameters;
|
---|
92 |
|
---|
93 | // Initialize some variables to create a process
|
---|
94 | ZeroMemory( @StartupInfo, SizeOf( StartupInfo ) );
|
---|
95 |
|
---|
96 | StartupInfo.cb := SizeOf( StartupInfo );
|
---|
97 | StartupInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
|
---|
98 | StartupInfo.wShowWindow := SW_HIDE;
|
---|
99 |
|
---|
100 | if Assigned( PrintOutput ) then
|
---|
101 | begin
|
---|
102 | // Allow the started process to inherit our handles
|
---|
103 | FillChar( SecAttrs, SizeOf( SecAttrs ), #0);
|
---|
104 | SecAttrs.nLength := SizeOf(SecAttrs);
|
---|
105 | SecAttrs.lpSecurityDescriptor := nil;
|
---|
106 | SecAttrs.bInheritHandle := TRUE;
|
---|
107 |
|
---|
108 | // Create a pipe
|
---|
109 | pipeServer := CreateNamedPipe( PipeName,
|
---|
110 | PIPE_ACCESS_DUPLEX,
|
---|
111 | PIPE_TYPE_BYTE or PIPE_NOWAIT,
|
---|
112 | PIPE_UNLIMITED_INSTANCES,
|
---|
113 | PipeBufferSize, //out buffer
|
---|
114 | PipeBufferSize, // in buffer
|
---|
115 | 100, // default timeout (ms)
|
---|
116 | Addr( SecAttrs ) );
|
---|
117 |
|
---|
118 | // Get a handle to the other (client) end of the pipe
|
---|
119 | pipeClient := CreateFile( PipeName,
|
---|
120 | GENERIC_READ or GENERIC_WRITE,
|
---|
121 | FILE_SHARE_READ or FILE_SHARE_WRITE,
|
---|
122 | Addr( SecAttrs ),
|
---|
123 | OPEN_EXISTING,
|
---|
124 | FILE_ATTRIBUTE_NORMAL,
|
---|
125 | 0 );
|
---|
126 |
|
---|
127 | // setup the process to write into the other end
|
---|
128 | StartupInfo.hStdOutput := pipeClient;
|
---|
129 | StartupInfo.hStdError := pipeClient;
|
---|
130 | end;
|
---|
131 |
|
---|
132 | // Create the process
|
---|
133 | if not CreateProcess( Nil, // use next param for exe name
|
---|
134 | PChar( NameAndArgs ), // command line
|
---|
135 | Nil, // no security attributes
|
---|
136 | Nil, // no thread security attributes
|
---|
137 | True, // do inherit handles
|
---|
138 | CREATE_NEW_PROCESS_GROUP, // so we can send
|
---|
139 | // it Ctrl signals
|
---|
140 | Nil, // no new environment
|
---|
141 | PChar( WorkingDir ), // directory
|
---|
142 | StartupInfo,
|
---|
143 | ProcessInfo ) then
|
---|
144 | begin
|
---|
145 | Result := GetLastError;
|
---|
146 | PrintOutput( 'Could not run '+NameAndArgs );
|
---|
147 | PrintOutput( 'Windows error text: ' + GetAPIErrorString( Result ) );
|
---|
148 | ResultCode := 1;
|
---|
149 | exit;
|
---|
150 | end;
|
---|
151 |
|
---|
152 | while true do
|
---|
153 | begin
|
---|
154 | if Assigned( TerminateCheck ) then
|
---|
155 | if TerminateCheck then
|
---|
156 | begin
|
---|
157 | GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, ProcessInfo.dwProcessID );
|
---|
158 | ResultCode := 1;
|
---|
159 | Result := 0;
|
---|
160 | exit;
|
---|
161 | end;
|
---|
162 |
|
---|
163 | // Wait 1 second to see if it finishes...
|
---|
164 | rc := WaitForSingleObject( ProcessInfo.hProcess, 1000 );
|
---|
165 |
|
---|
166 | if Assigned( PrintOutput ) then
|
---|
167 | begin
|
---|
168 | repeat
|
---|
169 | // Read the output from our end of the pipe
|
---|
170 | ReadFile( pipeServer,
|
---|
171 | buffer,
|
---|
172 | PipeBufferSize,
|
---|
173 | bytesRead,
|
---|
174 | nil );
|
---|
175 | buffer[ bytesRead ] := #0; // null terminate
|
---|
176 | if bytesRead > 0 then
|
---|
177 | PrintOutput( buffer );
|
---|
178 |
|
---|
179 | until bytesRead = 0;
|
---|
180 |
|
---|
181 | end;
|
---|
182 |
|
---|
183 | if rc <> WAIT_TIMEOUT then
|
---|
184 | begin
|
---|
185 | // finished
|
---|
186 | GetExitCodeProcess( ProcessInfo.hProcess,
|
---|
187 | ResultCode );
|
---|
188 | Result := 0;
|
---|
189 | // terminate loop
|
---|
190 | exit;
|
---|
191 | end;
|
---|
192 | end;
|
---|
193 | finally
|
---|
194 | if pipeClient <> 0 then
|
---|
195 | CloseHandle( pipeClient );
|
---|
196 | if pipeServer <> 0 then
|
---|
197 | CloseHandle( pipeServer );
|
---|
198 | end;
|
---|
199 |
|
---|
200 | end;
|
---|
201 | {$endif}
|
---|
202 |
|
---|
203 | {$ifdef os2}
|
---|
204 | Var
|
---|
205 | psd: STARTDATA;
|
---|
206 | SessID: LONGWORD;
|
---|
207 | apid: LONGWORD;
|
---|
208 | PgmName: CSTRING;
|
---|
209 | ObjBuf: CSTRING;
|
---|
210 | rc: Integer;
|
---|
211 |
|
---|
212 | Args: CSTRING;
|
---|
213 |
|
---|
214 | TerminationQueue:HQueue;
|
---|
215 | QueueName: CSTRING;
|
---|
216 | QueueRequest: REQUESTDATA;
|
---|
217 | DataLength: ULONG;
|
---|
218 | DataAddress: ^TermQResults;
|
---|
219 | ElementCode: ULONG;
|
---|
220 | NoWait: BOOL;
|
---|
221 | ElemPriority: BYTE;
|
---|
222 | SemName: CString;
|
---|
223 | SemHandle: HEV;
|
---|
224 |
|
---|
225 | SemPostCount: ULONG;
|
---|
226 | Begin
|
---|
227 | QueueName := '\QUEUES\SIBYL_EXECUTE_TERMQ'
|
---|
228 | + IntToStr( GetCurrentProcessID );
|
---|
229 | rc := DosCreateQueue( TerminationQueue,
|
---|
230 | QUE_FIFO, // normal queue
|
---|
231 | QueueName );
|
---|
232 | if rc <> 0 then
|
---|
233 | begin
|
---|
234 | Result := rc;
|
---|
235 | exit;
|
---|
236 | end;
|
---|
237 |
|
---|
238 | psd.Length := SizeOf( psd );
|
---|
239 | psd.Related := SSF_RELATED_CHILD; // yes we want to know about it
|
---|
240 | psd.FgBg := SSF_FGBG_FORE; // run in foreground
|
---|
241 | psd.TraceOpt := SSF_TRACEOPT_NONE; // no tracing!
|
---|
242 |
|
---|
243 | PgmName := ProgramName; // copy to a cstring
|
---|
244 | psd.PgmName := @PgmName; // program
|
---|
245 | psd.PgmTitle := @PgmName; // window title
|
---|
246 |
|
---|
247 | Args :=parameters; // copy to a cstring
|
---|
248 | psd.PgmInputs := @Args; //arguments
|
---|
249 | psd.TermQ := @QueueName; // Termination Queue name
|
---|
250 | psd.Environment := NIL; // no special environment to pass
|
---|
251 | psd.InheritOpt := SSF_INHERTOPT_PARENT;
|
---|
252 | // use parent file handles
|
---|
253 | // AND (more importantly) parent's current drive and dir
|
---|
254 | psd.SessionType := SSF_TYPE_DEFAULT; // whatever the exe says
|
---|
255 | psd.IconFile := NIL; // no icon file
|
---|
256 | psd.PgmHandle := 0; // no program handle
|
---|
257 | psd.PgmControl := 0; // SSF_CONTROL_MINIMIZE; // run minimized
|
---|
258 | psd.InitXPos := 0; // position x
|
---|
259 | psd.InitYPos := 0; // position y
|
---|
260 | psd.Reserved := 0; // blah
|
---|
261 | psd.ObjectBuffer := @ObjBuf; // put errors here
|
---|
262 | psd.ObjectBuffLen := 100; // up to 100 chars
|
---|
263 |
|
---|
264 | rc := DosStartSession( psd, SessID, apid );
|
---|
265 |
|
---|
266 | if ( rc <> 0 )
|
---|
267 | // but we don't care if it just started in the background!
|
---|
268 | and ( rc <> ERROR_SMG_START_IN_BACKGROUND )
|
---|
269 | then
|
---|
270 | begin
|
---|
271 | Result := rc;
|
---|
272 | DosCloseQueue( TerminationQueue );
|
---|
273 | exit;
|
---|
274 | end;
|
---|
275 |
|
---|
276 | // DosCloseQueue( TerminationQueue );
|
---|
277 | // exit;
|
---|
278 |
|
---|
279 | // create a semaphore so we can check the queue!!
|
---|
280 | SemName := '\SEM32\SIBYL_EXECUTE_TERMQ'
|
---|
281 | + IntToStr( GetCurrentProcessID );
|
---|
282 | rc := DosCreateEventSem( SemName,
|
---|
283 | SemHandle,
|
---|
284 | 0,
|
---|
285 | FALSE );
|
---|
286 | if ( rc <> 0 ) then
|
---|
287 | begin
|
---|
288 | Result := rc;
|
---|
289 | DosCloseQueue( TerminationQueue );
|
---|
290 | exit;
|
---|
291 | end;
|
---|
292 |
|
---|
293 | // OK, we started it
|
---|
294 | Result := 0;
|
---|
295 |
|
---|
296 | ElementCode := 0; // get element at front of queue
|
---|
297 | NoWait := True; // don't wait for data: so we supply semaphore instead
|
---|
298 |
|
---|
299 | // assocaite the semaphore with the queue, don't remove item if it's already there
|
---|
300 | rc := DosPeekQueue( TerminationQueue,
|
---|
301 | QueueRequest,
|
---|
302 | DataLength,
|
---|
303 | DataAddress,
|
---|
304 | ElementCode,
|
---|
305 | NoWait,
|
---|
306 | ElemPriority,
|
---|
307 | SemHandle );
|
---|
308 |
|
---|
309 | repeat
|
---|
310 | rc := DosQueryEventSem( SemHandle, SemPostCount );
|
---|
311 |
|
---|
312 | // Handle PM messages (including to our own app!)
|
---|
313 | Application.ProcessMessages;
|
---|
314 |
|
---|
315 | // Give up CPU briefly so we don't appear too greedy when idle.
|
---|
316 | DosSleep( 1 );
|
---|
317 |
|
---|
318 | until SemPostCount > 0; // until semaphore is posted, indicating an item in the TermQ
|
---|
319 |
|
---|
320 | // The program has terminated.
|
---|
321 | // Now read the item
|
---|
322 | rc := DosReadQueue( TerminationQueue,
|
---|
323 | QueueRequest,
|
---|
324 | DataLength,
|
---|
325 | DataAddress,
|
---|
326 | ElementCode,
|
---|
327 | NoWait,
|
---|
328 | ElemPriority,
|
---|
329 | SemHandle );
|
---|
330 |
|
---|
331 | ResultCode := DataAddress^.ResultCode;
|
---|
332 | DosCloseQueue( TerminationQueue );
|
---|
333 | // Free the memory used by the queue element
|
---|
334 | DosFreeMem( DataAddress );
|
---|
335 |
|
---|
336 | // Close semaphore
|
---|
337 | DosCloseEventSem( SemHandle );
|
---|
338 |
|
---|
339 | End;
|
---|
340 |
|
---|
341 | {$endif}
|
---|
342 |
|
---|
343 | {$ifdef os2}
|
---|
344 | Function LaunchProgram( ProgramName: string;
|
---|
345 | Parameters: string;
|
---|
346 | WorkingDir: string ): APIRET;
|
---|
347 | Var
|
---|
348 | psd: STARTDATA;
|
---|
349 | apid: LONGWORD;
|
---|
350 | PgmName: CSTRING;
|
---|
351 | ObjBuf: CSTRING;
|
---|
352 | rc: Integer;
|
---|
353 |
|
---|
354 | Args: CSTRING;
|
---|
355 | ProgramHandle: TProgramHandle;
|
---|
356 | Begin
|
---|
357 | psd.Length := SizeOf( psd );
|
---|
358 | psd.Related := SSF_RELATED_INDEPENDENT; // don't want to know about it or own it
|
---|
359 | psd.FgBg := SSF_FGBG_FORE; // run in foreground
|
---|
360 | psd.TraceOpt := SSF_TRACEOPT_NONE; // no tracing!
|
---|
361 |
|
---|
362 | PgmName := ProgramName; // copy to a cstring
|
---|
363 | psd.PgmName := @PgmName; // program
|
---|
364 | psd.PgmTitle := @PgmName; // window title
|
---|
365 |
|
---|
366 | Args := parameters; // copy to a cstring
|
---|
367 | psd.PgmInputs := @Args; //arguments
|
---|
368 | psd.TermQ := nil; // Termination Queue name
|
---|
369 | psd.Environment := NIL; // no special environment to pass
|
---|
370 | psd.InheritOpt := SSF_INHERTOPT_PARENT;
|
---|
371 | // use parent file handles
|
---|
372 | // AND (more importantly) parent's current drive and dir
|
---|
373 | psd.SessionType := SSF_TYPE_DEFAULT; // whatever the exe says
|
---|
374 | psd.IconFile := NIL; // no icon file
|
---|
375 | psd.PgmHandle := 0; // no program handle
|
---|
376 | psd.PgmControl := 0; // SSF_CONTROL_MINIMIZE; // run minimized
|
---|
377 | psd.InitXPos := 0; // position x
|
---|
378 | psd.InitYPos := 0; // position y
|
---|
379 | psd.Reserved := 0; // blah
|
---|
380 | psd.ObjectBuffer := @ObjBuf; // put errors here
|
---|
381 | psd.ObjectBuffLen := 100; // up to 100 chars
|
---|
382 |
|
---|
383 | rc := DosStartSession( psd, ProgramHandle, apid );
|
---|
384 |
|
---|
385 | // we don't care if it just started in the background!
|
---|
386 | if rc = ERROR_SMG_START_IN_BACKGROUND then
|
---|
387 | rc := 0;
|
---|
388 |
|
---|
389 | result := rc;
|
---|
390 | end;
|
---|
391 | {$endif}
|
---|
392 |
|
---|
393 | end.
|
---|