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

时间:2009-11-11 19:25:54

标签: delphi

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

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

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

我怎样才能完成我在这之后所做的事情?

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 个答案:

答案 0 :(得分: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结构的MSDN页面。

答案 1 :(得分:3)

@ mghie的答案中的代码一般都有正确的想法,但是在等待进程句柄时处理消息的代码可能会更好。试试这个:

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;

答案 2 :(得分:0)

你的等待(50毫秒太短),试试

WaitForSingleObject(sei.hProcess, INFINITE)

可以省略对有效进程句柄(sei.hProcess&lt;&gt; 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;