代码之家  ›  专栏  ›  技术社区  ›  croceldon

Delphi:如何以提升的状态启动应用程序并等待它终止?

  •  13
  • croceldon  · 技术社区  · 15 年前

    我正在尝试用提升的权限从程序启动另一个应用程序,并等待它终止,然后继续。

    我在网上尝试过几种不同的解决方案,但我找不到一种完全正确的解决方案。

    下面的代码是最接近我的工作权利。它以提升的权限运行应用程序并等待其终止,但一旦外部应用程序终止,它就会冻结。换句话说,一旦启动的应用程序关闭,它就不会继续处理。

    我怎样才能完成我在这里的使命?

    procedure TfMain.RunFileAsAdminWait(hWnd: HWND; aFile, aParameters: string);
    var
      sei: TShellExecuteInfo;
    begin
      FillChar(sei, SizeOf(sei), 0);
      sei.cbSize := SizeOf(sei);
      sei.Wnd := hWnd;
      sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
      sei.lpVerb := 'runas';
      sei.lpFile := PChar(aFile);
      sei.lpParameters := PChar(aParameters);
      sei.nShow := SW_SHOWNORMAL;
    
      if not ShellExecuteEx(@sei) then
        RaiseLastOSError
      else
        while WaitForSingleObject(sei.hProcess, 50) <> WAIT_OBJECT_0 do
          Application.ProcessMessages;
    
      CloseHandle(sei.hProcess);
    end;
    

    更新:

    我已经想出了下面的函数,但是只有在调用了showmessage语句之后,它才起作用。所以,我必须:

    RunFileAsAdminWait(Handle, ExtractFilePath(Application.Exename) + 'AutoUpdate.exe', '/auto');
    ShowMessage('test');
    

    以使功能正常工作。 如果没有ShowMessage呼叫,如何使其正常工作?

    以下是更新后的函数:

    procedure TfMain.RunFileAsAdminWait(hWnd: HWND; aFile, aParameters: string);
    var
      sei: TShellExecuteInfo;
    begin
      FillChar(sei, SizeOf(sei), 0);
      sei.cbSize := SizeOf(sei);
      sei.Wnd := hWnd;
      sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
      sei.lpVerb := 'runas';
      sei.lpFile := PChar(aFile);
      sei.lpParameters := PChar(aParameters);
      sei.nShow := SW_SHOWNORMAL;
    
      if not ShellExecuteEx(@sei) then
        RaiseLastOSError
      else
        if sei.hProcess <> 0 then
          WaitForSingleObject(sei.hProcess, 50)
        else
          Exit;
    
      CloseHandle(sei.hProcess);
    end;
    
    3 回复  |  直到 9 年前
        1
  •  15
  •   mghie    15 年前

    以下代码适用于我:

    procedure RunFileAsAdminWait(hWnd: HWND; aFile, aParameters: string);
    var
      sei: TShellExecuteInfo;
    begin
      FillChar(sei, SizeOf(sei), 0);
      sei.cbSize := SizeOf(sei);
      sei.Wnd := hWnd;
      sei.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS;
      sei.lpVerb := 'runas';
      sei.lpFile := PChar(aFile);
      sei.lpParameters := PChar(aParameters);
      sei.nShow := SW_SHOWNORMAL;
    
      if not ShellExecuteEx(@sei) then
        RaiseLastOSError;
      if sei.hProcess <> 0 then begin
        while WaitForSingleObject(sei.hProcess, 50) = WAIT_TIMEOUT do
          Application.ProcessMessages;
        CloseHandle(sei.hProcess);
      end;
    end;
    

    你必须通过 SEE_MASK_NOCLOSEPROCESS 获取要等待的进程句柄的标志。我还将代码改为循环,只要 WaitForSingleObject() 返回超时。

    有关标志的更多信息,请参阅 SHELLEXECUTEINFO 结构。

        2
  •  3
  •   Remy Lebeau    9 年前

    @mghie's answer中的代码通常具有正确的思想,但是在等待进程句柄时处理消息的代码可能更好。试试这个:

    procedure RunFileAsAdminWait(hWnd: HWND; aFile, aParameters: string);
    var
      sei: TShellExecuteInfo;
      Ret: DWORD;
    begin
      FillChar(sei, SizeOf(sei), 0);
      sei.cbSize := SizeOf(sei);
      sei.Wnd := hWnd;
      sei.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS;
      sei.lpVerb := 'runas';
      sei.lpFile := PChar(aFile);
      sei.lpParameters := PChar(aParameters);
      sei.nShow := SW_SHOWNORMAL;
    
      if not ShellExecuteEx(@sei) then
        RaiseLastOSError;
      if sei.hProcess <> 0 then
      try
        repeat
          Ret := MsgWaitForMultipleObjects(1, sei.hProcess, False, INFINITE, QS_ALLINPUT);
          if Ret = (WAIT_OBJECT_0+1) then Application.ProcessMessages
          else if Ret = WAIT_FAILED then RaiseLastOSError;
        until Ret = WAIT_OBJECT_0;
      finally
        CloseHandle(sei.hProcess);
      end;
    end;
    
        3
  •  0
  •   Remko    15 年前

    您的等待时间(50毫秒太短),请尝试

    WaitForSingleObject(sei.hProcess, INFINITE)
    

    可以忽略对有效进程句柄(sei.hprocess<>0)的检查。

    正确答案:

      while MsgWaitForMultipleObjects(1, sei.hProcess, False, INFINITE, QS_ALLINPUT)
        <> WAIT_OBJECT_0 do
      begin
        while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do
        begin
          DispatchMessage(Msg);
        end;
      end;