如何调用应用程序并等待其退出的高质量代码示例 [英] Good quality code example of how to call an application and wait for it to exit

查看:72
本文介绍了如何调用应用程序并等待其退出的高质量代码示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用:Delphi XE2; Windows 32位VCL应用程序



在我的Delphi应用程序中,我需要使用ShellExecute调用一个应用程序并等待其完成,然后再继续操作。 b

我在ShellExecute的SO上看到了许多带有MsgWaitForMultipleObjects的示例,但不知道哪一个是最好的,因为它们大多在做不建议的操作。



我看到 NFX 在此帖子中,此处未使用Application.ProcessMessages,但不确定是否正确或最佳,因此这个问题。



如果您能提供高质量的代码示例,将感到非常高兴。



TIA提供任何答案。

解决方案

我使用这些函数异步执行子进程,并在进程终止时回叫它。它的工作方式是创建一个线程,该线程等待该过程终止,然后通过给定的event方法调用主程序线程。请注意,您的程序会在子进程运行时继续运行,因此您需要某种形式的逻辑来防止无限生成子进程。

  UNIT SpawnFuncs; 

接口

{$ IF CompilerVersion> = 20}
{$ DEFINE ANONYMOUS_METHODS}
{$ ELSE}
{$ UNDEF ANONYMOUS_METHODS}
{$ ENDIF}

TYPE
TSpawnAction =(saStarted,saEnded);
TSpawnArgs = RECORD
Action:TSpawnAction;
FileName:字符串;
PROCEDURE Initialize(Act:TSpawnAction; CONST FN:String);排队;
CLASS FUNCTION Create(Act:TSpawnAction; CONST FN:String):TSpawnArgs;静态的;
END;
{$ IFDEF ANONYMOUS_METHODS}
TSpawnEvent =引用程序(发送方:TObject; CON Args:TSpawnArgs);
{$ ELSE}
TSpawnEvent = PROCEDURE(Sender:TObject; CONST Args:TSpawnArgs)对象;
{$ ENDIF}

功能ShellExec(CONST FileName,Tail:String; Event:TSpawnEvent = NIL; Sender:TObject = NIL):BOOLEAN;超载;
功能ShellExec(CONST FileName:String;事件:TSpawnEvent = NIL;发件人:TObject = NIL):BOOLEAN;超载;
功能ShellExec(CONST FileName:String; VAR EndedFlag:BOOLEAN):BOOLEAN;超载;
功能ShellExec(CONST FileName,Tail:String; VAR EndedFlag:BOOLEAN):BOOLEAN;超载;

过程ShellExecExcept(CONST FileName:String;事件:TSpawnEvent = NIL;发件人:TObject = NIL);重载:
过程ShellExecExcept(CONST FileName,Tail:String;事件:TSpawnEvent = NIL;发件人:TObject = NIL);超载;
过程ShellExecExcept(CONST FileName:String; VAR EndedFlag:BOOLEAN);超载;
过程ShellExecExcept(CONST FileName,Tail:String; VAR EndedFlag:BOOLEAN);超载;

实现

使用Windows,SysUtils,Classes,ShellApi;

类型
TWaitThread = CLASS(TThread)
构造函数Create(CONST FileName:String; ProcessHandle:THandle; Event:TSpawnEvent; Sender:TObject);再介绍;超载;
CONSTRUCTOR Create(CONST FileName:String; ProcessHandle:THandle; EndedFlag:PBoolean);超载;
程序执行;超越
PROCEDURE DoEvent(Action:TSpawnAction);
PRIVATE
处理:THandle;
事件:TSpawnEvent;
EndedFlag:PBoolean;
FN:字符串;
发件人:TObject;
{$ IFNDEF ANONYMOUS_METHODS}
Args:TSpawnArgs;
PROCEDURE RunEvent;
{$ ENDIF}
END;

构造函数TWaitThread.Create(CONST FileName:String; ProcessHandle:THandle;事件:TSpawnEvent;发送方:TObject);
BEGIN
继承Create(TRUE);
Handle:= ProcessHandle; Self.Event:=事件; FN:= FileName; Self.Sender:=发送者; FreeOnTerminate:= TRUE;
恢复
结束;

{$ IFNDEF ANONYMOUS_METHODS}
过程TWaitThread.RunEvent;
BEGIN
Event(Sender,Args)
END;
{$ ENDIF}

