How do I properly use the WaitForSingleObject method to wait for an external program to terminate?
I'm trying to launch an external application with elevated status, and wait until it exits before continuing:
var
FProcess: THandle;
ExecInfo: TShellExecuteInfo;
begin
FillChar(ExecInfo, SizeOf(ExecInfo), 0);
with ExecInfo do
begin
cbSize := SizeOf(ExecInfo);
fMask := 0;
Wnd := AWindow;
lpVerb := 'runas';
lpFile := PChar(APath);
lpParameters := PChar(AParams);
lpDirectory := PChar(AWorkDir);
nShow := SW_NORMAL;
end;
Result := ShellExecuteEx(@ExecInfo);
if Wait then
begin
while WaitForSingleObject(ExecInfo.hProcess, INFINITE) <> WAIT_TIMEOUT do
Application.ProcessMessages;
end;
This launches, but it just keeps waiting. The calling program never continues past the call to WaitForSingleObject, 开发者_开发百科even after the called program exits.
I've tried WAIT_OBJECT_0 instead of WAIT_TIMEOUT, but I have the same problem. What am I doing wrong here?
What the code
while WaitForSingleObject(ExecInfo.hProcess, INFINITE) <> WAIT_TIMEOUT do
Application.ProcessMessages;
is supposed to do? It is an infinite loop.
Use just
WaitForSingleObject(ExecInfo.hProcess, INFINITE);
instead. And yes, you need
fMask:= SEE_MASK_NOCLOSEPROCESS;
to obtain the process handle.
Your code is broken. You are not passing the SEE_MASK_NOCLOSEPROCESS
flag to ShellExecuteEx()
, so it will not return a valid process handle to you, and your loop is ignoring the errors that WaitForSingleObject()
tells you because of that, so you end up in an endless loop.
Try this instead:
var
ExecInfo: TShellExecuteInfo;
begin
ZeroMemory(@ExecInfo, SizeOf(ExecInfo));
with ExecInfo do
begin
cbSize := SizeOf(ExecInfo);
fMask := SEE_MASK_NOCLOSEPROCESS;
Wnd := AWindow;
lpVerb := 'runas';
lpFile := PChar(APath);
lpParameters := PChar(AParams);
lpDirectory := PChar(AWorkDir);
nShow := SW_NORMAL;
end;
Result := ShellExecuteEx(@ExecInfo);
if Result and Wait then
begin
if ExecInfo.hProcess <> 0 then // no handle if the process was activated by DDE
begin
repeat
if MsgWaitForMultipleObjects(1, ExecInfo.hProcess, FALSE, INFINITE, QS_ALLINPUT) = (WAIT_OBJECT_0+1) then
Application.ProcessMessages
else
Break;
until False;
CloseHandle(ExecInfo.hProcess);
end;
end;
end;
If you read description of ShellExecuteEx in MSDN, you will see this:
hProcess
Type: HANDLE
A handle to the newly started application. This member is set on return and is always NULL unless fMask is set to SEE_MASK_NOCLOSEPROCESS. Even if fMask is set to SEE_MASK_NOCLOSEPROCESS, hProcess will be NULL if no process was launched.
I.e. you simply don't have a valid handle. You need to set fMask as written above.
精彩评论