process.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2008 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. {$IFDEF FPC_DOTTEDUNITS}
  11. Uses
  12. WinApi.Windows;
  13. {$ELSE}
  14. Uses
  15. Windows;
  16. {$ENDIF}
  17. Resourcestring
  18. SNoCommandLine = 'Cannot execute empty command-line';
  19. SErrCannotExecute = 'Failed to execute %s : %d';
  20. { SErrNoSuchProgram = 'Executable not found: "%s"';
  21. SErrNoTerminalProgram = 'Could not detect X-Terminal program';
  22. }
  23. Const
  24. PriorityConstants : Array [TProcessPriority] of Cardinal =
  25. (HIGH_PRIORITY_CLASS,IDLE_PRIORITY_CLASS,
  26. NORMAL_PRIORITY_CLASS,REALTIME_PRIORITY_CLASS,
  27. BELOW_NORMAL_PRIORITY_CLASS,ABOVE_NORMAL_PRIORITY_CLASS);
  28. procedure TProcessnamemacro.CloseProcessHandles;
  29. begin
  30. if (FProcessHandle<>0) then
  31. CloseHandle(FProcessHandle);
  32. if (FThreadHandle<>0) then
  33. CloseHandle(FThreadHandle);
  34. end;
  35. Function TProcessnamemacro.PeekExitStatus : Boolean;
  36. begin
  37. Result:=GetExitCodeProcess(ProcessHandle,FExitCode) and (FExitCode<>Still_Active);
  38. // wait up to 10ms extra till process really done to get rest of input bug #39821
  39. if not Result Then
  40. WaitForSingleObject(FProcessHandle,10);
  41. end;
  42. Function GetStartupFlags (P : TProcessnamemacro): Cardinal;
  43. begin
  44. Result:=0;
  45. if poUsePipes in P.Options then
  46. Result:=Result or Startf_UseStdHandles;
  47. if suoUseShowWindow in P.StartupOptions then
  48. Result:=Result or startf_USESHOWWINDOW;
  49. if suoUSESIZE in P.StartupOptions then
  50. Result:=Result or startf_usesize;
  51. if suoUsePosition in P.StartupOptions then
  52. Result:=Result or startf_USEPOSITION;
  53. if suoUSECOUNTCHARS in P.Startupoptions then
  54. Result:=Result or startf_usecountchars;
  55. if suoUsefIllAttribute in P.StartupOptions then
  56. Result:=Result or startf_USEFILLATTRIBUTE;
  57. end;
  58. Function GetCreationFlags(P : TProcessnamemacro) : Cardinal;
  59. begin
  60. Result:=CREATE_UNICODE_ENVIRONMENT;
  61. if poNoConsole in P.Options then
  62. Result:=Result or CREATE_NO_WINDOW;
  63. if poNewConsole in P.Options then
  64. Result:=Result or Create_new_console;
  65. if poNewProcessGroup in P.Options then
  66. Result:=Result or CREATE_NEW_PROCESS_GROUP;
  67. If poRunSuspended in P.Options Then
  68. Result:=Result or Create_Suspended;
  69. if poDebugProcess in P.Options Then
  70. Result:=Result or DEBUG_PROCESS;
  71. if poDebugOnlyThisProcess in P.Options Then
  72. Result:=Result or DEBUG_ONLY_THIS_PROCESS;
  73. if poDefaultErrorMode in P.Options Then
  74. Result:=Result or CREATE_DEFAULT_ERROR_MODE;
  75. if poDetached in P.Options Then
  76. Result:=Result or DETACHED_PROCESS;
  77. result:=result or PriorityConstants[P.FProcessPriority];
  78. end;
  79. function WStrAsUniquePWideChar(var s: UnicodeString): PWideChar;
  80. begin
  81. UniqueString(s);
  82. if s<>'' then
  83. Result:=PWideChar(s)
  84. else
  85. Result:=nil;
  86. end;
  87. Function StringsToWChars(List : TProcessStrings): pointer;
  88. var
  89. EnvBlock: UnicodeString;
  90. I: Integer;
  91. begin
  92. EnvBlock := '';
  93. For I:=0 to List.Count-1 do
  94. EnvBlock := EnvBlock + List[i] + #0;
  95. EnvBlock := EnvBlock + #0;
  96. GetMem(Result, Length(EnvBlock)*2);
  97. CopyMemory(Result, @EnvBlock[1], Length(EnvBlock)*2);
  98. end;
  99. Procedure InitProcessAttributes(P : TProcessnamemacro; Var PA : TSecurityAttributes);
  100. begin
  101. FillChar(PA,SizeOf(PA),0);
  102. PA.nLength := SizeOf(PA);
  103. end;
  104. Procedure InitThreadAttributes(P : TProcessnamemacro; Var TA : TSecurityAttributes);
  105. begin
  106. FillChar(TA,SizeOf(TA),0);
  107. TA.nLength := SizeOf(TA);
  108. end;
  109. Procedure InitStartupInfo(P : TProcessnamemacro; Var SI : STARTUPINFOW);
  110. Const
  111. SWC : Array [TShowWindowOptions] of Cardinal =
  112. (0,SW_HIDE,SW_Maximize,SW_Minimize,SW_Restore,SW_Show,
  113. SW_ShowDefault,SW_ShowMaximized,SW_ShowMinimized,
  114. SW_showMinNOActive,SW_ShowNA,SW_ShowNoActivate,SW_ShowNormal);
  115. begin
  116. FillChar(SI,SizeOf(SI),0);
  117. SI.cb:=SizeOf(SI);
  118. SI.dwFlags:=GetStartupFlags(P);
  119. if P.FShowWindow<>swoNone then
  120. SI.dwFlags:=SI.dwFlags or Startf_UseShowWindow
  121. else
  122. SI.dwFlags:=SI.dwFlags and not Startf_UseShowWindow;
  123. SI.wShowWindow:=SWC[P.FShowWindow];
  124. if (poUsePipes in P.Options) then
  125. begin
  126. SI.dwFlags:=SI.dwFlags or Startf_UseStdHandles;
  127. end;
  128. if P.FillAttribute<>0 then
  129. begin
  130. SI.dwFlags:=SI.dwFlags or Startf_UseFillAttribute;
  131. SI.dwFillAttribute:=P.FillAttribute;
  132. end;
  133. SI.dwXCountChars:=P.WindowColumns;
  134. SI.dwYCountChars:=P.WindowRows;
  135. SI.dwYsize:=P.WindowHeight;
  136. SI.dwXsize:=P.WindowWidth;
  137. SI.dwy:=P.WindowTop;
  138. SI.dwX:=P.WindowLeft;
  139. end;
  140. { The handles that are to be passed to the child process must be
  141. inheritable. On the other hand, only non-inheritable handles
  142. allow the sending of EOF when the write-end is closed. This
  143. function is used to duplicate the child process's ends of the
  144. handles into inheritable ones, leaving the parent-side handles
  145. non-inheritable.
  146. }
  147. function DuplicateHandleFP(var handle: THandle): Boolean;
  148. var
  149. oldHandle: THandle;
  150. begin
  151. oldHandle := handle;
  152. Result := DuplicateHandle
  153. ( GetCurrentProcess(),
  154. oldHandle,
  155. GetCurrentProcess(),
  156. @handle,
  157. 0,
  158. true,
  159. DUPLICATE_SAME_ACCESS
  160. );
  161. if Result then
  162. Result := CloseHandle(oldHandle);
  163. end;
  164. Procedure CreatePipes(Var HI,HO,HE : Thandle; Var SI : TStartupInfoW; CI, CE : Boolean; APipeBufferSize : Cardinal);
  165. begin
  166. if CI then
  167. begin
  168. CreatePipeHandles(SI.hStdInput,HI, APipeBufferSize);
  169. DuplicateHandleFP(SI.hStdInput);
  170. end
  171. else
  172. begin
  173. SI.hStdInput:=StdInputHandle;
  174. end;
  175. CreatePipeHandles(HO,Si.hStdOutput, APipeBufferSize);
  176. DuplicateHandleFP( Si.hStdOutput);
  177. if CE then begin
  178. CreatePipeHandles(HE,SI.hStdError, APipeBufferSize);
  179. DuplicateHandleFP( SI.hStdError);
  180. end
  181. else
  182. begin
  183. SI.hStdError:=SI.hStdOutput;
  184. HE:=HO;
  185. end;
  186. end;
  187. {Function MaybeQuote(Const S : String) : String;
  188. begin
  189. If (Pos(' ',S)<>0) then
  190. Result:='"'+S+'"'
  191. else
  192. Result:=S;
  193. end;
  194. }
  195. Function MaybeQuoteIfNotQuoted(Const S : TProcessString) : TProcessString;
  196. begin
  197. If (Pos(' ',S)<>0) and (pos('"',S)=0) then
  198. Result:='"'+S+'"'
  199. else
  200. Result:=S;
  201. end;
  202. Procedure TProcessnamemacro.Execute;
  203. Var
  204. i : Integer;
  205. WName,WDir,WCommandLine : UnicodeString;
  206. PWName,PWDir,PWCommandLine : PWideChar;
  207. FEnv: pointer;
  208. FCreationFlags : Cardinal;
  209. FProcessAttributes : TSecurityAttributes;
  210. FThreadAttributes : TSecurityAttributes;
  211. FProcessInformation : TProcessInformation;
  212. FStartupInfo : STARTUPINFOW;
  213. HI,HO,HE : THandle;
  214. Cmd : TProcessString;
  215. begin
  216. WName:='';
  217. WCommandLine:='';
  218. WDir:='';
  219. if (FApplicationName='') and (FCommandLine='') and (FExecutable='') then
  220. Raise EProcess.Create(SNoCommandline);
  221. if (FApplicationName<>'') then
  222. begin
  223. WName:=FApplicationName;
  224. WCommandLine:=FCommandLine;
  225. end
  226. else If (FCommandLine<>'') then
  227. WCommandLine:=FCommandLine
  228. else if (FExecutable<>'') then
  229. begin
  230. Cmd:=MaybeQuoteIfNotQuoted(Executable);
  231. For I:=0 to Parameters.Count-1 do
  232. Cmd:=Cmd+' '+MaybeQuoteIfNotQuoted(Parameters[i]);
  233. WCommandLine:=Cmd;
  234. end;
  235. If FCurrentDirectory<>'' then
  236. WDir:=FCurrentDirectory;
  237. if FEnvironment.Count<>0 then
  238. FEnv:=StringsToWChars(FEnvironment)
  239. else
  240. FEnv:=Nil;
  241. Try
  242. FCreationFlags:=GetCreationFlags(Self);
  243. InitProcessAttributes(Self,FProcessAttributes);
  244. InitThreadAttributes(Self,FThreadAttributes);
  245. InitStartupInfo(Self,FStartUpInfo);
  246. If poUsePipes in Options then
  247. CreatePipes(HI,HO,HE,FStartupInfo,Not(poPassInput in Options), Not(poStdErrToOutPut in Options), FPipeBufferSize);
  248. Try
  249. // Beware: CreateProcess can alter the strings
  250. // Beware: nil is not the same as a pointer to a #0
  251. PWName:=WStrAsUniquePWideChar(WName);
  252. PWCommandLine:=WStrAsUniquePWideChar(WCommandLine);
  253. PWDir:=WStrAsUniquePWideChar(WDir);
  254. If Not CreateProcessW (PWName,PWCommandLine,@FProcessAttributes,@FThreadAttributes,
  255. FInheritHandles,FCreationFlags,FEnv,PWDir,FStartupInfo,
  256. fProcessInformation) then
  257. Raise EProcess.CreateFmt(SErrCannotExecute,[FCommandLine,GetLastError]);
  258. FProcessHandle:=FProcessInformation.hProcess;
  259. FThreadHandle:=FProcessInformation.hThread;
  260. FThreadId:=FProcessInformation.dwThreadId;
  261. FProcessID:=FProcessINformation.dwProcessID;
  262. Finally
  263. if POUsePipes in Options then
  264. begin
  265. if not (poPassInput in Options) then
  266. FileClose(FStartupInfo.hStdInput);
  267. FileClose(FStartupInfo.hStdOutput);
  268. if Not (poStdErrToOutPut in Options) then
  269. FileClose(FStartupInfo.hStdError);
  270. CreateStreams(HI,HO,HE);
  271. if poPassInput in Options then
  272. FInputStream.DontClose:=true;
  273. end;
  274. end;
  275. FRunning:=True;
  276. Finally
  277. If FEnv<>Nil then
  278. FreeMem(FEnv);
  279. end;
  280. if not (csDesigning in ComponentState) and // This would hang the IDE !
  281. (poWaitOnExit in Options) and
  282. not (poRunSuspended in Options) then
  283. WaitOnExit;
  284. end;
  285. Function TProcessnamemacro.WaitOnExit : Boolean;
  286. Var
  287. R : DWord;
  288. begin
  289. R:=WaitForSingleObject (FProcessHandle,Infinite);
  290. Result:=(R<>Wait_Failed);
  291. If Result then
  292. GetExitStatus;
  293. FRunning:=False;
  294. end;
  295. Function TProcessnamemacro.WaitOnExit(Timeout : DWord) : Boolean;
  296. Var
  297. R : DWord;
  298. begin
  299. R:=WaitForSingleObject (FProcessHandle,Timeout);
  300. Result:=R=0;
  301. If Result then
  302. begin
  303. GetExitStatus;
  304. FRunning:=False;
  305. end;
  306. end;
  307. Function TProcessnamemacro.Suspend : Longint;
  308. begin
  309. Result:=SuspendThread(ThreadHandle);
  310. end;
  311. Function TProcessnamemacro.Resume : LongInt;
  312. begin
  313. Result:=ResumeThread(ThreadHandle);
  314. end;
  315. Function TProcessnamemacro.Terminate(AExitCode : Integer) : Boolean;
  316. begin
  317. Result:=False;
  318. If ExitStatus=Still_active then
  319. Result:=TerminateProcess(Handle,AexitCode);
  320. end;
  321. Procedure TProcessnamemacro.SetShowWindow (Value : TShowWindowOptions);
  322. begin
  323. FShowWindow:=Value;
  324. end;