构造函数TWaitThread.Create(CONST FileName:String; ProcessHandle:THandle; EndedFlag:PBoolean);
BEGIN
继承Create(TRUE);
Handle:= ProcessHandle; EndedFlag ^:= FALSE; Self.EndedFlag:= EndedFlag; FreeOnTerminate:= TRUE;
恢复
结束;

过程TWaitThread.DoEvent(Action:TSpawnAction);
开始
如果已分配(EndedFlag)然后
EndedFlag ^:=(动作= saEnded)
其他开始
{$ IFDEF ANONYMOUS_METHODS}
同步(操作步骤BEGIN Event(Sender,TSpawnArgs.Create(Action,FN))END)
{$ ELSE}
Args:= TSpawnArgs.Create(Action,FN);
Synchronize(RunEvent)
{$ ENDIF}
END
END;

过程TWaitThread.Execute;
开始
DoEvent(saStarted);
WaitForSingleObject(Handle,INFINITE);
CloseHandle(Handle);
DoEvent(saEnded)
END;

功能ShellExec(CONST FileName,Tail:String;事件:TSpawnEvent;发送方:TObject; EndedFlag:PBoolean):BOOLEAN;超载;
VAR
信息:TShellExecuteInfo;
PTail:PChar;

开始
ASSERT(不(已分配(事件)和已分配(EndedFlag)),’ShellExec调用了Event和EndedFlag!);
IF Tail =’’THEN PTail:= NIL ELSE PTail:= PChar(Tail);
FillChar(Info,SizeOf(TShellExecuteInfo),0);
Info.cbSize:= SizeOf(TShellExecuteInfo);
Info.fMask:= SEE_MASK_FLAG_NO_UI;
Info.lpFile:= PChar(FileName);
Info.lpParameters:= PTail;
Info.nShow:= SW_SHOW;
如果不是(已分配(事件)或已分配(EndedFlag)),则
结果:= ShellExecuteEx(@Info)
否则开始
Info.fMask:= Info.fMask或SEE_MASK_NOCLOSEPROCESS ;
结果:= ShellExecuteEx(@Info)AND(Info.hProcess> 0);
IF结果然后
如果已分配(事件)然后
TWaitThread.Create(FileName,Info.hProcess,Event,Sender)
ELSE
TWaitThread.Create(FileName, Info.hProcess,EndedFlag)
END
END;

功能ShellExec(CONST FileName,Tail:String;事件:TSpawnEvent = NIL;发件人:TObject = NIL):BOOLEAN;
BEGIN
结果:= ShellExec(FileName,Tail,Event,Sender,NIL)
END;

功能ShellExec(CONST FileName:String;事件:TSpawnEvent = NIL;发件人:TObject = NIL):BOOLEAN;
开始
结果:= ShellExec(FileName,’,Event,Sender)
END;

功能ShellExec(CONST FileName,Tail:String; VAR EndedFlag:BOOLEAN):BOOLEAN;
BEGIN
结果:= ShellExec(FileName,Tail,NIL,NIL,@ EndedFlag)
END;

功能ShellExec(CONST FileName:String; VAR EndedFlag:BOOLEAN):BOOLEAN;
开始
结果:= ShellExec(FileName,’,EndedFlag)
END;

过程ShellExecExcept(CONST FileName:String;事件:TSpawnEvent = NIL;发件人:TObject = NIL);
开始
如果不是ShellExec(FileName,Event,Sender)然后RaiseLastOSError
END;

过程ShellExecExcept(CONST FileName,Tail:String;事件:TSpawnEvent = NIL;发送者:TObject = NIL);
开始
如果不是ShellExec(FileName,Tail,Event,Sender)然后RaiseLastOSError
END;

过程ShellExecExcept(CONST FileName:String; VAR EndedFlag:BOOLEAN);
开始
如果不是ShellExec(FileName,EndedFlag)然后RaiseLastOSError
END;

程序ShellExecExcept(CONST FileName,Tail:String; VAR EndedFlag:BOOLEAN);
开始
如果不是ShellExec(FileName,Tail,EndedFlag)然后RaiseLastOSError
END;

{TSpawnArgs}

类功能TSpawnArgs.Create(Act:TSpawnAction; CONST FN:String):TSpawnArgs;
BEGIN
结果.Initialize(Act,FN)
END;

