网站首页 > 技术文章 正文
3.Lazarus执行外部程序之TProcess
可以使用 TProcess 来启动外部程序。使用 TProcess 的一些好处:
- 平台独立
- 从stdout读和写到stdin的能力
- 能做到等待一个命令完成或当你的程序移动时让它运行
重要注释:
- TProcess 不是一个 terminal(控制台)/shell,不能直接执行 scripts(脚本),或使用运算符像"|", ">","<", "&" 等等重定向输出,使用 pascal 的 TProcess 获得相同的结果 是可能的,下面有一些示例。
- 很可能在Linux/Unix上必须指定完整的路径到可执行文件。例如:'/bin/cp' 代替 'cp' 。如果程序是在标准的PATH(路径)上,那么可以从 LCL 的单元 FileUtil,使用函数 FindDefaultExecutablePath。
- 在Windows上,如果该命令是在 PATH 路径中,你不需要指定完整的路径。
3.1 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) |
3.2 一个简单的示例
这个示例仅显示如何运行一个外部程序,这不能使用在生产中,看大量输出。
// 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 run
begin
// 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.上面的程序学习从程序内部来运行一个外部程序。
3.3 一个提高的示例
如何读取一个运行程序的输出?好的,让我们稍微扩张我们的示例,并保持这个示例简单,从而可以从中学习。请不要在产品代码中使用这个示例,因为在代码中有大量输出。
// 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 run
begin
// 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.3.4 读大量输出
在前一个示例中,我们一直等到程序退出。然后,我们读取程序的输出。假设程序写很多数据到输出。然后,输出管道变满,并且调用程序等待,直到管道被读取。但是,调用程序不从它读,直到调用的程序被结束。一个停滞发生。
下面的示例因为不使用 poWaitOnExit 选项,但是,当程序仍然在运行时,从中输出读取。输出被存储在内存流中,随后可以被使用于读输出到一个 TStringList 中。如果我们想从一个外部进程中读输出,这是需要改写到产品使用的代码。
program LargeOutputDemo;
{$mode objfpc}{$H+}
uses
Classes, SysUtils, Process; // Process is the unit that holds TProcess
const
BUF_SIZE = 2048; // Buffer size for reading the output in chunks
var
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);3.5 关于TProcess的使用的提示
当创建一个跨平台程序,指定的操作系统可执行文件名称可以使用指令"{$IFDEF}"和{$ENDIF}"设置。
示例:
{...}
AProcess := TProcess.Create(nil)
{$IFDEF WIN32}
AProcess.Executable := 'calc.exe';
{$ENDIF}
{$IFDEF LINUX}
AProcess.Executable := FindDefaultExecutablePath('kcalc');
{$ENDIF}
AProcess.Execute;
{...}3.6 OS X 在前台显示应用程序包
可以凭借 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 poNoConsole
AProcess.Parameters.Add('/Application/iCal.app');如果应用程序需要参数,可以传送 open 程序的 --args 参数,将所有的参数传送到应用程序。
AProcess.Parameters.Add('--args');
AProcess.Parameters.Add('argument1');
AProcess.Parameters.Add('argument2');3.7 运行独立的程序
一般的,由应用程序开始的程序是一个子进程,当你的应用程序被杀死时,子进程亦被杀死。当我们想运行一个独立的保持运行的程序,你可以使用下面的代码:
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;3.8 替换shell运算符,像 "| < >"
有时,我们想运行更复杂的命令,命令传输它的数据到另一个命令或文件。类似于
ShellExecute('firstcommand.exe | secondcommand.exe');或
ShellExecute('dir > output.txt');使用 TProcess 执行这样的命令将不工作,例如:
// this won't work
Process.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.这是全部。现在可以从一个程序重定向输出到另一个。
猜你喜欢
- 2024-12-16 FFmpeg功能命令集合(超详细) ffmpeg 命令大全
- 2024-12-16 1.2 动态链接库与API 动态链接库原理
- 2024-12-16 QT开发经验详解 qt开发gui
- 2024-12-16 16k Star!一个开源的命令行视频播放器
- 2024-12-16 Qt多线程编程之QThread qt多线程例子
- 2024-12-16 「某CMS漏洞」SQL注入漏洞分析 sql注入漏洞代码
- 2024-12-16 Qt开发-Qt中的多线程编程 qt如何写多个线程
- 2024-12-16 k8s系列-06-containerd的基本操作
- 2024-12-16 实例讲解Simulink建立模型的版本管理
- 2024-12-16 网易视频云技术分享:Android 消息机制学习
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (77)
- vector线程安全吗 (73)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)
