dcprocessutf8.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. {
  2. Based on process.inc from 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. unit DCProcessUtf8;
  11. {$mode objfpc}{$H+}
  12. interface
  13. uses
  14. Classes, SysUtils
  15. {$IF DEFINED(MSWINDOWS)}
  16. , Process, Windows, Pipes, DCConvertEncoding
  17. {$ELSEIF DEFINED(UNIX)}
  18. , BaseUnix, Process, UTF8Process, DCUnix
  19. {$ENDIF}
  20. ;
  21. type
  22. { TProcessUtf8 }
  23. {$IF DEFINED(UNIX)}
  24. TProcessUtf8 = class(UTF8Process.TProcessUTF8)
  25. private
  26. procedure DoForkEvent(Sender : TObject);
  27. public
  28. constructor Create(AOwner : TComponent); override;
  29. procedure Execute; override;
  30. function Resume : Integer; override;
  31. function Suspend : Integer; override;
  32. function Terminate (AExitCode : Integer): Boolean; override;
  33. end;
  34. {$ELSEIF DEFINED(MSWINDOWS) AND (FPC_FULLVERSION < 30301)}
  35. TProcessUtf8 = class(TProcess)
  36. public
  37. procedure Execute; override;
  38. end;
  39. {$ELSE}
  40. TProcessUtf8 = class(TProcess);
  41. {$ENDIF}
  42. implementation
  43. {$IF DEFINED(UNIX)}
  44. { TProcessUtf8 }
  45. procedure TProcessUtf8.DoForkEvent(Sender: TObject);
  46. begin
  47. FileCloseOnExecAll;
  48. if (poNewProcessGroup in Options) then
  49. if (setpgid(0, 0) < 0) then fpExit(127);
  50. end;
  51. constructor TProcessUtf8.Create(AOwner: TComponent);
  52. begin
  53. inherited Create(AOwner);
  54. {$IF (FPC_FULLVERSION >= 30000)}
  55. OnForkEvent:= @DoForkEvent;
  56. {$ELSE}
  57. OnForkEvent:= @FileCloseOnExecAll;
  58. {$ENDIF}
  59. end;
  60. procedure TProcessUtf8.Execute;
  61. begin
  62. inherited Execute;
  63. if (poNewProcessGroup in Options) then
  64. PInteger(@ProcessId)^:= -ProcessId;
  65. end;
  66. function TProcessUtf8.Resume: Integer;
  67. begin
  68. if fpKill(ProcessId, SIGCONT) <> 0 then
  69. Result:= -1
  70. else
  71. Result:= 0;
  72. end;
  73. function TProcessUtf8.Suspend: Integer;
  74. begin
  75. if fpKill(ProcessId, SIGSTOP) <> 0 then
  76. Result:= -1
  77. else
  78. Result:= 1;
  79. end;
  80. function TProcessUtf8.Terminate(AExitCode: Integer): Boolean;
  81. begin
  82. Result:= fpKill(ProcessId, SIGTERM) = 0;
  83. if Result then
  84. begin
  85. if Running then
  86. Result:= fpKill(ProcessId, SIGKILL) = 0;
  87. end;
  88. if Result then WaitOnExit;
  89. end;
  90. {$ELSEIF DEFINED(MSWINDOWS) AND (FPC_FULLVERSION < 30301)}
  91. {$WARN SYMBOL_DEPRECATED OFF}
  92. {$IF FPC_FULLVERSION < 30000}
  93. type
  94. TStartupInfoW = TStartupInfo;
  95. {$ENDIF}
  96. resourcestring
  97. SNoCommandLine = 'Cannot execute empty command-line';
  98. SErrCannotExecute = 'Failed to execute %s : %d';
  99. const
  100. PriorityConstants: array [TProcessPriority] of Cardinal =
  101. (HIGH_PRIORITY_CLASS, IDLE_PRIORITY_CLASS,
  102. NORMAL_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS
  103. {$IF FPC_FULLVERSION >= 30200}
  104. , BELOW_NORMAL_PRIORITY_CLASS,ABOVE_NORMAL_PRIORITY_CLASS
  105. {$ENDIF}
  106. );
  107. function GetStartupFlags(P: TProcess): Cardinal;
  108. begin
  109. with P do
  110. begin
  111. Result := 0;
  112. if poUsePipes in Options then
  113. Result := Result or Startf_UseStdHandles;
  114. if suoUseShowWindow in StartupOptions then
  115. Result := Result or startf_USESHOWWINDOW;
  116. if suoUSESIZE in StartupOptions then
  117. Result := Result or startf_usesize;
  118. if suoUsePosition in StartupOptions then
  119. Result := Result or startf_USEPOSITION;
  120. if suoUSECOUNTCHARS in StartupOptions then
  121. Result := Result or startf_usecountchars;
  122. if suoUsefIllAttribute in StartupOptions then
  123. Result := Result or startf_USEFILLATTRIBUTE;
  124. end;
  125. end;
  126. function GetCreationFlags(P: TProcess): Cardinal;
  127. begin
  128. with P do
  129. begin
  130. Result := 0;
  131. if poNoConsole in Options then
  132. Result := Result or Detached_Process;
  133. if poNewConsole in Options then
  134. Result := Result or Create_new_console;
  135. if poNewProcessGroup in Options then
  136. Result := Result or CREATE_NEW_PROCESS_GROUP;
  137. if poRunSuspended in Options then
  138. Result := Result or Create_Suspended;
  139. if poDebugProcess in Options then
  140. Result := Result or DEBUG_PROCESS;
  141. if poDebugOnlyThisProcess in Options then
  142. Result := Result or DEBUG_ONLY_THIS_PROCESS;
  143. if poDefaultErrorMode in Options then
  144. Result := Result or CREATE_DEFAULT_ERROR_MODE;
  145. Result := Result or PriorityConstants[Priority];
  146. end;
  147. end;
  148. function StringsToPWideChars(List: TStrings): Pointer;
  149. var
  150. I: Integer;
  151. EnvBlock: WideString;
  152. begin
  153. EnvBlock := '';
  154. for I := 0 to List.Count - 1 do
  155. EnvBlock := EnvBlock + CeUtf8ToUtf16(List[I]) + #0;
  156. EnvBlock := EnvBlock + #0;
  157. GetMem(Result, Length(EnvBlock) * SizeOf(Widechar));
  158. CopyMemory(Result, @EnvBlock[1], Length(EnvBlock) * SizeOf(Widechar));
  159. end;
  160. procedure InitProcessAttributes(P: TProcess; var PA: TSecurityAttributes);
  161. begin
  162. FillChar(PA, SizeOf(PA), 0);
  163. PA.nLength := SizeOf(PA);
  164. end;
  165. procedure InitThreadAttributes(P: TProcess; var TA: TSecurityAttributes);
  166. begin
  167. FillChar(TA, SizeOf(TA), 0);
  168. TA.nLength := SizeOf(TA);
  169. end;
  170. procedure InitStartupInfo(P: TProcess; var SI: TStartupInfoW);
  171. const
  172. SWC: array [TShowWindowOptions] of Cardinal =
  173. (0, SW_HIDE, SW_Maximize, SW_Minimize, SW_Restore, SW_Show,
  174. SW_ShowDefault, SW_ShowMaximized, SW_ShowMinimized,
  175. SW_showMinNOActive, SW_ShowNA, SW_ShowNoActivate, SW_ShowNormal);
  176. begin
  177. FillChar(SI, SizeOf(SI), 0);
  178. with SI do
  179. begin
  180. dwFlags := GetStartupFlags(P);
  181. if P.ShowWindow <> swoNone then
  182. dwFlags := dwFlags or Startf_UseShowWindow
  183. else
  184. dwFlags := dwFlags and not Startf_UseShowWindow;
  185. wShowWindow := SWC[P.ShowWindow];
  186. if (poUsePipes in P.Options) then
  187. begin
  188. dwFlags := dwFlags or Startf_UseStdHandles;
  189. end;
  190. if P.FillAttribute <> 0 then
  191. begin
  192. dwFlags := dwFlags or Startf_UseFillAttribute;
  193. dwFillAttribute := P.FillAttribute;
  194. end;
  195. dwXCountChars := P.WindowColumns;
  196. dwYCountChars := P.WindowRows;
  197. dwYsize := P.WindowHeight;
  198. dwXsize := P.WindowWidth;
  199. dwy := P.WindowTop;
  200. dwX := P.WindowLeft;
  201. end;
  202. end;
  203. { The handles that are to be passed to the child process must be
  204. inheritable. On the other hand, only non-inheritable handles
  205. allow the sending of EOF when the write-end is closed. This
  206. function is used to duplicate the child process's ends of the
  207. handles into inheritable ones, leaving the parent-side handles
  208. non-inheritable.
  209. }
  210. function DuplicateHandleFP(var Handle: THandle): Boolean;
  211. var
  212. oldHandle: THandle;
  213. begin
  214. oldHandle := Handle;
  215. Result := DuplicateHandle(GetCurrentProcess(), oldHandle,
  216. GetCurrentProcess(), @Handle, 0, True, DUPLICATE_SAME_ACCESS);
  217. if Result then
  218. Result := CloseHandle(oldHandle);
  219. end;
  220. procedure CreatePipes(var HI, HO, HE: THandle; var SI: TStartupInfoW;
  221. CE: Boolean; APipeBufferSize: Cardinal);
  222. begin
  223. CreatePipeHandles(SI.hStdInput, HI, APipeBufferSize);
  224. DuplicateHandleFP(SI.hStdInput);
  225. CreatePipeHandles(HO, Si.hStdOutput, APipeBufferSize);
  226. DuplicateHandleFP(Si.hStdOutput);
  227. if CE then
  228. begin
  229. CreatePipeHandles(HE, SI.hStdError, APipeBufferSize);
  230. DuplicateHandleFP(SI.hStdError);
  231. end
  232. else
  233. begin
  234. SI.hStdError := SI.hStdOutput;
  235. HE := HO;
  236. end;
  237. end;
  238. function MaybeQuote(const S: String): String;
  239. begin
  240. if (Pos(' ', S) <> 0) then
  241. Result := '"' + S + '"'
  242. else
  243. Result := S;
  244. end;
  245. function MaybeQuoteIfNotQuoted(const S: String): String;
  246. begin
  247. if (Pos(' ', S) <> 0) and (pos('"', S) = 0) then
  248. Result := '"' + S + '"'
  249. else
  250. Result := S;
  251. end;
  252. { TProcessUtf8 }
  253. procedure TProcessUtf8.Execute;
  254. var
  255. I: Integer;
  256. PName, PDir, PCommandLine: PWideChar;
  257. FEnv: Pointer;
  258. FCreationFlags: Cardinal;
  259. FProcessAttributes: TSecurityAttributes;
  260. FThreadAttributes: TSecurityAttributes;
  261. FProcessInformation: TProcessInformation;
  262. FStartupInfo: TStartupInfoW;
  263. HI, HO, HE: THandle;
  264. Cmd: String;
  265. begin
  266. InheritHandles := True;
  267. PName := nil;
  268. PCommandLine := nil;
  269. PDir := nil;
  270. if (ApplicationName = '') and (CommandLine = '') and (Executable = '') then
  271. raise EProcess.Create(SNoCommandline);
  272. if (ApplicationName <> '') then
  273. begin
  274. PName := PWideChar(CeUtf8ToUtf16(ApplicationName));
  275. PCommandLine := PWideChar(CeUtf8ToUtf16(CommandLine));
  276. end
  277. else if (CommandLine <> '') then
  278. PCommandLine := PWideChar(CeUtf8ToUtf16(CommandLine))
  279. else if (Executable <> '') then
  280. begin
  281. Cmd := MaybeQuoteIfNotQuoted(Executable);
  282. for I := 0 to Parameters.Count - 1 do
  283. Cmd := Cmd + ' ' + MaybeQuoteIfNotQuoted(Parameters[I]);
  284. PCommandLine := PWideChar(CeUtf8ToUtf16(Cmd));
  285. end;
  286. if CurrentDirectory <> '' then
  287. PDir := PWideChar(CeUtf8ToUtf16(CurrentDirectory));
  288. if Environment.Count <> 0 then
  289. FEnv := StringsToPWideChars(Environment)
  290. else
  291. FEnv := nil;
  292. try
  293. FCreationFlags := GetCreationFlags(Self);
  294. InitProcessAttributes(Self, FProcessAttributes);
  295. InitThreadAttributes(Self, FThreadAttributes);
  296. InitStartupInfo(Self, FStartUpInfo);
  297. if poUsePipes in Options then
  298. CreatePipes(HI, HO, HE, FStartupInfo, not (poStdErrToOutPut in Options),
  299. PipeBufferSize);
  300. try
  301. if not CreateProcessW(PName, PCommandLine, @FProcessAttributes,
  302. @FThreadAttributes, InheritHandles, FCreationFlags, FEnv,
  303. PDir, FStartupInfo, FProcessInformation) then
  304. raise EProcess.CreateFmt(SErrCannotExecute, [CommandLine, GetLastError]);
  305. PHandle(@ProcessHandle)^ := FProcessInformation.hProcess;
  306. PHandle(@ThreadHandle)^ := FProcessInformation.hThread;
  307. PInteger(@ProcessID)^ := FProcessINformation.dwProcessID;
  308. finally
  309. if poUsePipes in Options then
  310. begin
  311. FileClose(FStartupInfo.hStdInput);
  312. FileClose(FStartupInfo.hStdOutput);
  313. if not (poStdErrToOutPut in Options) then
  314. FileClose(FStartupInfo.hStdError);
  315. CreateStreams(HI, HO, HE);
  316. end;
  317. end;
  318. FRunning := True;
  319. finally
  320. if FEnv <> nil then
  321. FreeMem(FEnv);
  322. end;
  323. if not (csDesigning in ComponentState) and // This would hang the IDE !
  324. (poWaitOnExit in Options) and not (poRunSuspended in Options) then
  325. WaitOnExit;
  326. end;
  327. {$ENDIF}
  328. end.