过程TSpawnArgs.Initialize(Act:TSpawnAction; CONST FN:String);
开始
行动:=行动; FileName:= FN
END;

结束。

按以下方式使用:

 使用SpawnFuncs; 

ShellExec(ProgramToRun,CommandLineArgs,Event,Sender)

  ShellExec(ProgramToRunOrFileToOpen,Event,Sender)

其中

  ProgramToRun =要运行的程序的名称
ProgramToRunOrFileToOpen =要运行的程序,或要打开的文件(例如.TXT文件)
CommandLineArgs =传递给程序
的命令行参数事件=在程序$开始和终止时运行的(可能是匿名的)方法b $ b Sender =将参数Sender传递给方法

或者,如果您只是感兴趣知道子进程何时终止,有两个简化版本可以接受BOOLEAN变量,该变量将在子程序终止后立即设置为TRUE。您无需先将其设置为FALSE,因为它将自动完成:

  ShellExec(ProgramToRun,ChildProcessEnded); 

如果您不提供事件处理程序或BOOLEAN变量,则ShellExec过程只需运行/打开



如果不提供Sender,则在事件处理程序中将未定义Sender参数。



事件处理程序必须是具有以下签名的方法(匿名或其他):

  PROCEDURE SpawnEvent (发件人:TObject; CONST Args:TSpawnArgs); 

其中Args包含以下字段:

  Action = saStarted或saEnded 
FileName =传递给ShellExec的文件名

如果您更喜欢使用SEH(结构化异常处理)而不是错误返回值,则可以使用ShellExecExcept PROCEDURE代替ShellExec FUNCTIONs。如果执行请求失败,则会引发OS错误。


Using: Delphi XE2; Windows 32-bit VCL application

From within my Delphi application, I need to call an application using ShellExecute and wait until it finishes before proceeding.

I see many examples here on SO of ShellExecute with MsgWaitForMultipleObjects but can't know which one is the best because they are mostly doing what is not recommended ie. also using Application.ProcessMessages which is not recommended by many.

I see an answer by NFX here in this post which does not use Application.ProcessMessages, but am not sure if it is correct or optimum, and hence this question.

Would be glad if you could provide a good quality code sample.

TIA for any answers.

解决方案

I use these functions to execute a child process asynchronously and have it call back when the process terminates. It works by creating a thread that waits until the process terminates and then calls back to the main program thread via the event method given. Beware, that your program continues to run while the child process is running, so you'll need some form of logic to prevent an infinite occurence of spawning child processes.

UNIT SpawnFuncs;

INTERFACE

{$IF CompilerVersion >= 20 }
  {$DEFINE ANONYMOUS_METHODS }
{$ELSE }
  {$UNDEF ANONYMOUS_METHODS }
{$ENDIF }

TYPE
  TSpawnAction  = (saStarted,saEnded);
  TSpawnArgs    = RECORD
                    Action      : TSpawnAction;
                    FileName    : String;
                    PROCEDURE   Initialize(Act : TSpawnAction ; CONST FN : String); INLINE;
                    CLASS FUNCTION Create(Act : TSpawnAction ; CONST FN : String) : TSpawnArgs; static;
                  END;
  {$IFDEF ANONYMOUS_METHODS }
    TSpawnEvent = REFERENCE TO PROCEDURE(Sender : TObject ; CONST Args : TSpawnArgs);
  {$ELSE }
    TSpawnEvent = PROCEDURE(Sender : TObject ; CONST Args : TSpawnArgs) OF OBJECT;
  {$ENDIF }

FUNCTION ShellExec(CONST FileName,Tail : String ; Event : TSpawnEvent = NIL ; Sender : TObject = NIL) : BOOLEAN; OVERLOAD;
FUNCTION ShellExec(CONST FileName : String ; Event : TSpawnEvent = NIL ; Sender : TObject = NIL) : BOOLEAN; OVERLOAD;
FUNCTION ShellExec(CONST FileName : String ; VAR EndedFlag : BOOLEAN) : BOOLEAN; OVERLOAD;
FUNCTION ShellExec(CONST FileName,Tail : String ; VAR EndedFlag : BOOLEAN) : BOOLEAN; OVERLOAD;

