使用:Delphi XE2; Windows 32位VCL应用程序
在我的Delphi应用程序中,我需要使用ShellExecute调用一个应用程序并等到它完成后再继续。
我在ShellExecute的SO上看到了很多关于MsgWaitForMultipleObjects的例子,但是不知道哪一个是最好的,因为他们大多是做不推荐的事情,即。还使用了许多人不推荐的Application.ProcessMessages。
我在NFX中看到post的答案,它不使用Application.ProcessMessages,但不确定它是否正确或最佳,因此这个问题。
如果您能提供高质量的代码示例,我们将非常高兴。
任何答案的TIA。
答案 0 :(得分:4)
ShellExecuteEx()
和CreateProcess()
都会返回您可以等待的HANDLE。当产生的进程退出时,HANDLE会发出信号。
如果您必须在等待时抽取消息队列,请使用MsgWaitForMultipleObjects()
检测新消息何时等待处理。否则,您可以改为使用WaitForSingleObject()
。
NFX的回答并没有使用Application.ProcessMessages()
,但仍会提供消息,因此根本问题仍然存在。如果您在主线程中进行等待,除非您不介意向用户(或操作系统)呈现无响应的UI,否则无法避免这种情况。您可以选择在工作线程中等待,然后您不需要在等待时抽取消息,并且您的UI不会被阻止。如果需要,您可以在等待时禁用UI或显示状态UI。
答案 1 :(得分:2)
function StartProcess(Exename: string; CmdLineArgs: string = ''; ShowWindow: boolean = True;
WaitForFinish: boolean = False): integer;
var
StartInfo: TStartupInfo;
ProcInfo: TProcessInformation;
CreateOK: boolean;
begin
// Simple wrapper for the CreateProcess command
// returns the process id of the started process.
FillChar(StartInfo, SizeOf(TStartupInfo), #0);
FillChar(ProcInfo, SizeOf(TProcessInformation), #0);
StartInfo.cb := SizeOf(TStartupInfo);
if not(ShowWindow) then
begin
StartInfo.dwFlags := STARTF_USESHOWWINDOW;
StartInfo.wShowWindow := SW_HIDE;
end;
CreateOK := CreateProcess(nil, PChar(Exename + ' ' + CmdLineArgs), nil, nil, False,
CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS, nil, nil, StartInfo, ProcInfo);
Result := ProcInfo.dwProcessId;
if CreateOK then
begin
// may or may not be needed. Usually wait for child processes
if WaitForFinish then
WaitForSingleObject(ProcInfo.hProcess, Infinite);
end
else
begin
// ShowMessage('Unable to run '+ProgramName);
SysErrorMessage(GetLastError());
end;
// close process & thread handles
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
end;
答案 2 :(得分:2)
我使用这些函数异步执行子进程,并在进程终止时回调它。它的工作原理是创建一个等待进程终止的线程,然后通过给定的事件方法回调主程序线程。请注意,您的程序在子进程运行时继续运行,因此您需要某种形式的逻辑来防止产生子进程的无限发生。
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.
按如下方式使用:
USES SpawnFuncs;
ShellExec(ProgramToRun,CommandLineArgs,Event,Sender)
或
ShellExec(ProgramToRunOrFileToOpen,Event,Sender)
,其中
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
或者,如果您只是想知道子进程何时终止,则有两个简化版本接受BOOLEAN变量,一旦子程序终止,该变量将设置为TRUE。您不需要先将其设置为FALSE,因为它将自动完成:
ShellExec(ProgramToRun,ChildProcessEnded);
如果您不提供事件处理程序或BOOLEAN变量,则ShellExec过程只是运行/打开给定的文件并且不执行回调。
如果您不提供发件人,则Sender参数将在事件处理程序中未定义。
事件处理程序必须是具有以下签名的方法(匿名或其他方式):
PROCEDURE SpawnEvent(Sender : TObject ; CONST Args : TSpawnArgs);
其中Args包含以下字段:
Action = either saStarted or saEnded
FileName = the name of the file that passed to ShellExec
如果您更喜欢使用SEH(结构化异常处理)而不是错误返回值,则可以使用ShellExecExcept PROCEDURE而不是ShellExec FUNCTION。如果执行请求失败,这些将引发操作系统错误。