等待ShellExecute执行完成后再继续操作?

9

我有一个希望很快得到回答的问题:是否可能稍微延迟ShellExecute的执行?

我有一个包含自动更新程序的应用程序。在它下载所有必要文件等后,将当前文件重命名为*.OLD,新文件作为前一个文件。非常简单。但是然后我需要删除那些.OLD文件。这个“清理”过程在MainForm.OnActivate上执行(检查是否为第一个激活进程)。但是这显然发生得太快了(我从DeleteFile中得到False)。这是该过程:

procedure TUpdateForm.OKBtnClick(Sender: TObject);
const SHELL = 'ping 127.0.0.1 -n 2';
begin
  ShellExecute(0,'open',pchar(SHELL+#13+Application.ExeName),nil,nil,SW_SHOWNORMAL);
  Application.Terminate;
end;

这个程序旨在重新启动应用程序。我确信删除问题是由第二个应用程序的快速启动引起的,因为如果我自己重新启动它,并给予一点时间,文件就可以正常删除。
简短版本:我需要调用ShellExecute(),等待一段时间(大约0.1秒),然后执行命令。
注意:我尝试使用-ping命令来延迟它,但它没有起作用。
非常感谢您的帮助。
编辑:重新表述
我需要这样的操作||第一个应用程序关闭;等待100毫秒;打开第二个应用程序||。我需要先调用ShellExecute,然后等待调用应用程序完全关闭,然后执行shell(即打开第二个应用程序)。

3
我不明白。你想等待100毫秒,然后执行ShellExecute吗?那么就这样做!Sleep(100); ShellExecute(...)。或者你想让ShellExecute 在新创建的进程退出之前不返回吗?如果是这样,理论上,你希望在ShellExecute之后而不是之前使用Sleep。但那是一个丑陋的解决方案。相反,你应该使用WaitForSingleObject或类似的方法。 - Andreas Rejbrand
1
我会尝试重新表述一下:我需要这个操作发生 || 第一个应用程序关闭;等待100毫秒;第二个应用程序打开 ||。我需要先调用ShellExecute,然后等待调用的应用程序完全关闭,然后执行shell(即打开第二个应用程序)-> 您提出的建议都不是我需要的。我需要ShellExecute在当前应用程序完全关闭之前 _不要执行_(以便我可以删除其文件)。 - Martin Melka
你不能这样做。在应用程序1关闭之后,但在应用程序2打开之前,你无法做任何事情,因为没有程序在运行。你可以调用某个程序,等待一段时间,然后再调用另一个程序,最后终止。但是,你应该使用WaitForSingleObject或其他更可靠的方法来代替固定的“睡眠”时间。 - Andreas Rejbrand
为什么不让ShellExecute调用第三方应用程序,该应用程序除了 sleep(1000); shellexecute(... 什么也不做呢? - Johan
@Johan 可能可以实现,但我觉得应该有更好的办法 :/ - Martin Melka
3个回答

6

你正在做一个自动修补程序,对吗?

我也遇到过同样的问题,这是我绕过它的方法:

你运行第二个应用程序,并使用参数"--delay"或类似的东西。 第二个应用程序处理参数“--delay”并休眠100毫秒,然后正常继续运行。


4
如果你同时将WTV文件复制到外部硬盘,那么100毫秒可能不够用,你仍然会失败。基于WaitForSingleObject的解决方案更好。或者,第二个应用程序可以在启动时执行while not done do sleep(100)或类似的操作。 - Andreas Rejbrand
当我运行我的“重新运行程序”时,我只需传递参数以复制文件(源、目标),然后如果由于应用程序仍在运行而失败,则重试。因此,这解决了时间问题,因为它只是稍微休眠一下,然后再次尝试。 - mj2008

3
这个程序是我们游戏引擎中的一些实用代码。它可以运行一个可执行文件,可选择等待其退出。它会返回其退出码:
function TSvUtils.FileExecute(ahWnd: Cardinal; const aFileName, aParams, aStartDir: string; aShowCmd: Integer; aWait: Boolean): Integer;
var
  Info: TShellExecuteInfo;
  ExitCode: DWORD;
begin

  Result := -1;
  FillChar(Info, SizeOf(Info), 0);
  Info.cbSize := SizeOf(TShellExecuteInfo);
  with Info do begin
    fMask := SEE_MASK_NOCLOSEPROCESS;
    Wnd := ahWnd;
    lpFile := PChar(aFileName);
    lpParameters := PChar(aParams);
    lpDirectory := PChar(aStartDir);
    nShow := aShowCmd;
  end;

  if ShellExecuteEx(@Info) then
  begin
    if aWait then
    begin
      repeat
        Sleep(1);
        Application.ProcessMessages;
        GetExitCodeProcess(Info.hProcess, ExitCode);
      until (ExitCode <> STILL_ACTIVE) or Application.Terminated;
      CloseHandle(Info.hProcess);
      Result := ExitCode;
    end;
  end
end;

这里有一些代码可以检查进程是否存在。因此...当前应用程序调用更新程序并终止。更新程序可以检查旧应用程序是否已终止并执行其任务(重命名、更新、删除等):

function TSvUtils.ProcessExists(const aExeFileName: string; aBringToForgound: Boolean=False): Boolean;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin

  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
  Result := False;
  while Integer(ContinueLoop) <> 0 do
  begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(aExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
      UpperCase(aExeFileName))) then
    begin
      if aBringToForgound then
        EnumWindows(@BringToForgroundEnumProcess, FProcessEntry32.th32ProcessID);
      Result := True;
    end;
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;

1
谢谢您的回答,但我觉得您误解了我的问题。我需要将一个可执行文件放入“查询”中,退出当前应用程序(调用者),然后从查询中运行该可执行文件。 - Martin Melka
好的...思考中...您目前的应用程序会启动更新程序来执行工作并退出。更新程序然后检查旧的应用程序是否已终止(我有可以用来检查此情况的代码),然后进行更新、重命名、删除等操作。完成后,它会启动新的应用程序并自行终止? - Simvector Technologies

2

如果你可以使用CreateProcess代替ShellExecute,你可以等待进程句柄。当应用程序退出时,进程句柄会发出信号。例如:

function ExecAndWait(APath: string; var VProcessResult: cardinal): boolean;
var
  LWaitResult : integer;
  LStartupInfo: TStartupInfo;
  LProcessInfo: TProcessInformation;
begin
  Result := False;

  FillChar(LStartupInfo, SizeOf(TStartupInfo), 0);

  with LStartupInfo do
  begin
    cb := SizeOf(TStartupInfo);

    dwFlags := STARTF_USESHOWWINDOW or STARTF_FORCEONFEEDBACK;
    wShowWindow := SW_SHOWDEFAULT;
  end;

  if CreateProcess(nil, PChar(APath), nil, nil, 
                   False, NORMAL_PRIORITY_CLASS,
                   nil, nil, LStartupInfo, LProcessInfo) then
  begin

    repeat
      LWaitResult := WaitForSingleObject(LProcessInfo.hProcess, 500);
      // do something, like update a GUI or call Application.ProcessMessages
    until LWaitResult <> WAIT_TIMEOUT;
    result := LWaitResult = WAIT_OBJECT_0;
    GetExitCodeProcess(LProcessInfo.hProcess, VProcessResult);
    CloseHandle(LProcessInfo.hProcess);
    CloseHandle(LProcessInfo.hThread);
  end;
end;

在ExecAndWait返回之后,如果需要的话可以睡眠100毫秒。

N@


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接