PROCEDURE ShellExecExcept(CONST FileName : String ; Event : TSpawnEvent = NIL ; Sender : TObject = NIL); OVERLOAD:
PROCEDURE ShellExecExcept(CONST FileName,Tail : String ; Event : TSpawnEvent = NIL ; Sender : TObject = NIL); OVERLOAD;
PROCEDURE ShellExecExcept(CONST FileName : String ; VAR EndedFlag : BOOLEAN); OVERLOAD;
PROCEDURE ShellExecExcept(CONST FileName,Tail : String ; VAR EndedFlag : BOOLEAN); OVERLOAD;

IMPLEMENTATION

USES Windows,SysUtils,Classes,ShellApi;

TYPE
  TWaitThread   = CLASS(TThread)
                    CONSTRUCTOR Create(CONST FileName : String ; ProcessHandle : THandle ; Event : TSpawnEvent ; Sender : TObject); REINTRODUCE; OVERLOAD;
                    CONSTRUCTOR Create(CONST FileName : String ; ProcessHandle : THandle ; EndedFlag : PBoolean); OVERLOAD;
                    PROCEDURE   Execute; OVERRIDE;
                    PROCEDURE   DoEvent(Action : TSpawnAction);
                  PRIVATE
                    Handle      : THandle;
                    Event       : TSpawnEvent;
                    EndedFlag   : PBoolean;
                    FN          : String;
                    Sender      : TObject;
                    {$IFNDEF ANONYMOUS_METHODS }
                      Args      : TSpawnArgs;
                      PROCEDURE RunEvent;
                    {$ENDIF }
                  END;

CONSTRUCTOR TWaitThread.Create(CONST FileName : String ; ProcessHandle : THandle ; Event : TSpawnEvent ; Sender : TObject);
  BEGIN
    INHERITED Create(TRUE);
    Handle:=ProcessHandle; Self.Event:=Event; FN:=FileName; Self.Sender:=Sender; FreeOnTerminate:=TRUE;
    Resume
  END;

{$IFNDEF ANONYMOUS_METHODS }
PROCEDURE TWaitThread.RunEvent;
  BEGIN
    Event(Sender,Args)
  END;
{$ENDIF }

CONSTRUCTOR TWaitThread.Create(CONST FileName : String ; ProcessHandle : THandle ; EndedFlag : PBoolean);
  BEGIN
    INHERITED Create(TRUE);
    Handle:=ProcessHandle; EndedFlag^:=FALSE; Self.EndedFlag:=EndedFlag; FreeOnTerminate:=TRUE;
    Resume
  END;

PROCEDURE TWaitThread.DoEvent(Action : TSpawnAction);
  BEGIN
    IF Assigned(EndedFlag) THEN
      EndedFlag^:=(Action=saEnded)
    ELSE BEGIN
      {$IFDEF ANONYMOUS_METHODS }
        Synchronize(PROCEDURE BEGIN Event(Sender,TSpawnArgs.Create(Action,FN)) END)
      {$ELSE }
        Args:=TSpawnArgs.Create(Action,FN);
        Synchronize(RunEvent)
      {$ENDIF }
    END
  END;

PROCEDURE TWaitThread.Execute;
  BEGIN
    DoEvent(saStarted);
    WaitForSingleObject(Handle,INFINITE);
    CloseHandle(Handle);
    DoEvent(saEnded)
  END;

FUNCTION ShellExec(CONST FileName,Tail : String ; Event : TSpawnEvent ; Sender : TObject ; EndedFlag : PBoolean) : BOOLEAN; OVERLOAD;
  VAR
    Info  : TShellExecuteInfo;
    PTail : PChar;

  BEGIN
    ASSERT(NOT (Assigned(Event) AND Assigned(EndedFlag)),'ShellExec called with both Event and EndedFlag!');
    IF Tail='' THEN PTail:=NIL ELSE PTail:=PChar(Tail);
    FillChar(Info,SizeOf(TShellExecuteInfo),0);
    Info.cbSize:=SizeOf(TShellExecuteInfo);
    Info.fMask:=SEE_MASK_FLAG_NO_UI;
    Info.lpFile:=PChar(FileName);
    Info.lpParameters:=PTail;
    Info.nShow:=SW_SHOW;
    IF NOT (Assigned(Event) OR Assigned(EndedFlag)) THEN
      Result:=ShellExecuteEx(@Info)
    ELSE BEGIN
      Info.fMask:=Info.fMask OR SEE_MASK_NOCLOSEPROCESS;
      Result:=ShellExecuteEx(@Info) AND (Info.hProcess>0);
      IF Result THEN
        IF Assigned(Event) THEN
          TWaitThread.Create(FileName,Info.hProcess,Event,Sender)
        ELSE
          TWaitThread.Create(FileName,Info.hProcess,EndedFlag)
    END
  END;

