2
0

process.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {$mode objfpc}
  11. {$h+}
  12. unit process;
  13. interface
  14. Uses Classes,
  15. pipes,
  16. SysUtils;
  17. Type
  18. TProcessOption = (poRunSuspended,poWaitOnExit,
  19. poUsePipes,poStderrToOutPut,
  20. poNoConsole,poNewConsole,
  21. poDefaultErrorMode,poNewProcessGroup,
  22. poDebugProcess,poDebugOnlyThisProcess);
  23. TShowWindowOptions = (swoNone,swoHIDE,swoMaximize,swoMinimize,swoRestore,swoShow,
  24. swoShowDefault,swoShowMaximized,swoShowMinimized,
  25. swoshowMinNOActive,swoShowNA,swoShowNoActivate,swoShowNormal);
  26. TStartupOption = (suoUseShowWindow,suoUseSize,suoUsePosition,
  27. suoUseCountChars,suoUseFillAttribute);
  28. TProcessPriority = (ppHigh,ppIdle,ppNormal,ppRealTime);
  29. TProcessOptions = set of TProcessOption;
  30. TStartupOptions = set of TStartupOption;
  31. Type
  32. {$ifdef UNIX}
  33. TProcessForkEvent = procedure;
  34. {$endif UNIX}
  35. { TProcess }
  36. TProcess = Class (TComponent)
  37. Private
  38. FProcessOptions : TProcessOptions;
  39. FStartupOptions : TStartupOptions;
  40. FProcessID : Integer;
  41. FTerminalProgram: String;
  42. FThreadID : Integer;
  43. FProcessHandle : Thandle;
  44. FThreadHandle : Thandle;
  45. FFillAttribute : Cardinal;
  46. FApplicationName : string;
  47. FConsoleTitle : String;
  48. FCommandLine : String;
  49. FCurrentDirectory : String;
  50. FDesktop : String;
  51. FEnvironment : Tstrings;
  52. FExecutable : String;
  53. FParameters : TStrings;
  54. FShowWindow : TShowWindowOptions;
  55. FInherithandles : Boolean;
  56. {$ifdef UNIX}
  57. FForkEvent : TProcessForkEvent;
  58. {$endif UNIX}
  59. FProcessPriority : TProcessPriority;
  60. dwXCountchars,
  61. dwXSize,
  62. dwYsize,
  63. dwx,
  64. dwYcountChars,
  65. dwy : Cardinal;
  66. FXTermProgram: String;
  67. FPipeBufferSize : cardinal;
  68. Procedure FreeStreams;
  69. Function GetExitStatus : Integer;
  70. Function GetRunning : Boolean;
  71. Function GetWindowRect : TRect;
  72. procedure SetCommandLine(const AValue: String);
  73. procedure SetParameters(const AValue: TStrings);
  74. Procedure SetWindowRect (Value : TRect);
  75. Procedure SetShowWindow (Value : TShowWindowOptions);
  76. Procedure SetWindowColumns (Value : Cardinal);
  77. Procedure SetWindowHeight (Value : Cardinal);
  78. Procedure SetWindowLeft (Value : Cardinal);
  79. Procedure SetWindowRows (Value : Cardinal);
  80. Procedure SetWindowTop (Value : Cardinal);
  81. Procedure SetWindowWidth (Value : Cardinal);
  82. procedure SetApplicationName(const Value: String);
  83. procedure SetProcessOptions(const Value: TProcessOptions);
  84. procedure SetActive(const Value: Boolean);
  85. procedure SetEnvironment(const Value: TStrings);
  86. Procedure ConvertCommandLine;
  87. function PeekExitStatus: Boolean;
  88. Protected
  89. FRunning : Boolean;
  90. FExitCode : Cardinal;
  91. FInputStream : TOutputPipeStream;
  92. FOutputStream : TInputPipeStream;
  93. FStderrStream : TInputPipeStream;
  94. procedure CloseProcessHandles; virtual;
  95. Procedure CreateStreams(InHandle,OutHandle,ErrHandle : Longint);virtual;
  96. procedure FreeStream(var AStream: THandleStream);
  97. procedure Loaded; override;
  98. Public
  99. Constructor Create (AOwner : TComponent);override;
  100. Destructor Destroy; override;
  101. Procedure Execute; virtual;
  102. procedure CloseInput; virtual;
  103. procedure CloseOutput; virtual;
  104. procedure CloseStderr; virtual;
  105. Function Resume : Integer; virtual;
  106. Function Suspend : Integer; virtual;
  107. Function Terminate (AExitCode : Integer): Boolean; virtual;
  108. Function WaitOnExit : Boolean;
  109. Property WindowRect : Trect Read GetWindowRect Write SetWindowRect;
  110. Property Handle : THandle Read FProcessHandle;
  111. Property ProcessHandle : THandle Read FProcessHandle;
  112. Property ThreadHandle : THandle Read FThreadHandle;
  113. Property ProcessID : Integer Read FProcessID;
  114. Property ThreadID : Integer Read FThreadID;
  115. Property Input : TOutputPipeStream Read FInputStream;
  116. Property Output : TInputPipeStream Read FOutputStream;
  117. Property Stderr : TinputPipeStream Read FStderrStream;
  118. Property ExitStatus : Integer Read GetExitStatus;
  119. Property InheritHandles : Boolean Read FInheritHandles Write FInheritHandles;
  120. {$ifdef UNIX}
  121. property OnForkEvent : TProcessForkEvent Read FForkEvent Write FForkEvent;
  122. {$endif UNIX}
  123. Published
  124. property PipeBufferSize : cardinal read FPipeBufferSize write FPipeBufferSize default 1024;
  125. Property Active : Boolean Read GetRunning Write SetActive;
  126. Property ApplicationName : String Read FApplicationName Write SetApplicationName; deprecated;
  127. Property CommandLine : String Read FCommandLine Write SetCommandLine ; deprecated;
  128. Property Executable : String Read FExecutable Write FExecutable;
  129. Property Parameters : TStrings Read FParameters Write SetParameters;
  130. Property ConsoleTitle : String Read FConsoleTitle Write FConsoleTitle;
  131. Property CurrentDirectory : String Read FCurrentDirectory Write FCurrentDirectory;
  132. Property Desktop : String Read FDesktop Write FDesktop;
  133. Property Environment : TStrings Read FEnvironment Write SetEnvironment;
  134. Property Options : TProcessOptions Read FProcessOptions Write SetProcessOptions;
  135. Property Priority : TProcessPriority Read FProcessPriority Write FProcessPriority;
  136. Property StartupOptions : TStartupOptions Read FStartupOptions Write FStartupOptions;
  137. Property Running : Boolean Read GetRunning;
  138. Property ShowWindow : TShowWindowOptions Read FShowWindow Write SetShowWindow;
  139. Property WindowColumns : Cardinal Read dwXCountChars Write SetWindowColumns;
  140. Property WindowHeight : Cardinal Read dwYSize Write SetWindowHeight;
  141. Property WindowLeft : Cardinal Read dwX Write SetWindowLeft;
  142. Property WindowRows : Cardinal Read dwYCountChars Write SetWindowRows;
  143. Property WindowTop : Cardinal Read dwY Write SetWindowTop ;
  144. Property WindowWidth : Cardinal Read dwXSize Write SetWindowWidth;
  145. Property FillAttribute : Cardinal read FFillAttribute Write FFillAttribute;
  146. Property XTermProgram : String Read FXTermProgram Write FXTermProgram;
  147. end;
  148. EProcess = Class(Exception);
  149. Procedure CommandToList(S : String; List : TStrings);
  150. {$ifdef unix}
  151. Var
  152. TryTerminals : Array of string;
  153. XTermProgram : String;
  154. Function DetectXTerm : String;
  155. {$endif unix}
  156. function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string;var exitstatus:integer):integer;
  157. function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string):boolean;
  158. function RunCommandInDir(const curdir,cmdline:string;var outputstring:string):boolean; deprecated;
  159. function RunCommand(const exename:string;const commands:array of string;var outputstring:string):boolean;
  160. function RunCommand(const cmdline:string;var outputstring:string):boolean; deprecated;
  161. implementation
  162. {$i process.inc}
  163. Procedure CommandToList(S : String; List : TStrings);
  164. Function GetNextWord : String;
  165. Const
  166. WhiteSpace = [' ',#9,#10,#13];
  167. Literals = ['"',''''];
  168. Var
  169. Wstart,wend : Integer;
  170. InLiteral : Boolean;
  171. LastLiteral : char;
  172. begin
  173. WStart:=1;
  174. While (WStart<=Length(S)) and (S[WStart] in WhiteSpace) do
  175. Inc(WStart);
  176. WEnd:=WStart;
  177. InLiteral:=False;
  178. LastLiteral:=#0;
  179. While (Wend<=Length(S)) and (Not (S[Wend] in WhiteSpace) or InLiteral) do
  180. begin
  181. if S[Wend] in Literals then
  182. If InLiteral then
  183. InLiteral:=Not (S[Wend]=LastLiteral)
  184. else
  185. begin
  186. InLiteral:=True;
  187. LastLiteral:=S[Wend];
  188. end;
  189. inc(wend);
  190. end;
  191. Result:=Copy(S,WStart,WEnd-WStart);
  192. if (Length(Result) > 0)
  193. and (Result[1] = Result[Length(Result)]) // if 1st char = last char and..
  194. and (Result[1] in Literals) then // it's one of the literals, then
  195. Result:=Copy(Result, 2, Length(Result) - 2); //delete the 2 (but not others in it)
  196. While (WEnd<=Length(S)) and (S[Wend] in WhiteSpace) do
  197. inc(Wend);
  198. Delete(S,1,WEnd-1);
  199. end;
  200. Var
  201. W : String;
  202. begin
  203. While Length(S)>0 do
  204. begin
  205. W:=GetNextWord;
  206. If (W<>'') then
  207. List.Add(W);
  208. end;
  209. end;
  210. Constructor TProcess.Create (AOwner : TComponent);
  211. begin
  212. Inherited;
  213. FProcessPriority:=ppNormal;
  214. FShowWindow:=swoNone;
  215. FInheritHandles:=True;
  216. {$ifdef UNIX}
  217. FForkEvent:=nil;
  218. {$endif UNIX}
  219. FPipeBufferSize := 1024;
  220. FEnvironment:=TStringList.Create;
  221. FParameters:=TStringList.Create;
  222. end;
  223. Destructor TProcess.Destroy;
  224. begin
  225. FParameters.Free;
  226. FEnvironment.Free;
  227. FreeStreams;
  228. CloseProcessHandles;
  229. Inherited Destroy;
  230. end;
  231. Procedure TProcess.FreeStreams;
  232. begin
  233. If FStderrStream<>FOutputStream then
  234. FreeStream(THandleStream(FStderrStream));
  235. FreeStream(THandleStream(FOutputStream));
  236. FreeStream(THandleStream(FInputStream));
  237. end;
  238. Function TProcess.GetExitStatus : Integer;
  239. begin
  240. GetRunning;
  241. Result:=FExitCode;
  242. end;
  243. Function TProcess.GetRunning : Boolean;
  244. begin
  245. IF FRunning then
  246. FRunning:=Not PeekExitStatus;
  247. Result:=FRunning;
  248. end;
  249. Procedure TProcess.CreateStreams(InHandle,OutHandle,ErrHandle : Longint);
  250. begin
  251. FreeStreams;
  252. FInputStream:=TOutputPipeStream.Create (InHandle);
  253. FOutputStream:=TInputPipeStream.Create (OutHandle);
  254. if Not (poStderrToOutput in FProcessOptions) then
  255. FStderrStream:=TInputPipeStream.Create(ErrHandle);
  256. end;
  257. procedure TProcess.FreeStream(var AStream: THandleStream);
  258. begin
  259. if AStream = nil then exit;
  260. FreeAndNil(AStream);
  261. end;
  262. procedure TProcess.Loaded;
  263. begin
  264. inherited Loaded;
  265. If (csDesigning in ComponentState) and (CommandLine<>'') then
  266. ConvertCommandLine;
  267. end;
  268. procedure TProcess.CloseInput;
  269. begin
  270. FreeStream(THandleStream(FInputStream));
  271. end;
  272. procedure TProcess.CloseOutput;
  273. begin
  274. FreeStream(THandleStream(FOutputStream));
  275. end;
  276. procedure TProcess.CloseStderr;
  277. begin
  278. FreeStream(THandleStream(FStderrStream));
  279. end;
  280. Procedure TProcess.SetWindowColumns (Value : Cardinal);
  281. begin
  282. if Value<>0 then
  283. Include(FStartupOptions,suoUseCountChars);
  284. dwXCountChars:=Value;
  285. end;
  286. Procedure TProcess.SetWindowHeight (Value : Cardinal);
  287. begin
  288. if Value<>0 then
  289. include(FStartupOptions,suoUsePosition);
  290. dwYSize:=Value;
  291. end;
  292. Procedure TProcess.SetWindowLeft (Value : Cardinal);
  293. begin
  294. if Value<>0 then
  295. Include(FStartupOptions,suoUseSize);
  296. dwx:=Value;
  297. end;
  298. Procedure TProcess.SetWindowTop (Value : Cardinal);
  299. begin
  300. if Value<>0 then
  301. Include(FStartupOptions,suoUsePosition);
  302. dwy:=Value;
  303. end;
  304. Procedure TProcess.SetWindowWidth (Value : Cardinal);
  305. begin
  306. If (Value<>0) then
  307. Include(FStartupOptions,suoUseSize);
  308. dwXSize:=Value;
  309. end;
  310. Function TProcess.GetWindowRect : TRect;
  311. begin
  312. With Result do
  313. begin
  314. Left:=dwx;
  315. Right:=dwx+dwxSize;
  316. Top:=dwy;
  317. Bottom:=dwy+dwysize;
  318. end;
  319. end;
  320. procedure TProcess.SetCommandLine(const AValue: String);
  321. begin
  322. if FCommandLine=AValue then exit;
  323. FCommandLine:=AValue;
  324. If Not (csLoading in ComponentState) then
  325. ConvertCommandLine;
  326. end;
  327. procedure TProcess.SetParameters(const AValue: TStrings);
  328. begin
  329. FParameters.Assign(AValue);
  330. end;
  331. Procedure TProcess.SetWindowRect (Value : Trect);
  332. begin
  333. Include(FStartupOptions,suoUseSize);
  334. Include(FStartupOptions,suoUsePosition);
  335. With Value do
  336. begin
  337. dwx:=Left;
  338. dwxSize:=Right-Left;
  339. dwy:=Top;
  340. dwySize:=Bottom-top;
  341. end;
  342. end;
  343. Procedure TProcess.SetWindowRows (Value : Cardinal);
  344. begin
  345. if Value<>0 then
  346. Include(FStartupOptions,suoUseCountChars);
  347. dwYCountChars:=Value;
  348. end;
  349. procedure TProcess.SetApplicationName(const Value: String);
  350. begin
  351. FApplicationName := Value;
  352. If (csDesigning in ComponentState) and
  353. (FCommandLine='') then
  354. FCommandLine:=Value;
  355. end;
  356. procedure TProcess.SetProcessOptions(const Value: TProcessOptions);
  357. begin
  358. FProcessOptions := Value;
  359. If poNewConsole in FProcessOptions then
  360. Exclude(FProcessOptions,poNoConsole);
  361. if poRunSuspended in FProcessOptions then
  362. Exclude(FProcessOptions,poWaitOnExit);
  363. end;
  364. procedure TProcess.SetActive(const Value: Boolean);
  365. begin
  366. if (Value<>GetRunning) then
  367. If Value then
  368. Execute
  369. else
  370. Terminate(0);
  371. end;
  372. procedure TProcess.SetEnvironment(const Value: TStrings);
  373. begin
  374. FEnvironment.Assign(Value);
  375. end;
  376. procedure TProcess.ConvertCommandLine;
  377. begin
  378. FParameters.Clear;
  379. CommandToList(CommandLine,FParameters);
  380. If FParameters.Count>0 then
  381. begin
  382. Executable:=FParameters[0];
  383. FParameters.Delete(0);
  384. end;
  385. end;
  386. Const
  387. READ_BYTES = 65536; // not too small to avoid fragmentation when reading large files.
  388. // helperfunction that does the bulk of the work.
  389. function internalRuncommand(p:TProcess;var outputstring:string;var exitstatus:integer):integer;
  390. var
  391. numbytes,bytesread : integer;
  392. begin
  393. result:=-1;
  394. try
  395. try
  396. p.Options := [poUsePipes];
  397. bytesread:=0;
  398. p.Execute;
  399. while p.Running do
  400. begin
  401. Setlength(outputstring,BytesRead + READ_BYTES);
  402. NumBytes := p.Output.Read(outputstring[1+bytesread], READ_BYTES);
  403. if NumBytes > 0 then
  404. Inc(BytesRead, NumBytes)
  405. else
  406. Sleep(100);
  407. end;
  408. repeat
  409. Setlength(outputstring,BytesRead + READ_BYTES);
  410. NumBytes := p.Output.Read(outputstring[1+bytesread], READ_BYTES);
  411. if NumBytes > 0 then
  412. Inc(BytesRead, NumBytes);
  413. until NumBytes <= 0;
  414. setlength(outputstring,BytesRead);
  415. exitstatus:=p.exitstatus;
  416. result:=0; // we came to here, document that.
  417. except
  418. on e : Exception do
  419. begin
  420. result:=1;
  421. setlength(outputstring,BytesRead);
  422. end;
  423. end;
  424. finally
  425. p.free;
  426. end;
  427. end;
  428. function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string;var exitstatus:integer):integer;
  429. Var
  430. p : TProcess;
  431. i : integer;
  432. begin
  433. p:=TProcess.create(nil);
  434. p.Executable:=exename;
  435. if curdir<>'' then
  436. p.CurrentDirectory:=curdir;
  437. if high(commands)>=0 then
  438. for i:=low(commands) to high(commands) do
  439. p.Parameters.add(commands[i]);
  440. result:=internalruncommand(p,outputstring,exitstatus);
  441. end;
  442. function RunCommandInDir(const curdir,cmdline:string;var outputstring:string):boolean; deprecated;
  443. Var
  444. p : TProcess;
  445. exitstatus : integer;
  446. begin
  447. p:=TProcess.create(nil);
  448. p.commandline:=cmdline;
  449. if curdir<>'' then
  450. p.CurrentDirectory:=curdir;
  451. result:=internalruncommand(p,outputstring,exitstatus)=0;
  452. if exitstatus<>0 then result:=false;
  453. end;
  454. function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string):boolean;
  455. Var
  456. p : TProcess;
  457. i,
  458. exitstatus : integer;
  459. begin
  460. p:=TProcess.create(nil);
  461. p.Executable:=exename;
  462. if curdir<>'' then
  463. p.CurrentDirectory:=curdir;
  464. if high(commands)>=0 then
  465. for i:=low(commands) to high(commands) do
  466. p.Parameters.add(commands[i]);
  467. result:=internalruncommand(p,outputstring,exitstatus)=0;
  468. if exitstatus<>0 then result:=false;
  469. end;
  470. function RunCommand(const cmdline:string;var outputstring:string):boolean; deprecated;
  471. Var
  472. p : TProcess;
  473. exitstatus : integer;
  474. begin
  475. p:=TProcess.create(nil);
  476. p.commandline:=cmdline;
  477. result:=internalruncommand(p,outputstring,exitstatus)=0;
  478. if exitstatus<>0 then result:=false;
  479. end;
  480. function RunCommand(const exename:string;const commands:array of string;var outputstring:string):boolean;
  481. Var
  482. p : TProcess;
  483. i,
  484. exitstatus : integer;
  485. begin
  486. p:=TProcess.create(nil);
  487. p.Executable:=exename;
  488. if high(commands)>=0 then
  489. for i:=low(commands) to high(commands) do
  490. p.Parameters.add(commands[i]);
  491. result:=internalruncommand(p,outputstring,exitstatus)=0;
  492. if exitstatus<>0 then result:=false;
  493. end;
  494. end.