可以使用 TProcess 来启动外部程序。使用 TProcess 的一些好处:
重要注释:
TProcess是一个组件,可用于启动和控制其他进程(程序/二进制文件)。它包含许多控制进程如何启动的选项。其中许多是特定于 Win32 的,对其他平台没有影响,因此应谨慎使用。
使用此组件的最简单方法是创建一个实例,将 Executable 属性设置为应执行的程序的完整路径名,然后调用Execute。要确定进程是否仍在运行(即没有停止执行),可以检查 Running属性。
属性、事件和方法:
public | |
constructor Create(); override; | 创建TProcess类的新实例。 |
destructor Destroy; override; | 销毁这个TProcess实例 |
procedure Execute; virtual; | 使用给定的选项执行程序 |
procedure CloseInput; virtual; | 关闭进程的输入流 |
procedure CloseOutput; virtual; | 关闭进程的输出流 |
procedure CloseStderr; virtual; | 关闭进程的错误流 |
function Resume; virtual; | 恢复执行暂停的进程 |
function Suspend; virtual; | 暂停正在运行的进程 |
function Terminate(); virtual; | 终止正在运行的进程 |
function WaitOnExit; | 等待程序停止执行。 |
property WindowRect: Trect; [rw] | 主程序窗口的位置。 |
property Handle: THandle; [r] | 进程的句柄 |
property ProcessHandle: THandle; [r] | 句柄的别名 |
property ThreadHandle: THandle; [r] | 主进程线程句柄 |
` property ProcessID: Integer; [r] | 进程的标识。 |
property ThreadID: Integer; [r] | 主进程线程ID |
property Input: TOutputPipeStream; [r] | 流连接到进程的标准输入。 |
property Output: TInputPipeStream; [r] | 流连接到进程的标准输出。 |
property Stderr: TInputPipeStream; [r] | 流连接到进程的标准诊断输出。 |
property ExitStatus: Integer; [r] | 进程的退出状态。 |
property ExitCode: Integer; [r] | 进程退出代码 |
property InheritHandles: Boolean; [rw] | 创建的进程是否应该继承当前进程的打开句柄。 |
` property OnForkEvent: TProcessForkEvent; [rw] | Linux 上 fork 发生后触发的事件 |
published | |
property PipeBufferSize: Cardinal; [rw] | 使用管道时要使用的缓冲区大小 |
property Active: Boolean; [rw] | 启动或停止该过程。 |
property ApplicationName: string; [rw] deprecated ; | 要启动的应用程序的名称(已弃用) |
property Executable: string; [rw] | 可执行名称。 |
property Parameters: TStrings; [rw] | 命令行参数。 |
property ConsoleTitle: string; [rw] | 控制台窗口的标题 |
property CurrentDirectory: string; [rw] | 进程的工作目录。 |
` property Desktop: string; [rw] | 启动进程的桌面。 |
property Environment: TStrings; [rw] | 新进程的环境变量。 |
property Options: TProcessOptions; [rw] | 启动进程时要使用的选项。 |
property Priority: TProcessPriority; [rw] | 进程运行的优先级。 |
property StartupOptions: TStartupOptions; [rw] | 其他 (Windows) 启动选项 |
property Running: Boolean; [r] | 确定进程是否仍在运行。 |
property ShowWindow: TShowWindowOptions; [rw] | 确定进程主窗口的显示方式(仅限 Windows) |
property WindowColumns: Cardinal; [rw] | 控制台窗口中的列数(仅限 Windows) |
property WindowHeight: Cardinal; [rw] | 进程主窗口的高度 |
property WindowLeft: Cardinal; [rw] | 初始窗口的 X 坐标(仅限 Windows) |
property WindowRows: Cardinal; [rw] | 控制台窗口中的行数(仅限 Windows) |
property WindowTop: Cardinal; [rw] | 初始窗口的 Y 坐标(仅限 Windows) |
property WindowWidth: Cardinal; [rw] | 进程主窗口的高度(仅限 Windows) |
property FillAttribute: Cardinal; [rw] | 控制台窗口中字符的颜色属性(仅限 Windows) |
property XTermProgram: string; [rw] | 要使用的 XTerm 程序(仅限 unix) |
这个示例仅显示如何运行一个外部程序,这不能使用在生产中,看大量输出。
// This is a demo program that shows// how to launch an external program.program launchprogram; // Here we include files that have useful functions// and procedures we will need.uses Classes, SysUtils, Process; // This defines the var "AProcess" as a variable // of the type "TProcess"var AProcess: TProcess; // This is where our program starts to runbegin // Now we will create the TProcess object, and // assign it to the var AProcess. AProcess := TProcess.Create(nil); // Tell the new AProcess what the command to execute is. // Let's use the Free Pascal compiler (i386 version that is) AProcess.Executable:= 'ppc386'; // Pass -h together with ppc386 so actually 'ppc386 -h' is executed: AProcess.Parameters.Add('-h'); // We will define an option for when the program // is run. This option will make sure that our program // does not continue until the program we will launch // has stopped running. vvvvvvvvvvvvvv AProcess.Options := AProcess.Options + [poWaitOnExit]; // Now let AProcess run the program AProcess.Execute; // This is not reached until ppc386 stops running. AProcess.Free; end.上面的程序学习从程序内部来运行一个外部程序。
如何读取一个运行程序的输出?好的,让我们稍微扩张我们的示例,并保持这个示例简单,从而可以从中学习。请不要在产品代码中使用这个示例,因为在代码中有大量输出。
// This is a // FLAWED// demo program that shows// how to launch an external program// and read from its output.program launchprogram; // Here we include files that have useful functions// and procedures we will need.uses Classes, SysUtils, Process; // This is defining the var "AProcess" as a variable // of the type "TProcess"// Also now we are adding a TStringList to store the // data read from the programs output.var AProcess: TProcess; AStringList: TStringList;// This is where our program starts to runbegin // Now we will create the TProcess object, and // assign it to the var AProcess. AProcess := TProcess.Create(nil); // Tell the new AProcess what the command to execute is. AProcess.Executable := '/usr/bin/ppc386'; AProcess.Parameters.Add('-h'); // We will define an option for when the program // is run. This option will make sure that our program // does not continue until the program we will launch // has stopped running. Also now we will tell it that // we want to read the output of the file. AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes]; // Now that AProcess knows what the commandline is it can be run. AProcess.Execute; // After AProcess has finished, the rest of the program will be executed. // Now read the output of the program we just ran into a TStringList. AStringList := TStringList.Create; AStringList.LoadFromStream(AProcess.Output); // Save the output to a file and clean up the TStringList. AStringList.SaveToFile('output.txt'); AStringList.Free; // Now that the output from the process is processed, it can be freed. AProcess.Free; end.在前一个示例中,我们一直等到程序退出。然后,我们读取程序的输出。假设程序写很多数据到输出。然后,输出管道变满,并且调用程序等待,直到管道被读取。但是,调用程序不从它读,直到调用的程序被结束。一个停滞发生。
下面的示例因为不使用 poWaitOnExit 选项,但是,当程序仍然在运行时,从中输出读取。输出被存储在内存流中,随后可以被使用于读输出到一个 TStringList 中。如果我们想从一个外部进程中读输出,这是需要改写到产品使用的代码。
program LargeOutputDemo;{$mode objfpc}{$H+}uses Classes, SysUtils, Process; // Process is the unit that holds TProcessconst BUF_SIZE = 2048; // Buffer size for reading the output in chunksvar AProcess : TProcess; OutputStream : TStream; BytesRead : longint; Buffer : array[1..BUF_SIZE] of byte;begin // Set up the process; as an example a recursive directory search is used // because that will usually result in a lot of data. AProcess := TProcess.Create(nil); // The commands for Windows and *nix are different hence the $IFDEFs {$IFDEF Windows} // In Windows the dir command cannot be used directly because it's a build-in // shell command. Therefore cmd.exe and the extra parameters are needed. AProcess.Executable := 'c:\windows\system32\cmd.exe'; AProcess.Parameters.Add('/c'); AProcess.Parameters.Add('dir /s c:\windows'); {$ENDIF Windows} {$IFDEF Unix} AProcess.Executable := '/bin/ls'; AProcess.Parameters.Add('--recursive'); AProcess.Parameters.Add('--all'); AProcess.Parameters.Add('-l'); {$ENDIF Unix} // Process option poUsePipes has to be used so the output can be captured. // Process option poWaitOnExit can not be used because that would block // this program, preventing it from reading the output data of the process. AProcess.Options := [poUsePipes]; // Start the process (run the dir/ls command) AProcess.Execute; // Create a stream object to store the generated output in. This could // also be a file stream to directly save the output to disk. OutputStream := TMemoryStream.Create; // All generated output from AProcess is read in a loop until no more data is available repeat // Get the new data from the process to a maximum of the buffer size that was allocated. // Note that all read(...) calls will block except for the last one, which returns 0 (zero). BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE); // Add the bytes that were read to the stream for later usage OutputStream.Write(Buffer, BytesRead) until BytesRead = 0; // Stop if no more data is available // The process has finished so it can be cleaned up AProcess.Free; // Now that all data has been read it can be used; for example to save it to a file on disk with TFileStream.Create('output.txt', fmCreate) do begin OutputStream.Position := 0; // Required to make sure all data is copied from the start CopyFrom(OutputStream, OutputStream.Size); Free end; // Or the data can be shown on screen with TStringList.Create do begin OutputStream.Position := 0; // Required to make sure all data is copied from the start LoadFromStream(OutputStream); writeln(Text); writeln('--- Number of lines = ', Count, '----'); Free end; // Clean up OutputStream.Free;end.注意,上面应使用 RunCommand 完成:
var s: string;...RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);当创建一个跨平台程序,指定的操作系统可执行文件名称可以使用指令"{$IFDEF}"和{$ENDIF}"设置。
示例:
{...}AProcess := TProcess.Create(nil){$IFDEF WIN32} AProcess.Executable := 'calc.exe'; {$ENDIF}{$IFDEF LINUX} AProcess.Executable := FindDefaultExecutablePath('kcalc');{$ENDIF}AProcess.Execute;{...}可以凭借 TProces s通过在软件包中启动可执行文件开始一个应用程序包。 例如:
AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';这将开始 Calendar,但是窗口将在当前应用程序的后面。为在前台获取应用程序,你可以使用 open 带有-n参数:
AProcess.Executable:='/usr/bin/open';AProcess.Parameters.Add('-n');AProcess.Parameters.Add('-a'); //optional: to hide terminal - similar to Windows option poNoConsoleAProcess.Parameters.Add('/Application/iCal.app');如果应用程序需要参数,可以传送 open 程序的 --args 参数,将所有的参数传送到应用程序。
AProcess.Parameters.Add('--args');AProcess.Parameters.Add('argument1');AProcess.Parameters.Add('argument2');一般的,由应用程序开始的程序是一个子进程,当你的应用程序被杀死时,子进程亦被杀死。当我们想运行一个独立的保持运行的程序,你可以使用下面的代码:
var Process: TProcess; I: Integer;begin Process := TProcess.Create(nil); try Process.InheritHandles := False; Process.Options := []; Process.ShowWindow := swoShow; // Copy default environment variables including DISPLAY variable for GUI application to work for I := 1 to GetEnvironmentVariableCount do Process.Environment.Add(GetEnvironmentString(I)); Process.Executable := '/usr/bin/gedit'; Process.Execute; finally Process.Free; end;end;有时,我们想运行更复杂的命令,命令传输它的数据到另一个命令或文件。类似于
ShellExecute('firstcommand.exe | secondcommand.exe');或
ShellExecute('dir > output.txt');使用 TProcess 执行这样的命令将不工作,例如:
// this won't workProcess.CommandLine := 'firstcommand.exe | secondcommand.exe'; Process.Execute;为什么使用指定运算符到重定向输出不工作?TProcess ,它不是一个 shell 环境,仅是一个进程 process(进程)。它不是两个进程,它仅是一个。
如何使用TProcess 重定向输出呢?可以为每一个命令使用一个 TProcess 实例重定向一个命令的输出到另一个命令。以下是一个示例,解释重定向一个进程的输出到另一个。为重定向一个进程的输出到一个文件/流,不仅可以重定向"正常"输出(也被称为stdout),也可以重定向错误输出(stderr),如果你指定 poStderrToOutPut 选项,像在第二个进程选项中所看到的。
program Project1; uses Classes, sysutils, process; var FirstProcess, SecondProcess: TProcess; Buffer: array[0..127] of char; ReadCount: Integer; ReadSize: Integer;begin FirstProcess := TProcess.Create(nil); SecondProcess := TProcess.Create(nil); FirstProcess.Options := [poUsePipes]; FirstProcess.Executable := 'pwd'; SecondProcess.Options := [poUsePipes,poStderrToOutPut]; SecondProcess.Executable := 'grep'; SecondProcess.Parameters.Add(DirectorySeparator+ ' -'); // this would be the same as "pwd | grep / -" FirstProcess.Execute; SecondProcess.Execute; while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do begin if FirstProcess.Output.NumBytesAvailable > 0 then begin // make sure that we don't read more data than we have allocated // in the buffer ReadSize := FirstProcess.Output.NumBytesAvailable; if ReadSize > SizeOf(Buffer) then ReadSize := SizeOf(Buffer); // now read the output into the buffer ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize); // and write the buffer to the second process SecondProcess.Input.Write(Buffer[0], ReadCount); // if SecondProcess writes much data to it's Output then // we should read that data here to prevent a deadlock // see the previous example "Reading Large Output" end; end; // Close the input on the SecondProcess // so it finishes processing it's data SecondProcess.CloseInput; // and wait for it to complete // be carefull what command you run because it may not exit when // it's input is closed and the following line may loop forever while SecondProcess.Running do Sleep(1); // that's it! the rest of the program is just so the example // is a little 'useful' // we will reuse Buffer to output the SecondProcess's // output to *this* programs stdout WriteLn('Grep output Start:'); ReadSize := SecondProcess.Output.NumBytesAvailable; if ReadSize > SizeOf(Buffer) then ReadSize := SizeOf(Buffer); if ReadSize > 0 then begin ReadCount := SecondProcess.Output.Read(Buffer, ReadSize); WriteLn(Copy(Buffer,0, ReadCount)); end else WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus); WriteLn('Grep output Finish:'); // free our process objects FirstProcess.Free; SecondProcess.Free;end.这是全部。现在可以从一个程序重定向输出到另一个。
| 留言与评论(共有 0 条评论) “” |