FUNCTION ShellExec(CONST FileName,Tail : String ; Event : TSpawnEvent = NIL ; Sender : TObject = NIL) : BOOLEAN;
  BEGIN
    Result:=ShellExec(FileName,Tail,Event,Sender,NIL)
  END;

FUNCTION ShellExec(CONST FileName : String ; Event : TSpawnEvent = NIL ; Sender : TObject = NIL) : BOOLEAN;
  BEGIN
    Result:=ShellExec(FileName,'',Event,Sender)
  END;

FUNCTION ShellExec(CONST FileName,Tail : String ; VAR EndedFlag : BOOLEAN) : BOOLEAN;
  BEGIN
    Result:=ShellExec(FileName,Tail,NIL,NIL,@EndedFlag)
  END;

FUNCTION ShellExec(CONST FileName : String ; VAR EndedFlag : BOOLEAN) : BOOLEAN;
  BEGIN
    Result:=ShellExec(FileName,'',EndedFlag)
  END;

PROCEDURE ShellExecExcept(CONST FileName : String ; Event : TSpawnEvent = NIL ; Sender : TObject = NIL);
  BEGIN
    IF NOT ShellExec(FileName,Event,Sender) THEN RaiseLastOSError
  END;

PROCEDURE ShellExecExcept(CONST FileName,Tail : String ; Event : TSpawnEvent = NIL ; Sender : TObject = NIL);
  BEGIN
    IF NOT ShellExec(FileName,Tail,Event,Sender) THEN RaiseLastOSError
  END;

PROCEDURE ShellExecExcept(CONST FileName : String ; VAR EndedFlag : BOOLEAN);
  BEGIN
    IF NOT ShellExec(FileName,EndedFlag) THEN RaiseLastOSError
  END;

PROCEDURE ShellExecExcept(CONST FileName,Tail : String ; VAR EndedFlag : BOOLEAN);
  BEGIN
    IF NOT ShellExec(FileName,Tail,EndedFlag) THEN RaiseLastOSError
  END;

{ TSpawnArgs }

CLASS FUNCTION TSpawnArgs.Create(Act : TSpawnAction ; CONST FN : String) : TSpawnArgs;
  BEGIN
    Result.Initialize(Act,FN)
  END;

PROCEDURE TSpawnArgs.Initialize(Act : TSpawnAction ; CONST FN : String);
  BEGIN
    Action:=Act; FileName:=FN
  END;

END.

Use it as follows:

USES SpawnFuncs;

ShellExec(ProgramToRun,CommandLineArgs,Event,Sender)

or

ShellExec(ProgramToRunOrFileToOpen,Event,Sender)

where

ProgramToRun = Name of program to run
ProgramToRunOrFileToOpen = Program to run, or file to open (f.ex. a .TXT file)
CommandLineArgs = Command line parameters to pass to the program
Event = The (perhaps anonymous) method to run upon start and termination of program
Sender = The Sender parameter to pass to the method

Or, if you are simply interested in knowing when the child process has terminated, there are two simplified versions that accept a BOOLEAN variable that will be set to TRUE as soon as the child program terminates. You don't need to set it to FALSE first, as it will be done automatically:

ShellExec(ProgramToRun,ChildProcessEnded);

If you don't supply an event handler or BOOLEAN variable, the ShellExec procedure simply runs/opens the file given and performs no callback.

If you don't supply a Sender, the Sender parameter will be undefined in the event handler.

The event handler must be a method (anonymous or otherwise) with the following signature:

PROCEDURE SpawnEvent(Sender : TObject ; CONST Args : TSpawnArgs);

where Args contains the following fields:

Action = either saStarted or saEnded
FileName = the name of the file that passed to ShellExec

If you prefer to use SEH (Structured Exception Handling) instead of error return values, you can use the ShellExecExcept PROCEDUREs instead of the ShellExec FUNCTIONs. These will raise an OS Error in case the execute request failed.

这篇关于如何调用应用程序并等待其退出的高质量代码示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