常规的命令行调用,只能在一条命令执行完后,才能获取完整输出,而如果命令执行耗时很久,且选择了阻塞线程,就会很慢,出现卡死的假象,此时就要用到异步执行命令。
先放一篇官方文章,给出了各种命令行调用程序的方式。
Executing External Programs/zh CN - Free Pascal wiki
总结来看有以下几种方式
方法 | 库 | 平台 | 可单行完成 | 功能 |
---|---|---|---|---|
ExecuteProcess | RTL | 跨平台 | 是 | 非常有限,以同步方式执行 |
ShellExecute | WinAPI | 仅限微软的 Windows 系统 | 是 | 很多。能够提权启动需管理员权限的程序。 |
fpsystem、fpexecve | Unix | 仅限 Unix 系统 | ||
TProcess | FCL | 跨平台 | 否 | 完整 |
RunCommand | FCL | 跨平台 需要FPC 2.6.2+ | 是 | 支持常用的 TProcess 用法 |
OpenDocument | LCL | 跨平台 | 是 | 只能打开文档。将会用该文档类型关联的应用程序打开。 |
windows平台我常用的是ShellExecute ,是标准的微软 Windows 函数(ShellApi.h),documentation on MSDN 中有完善的文档。
uses ..., ShellApi;
// 简单的单行代码调用(忽略返回错误):
if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) =0 then;
// 执行批命令:
if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) =0 then;
// 在给定目录打开命令行窗口:
if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) =0 then;
// 用“start”命令在默认浏览器中打开 URL(通过短暂显示并隐藏的 cmd 窗口):
if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;
// 助手函数:
procedure RunShellExecute(const prog,params:string);
begin
// ( Handle, nil/'open'/'edit'/'find'/'explore'/'print', // 'open' 不是必需的
// path+prog, params, working folder,
// 0=hide / 1=SW_SHOWNORMAL / 3=max / 7=min) //要使用 SW_ constants 请 uses Windows 单元
if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //success
// 返回 0..32 表示出错
end;
跨平台则考虑Runcommand
function TMainForm.GetPDFPageCount(const PDFPath: string): integer;
var
success: boolean;
outs: string;
begin
Result := 0;
success := RunCommand('cmd.exe', ['/c', 'pdftk.exe ' + Utf8ToAnsi(PDFPath) + ' dump_data'], outs, [poNewProcessGroup, poUsePipes], swoHIDE);
if success then
begin
Result := ExtractNumberOfPages(outs);
//memLog.Lines.Add(pdfpath + '有' + IntToStr(Result) + '页');
if Result = -1 then
Result := 0;
end;
end;
但以上这些,都不能异步。
异步请使用 TAsyncProcess
// 异步线程输出更新
procedure TMainForm.AsyncProcess1ReadData(Sender: TObject);
var
AsyncProcess: TAsyncProcess absolute Sender;
aString: string = '';
Len: integer;
begin
MainForm.Cursor := crHourGlass;
Len := AsyncProcess.Output.NumBytesAvailable;
Setlength(aString, Len);
Len := AsyncProcess.Output.Read(aString[1], Len);
astring := Trim(aString);
if Pos('Analysis', astring) > 0 then
begin
UpdateProgress(40);
LogOutput('正在分析Python文件...');
end
else if Pos('Building', astring) > 0 then
begin
UpdateProgress(60);
LogOutput('正在构建EXE文件...');
end
else if Pos('Copying', astring) > 0 then
begin
UpdateProgress(80);
LogOutput('正在复制必要文件...');
end
else if (Pos('ERROR:', astring) > 0) or (Pos('Error:', astring) > 0) then
LogOutput('错误: ' + astring);
Application.ProcessMessages;
end;
ASyncProcess1.CommandLine := Cmd;
ASyncProcess1.Options := [poUsePipes, poStdErrToOutPut];
ASyncProcess1.Execute;