未提升的程序启动提升的更新程序,更新程序应等待程序完成

Posted

技术标签:

【中文标题】未提升的程序启动提升的更新程序,更新程序应等待程序完成【英文标题】:unelevated program starts an elevated updater, updater should wait for finishing of program 【发布时间】:2015-06-10 14:29:39 【问题描述】:

我有 2 个应用程序,program.exe 和 updater.exe,它们都是用 Delphi5 编写的。 程序在没有管理员权限(也没有清单)的情况下运行,更新程序有一个带有“requireAdministrator”的清单,因为他必须能够在 Program-Folder 写入来更新 program.exe。

问题是启动更新程序并让他等到程序关闭。 我在网上找到了不同的方法,但都没有奏效(在大多数情况下,第一个应用程序启动第二个应用程序并等待第二个应用程序结束,在我的情况下,第二个应用程序应该等待第一个应用程序结束)。

更新程序应该等待,这很容易 updater.exe

$R manifest.res
label.caption:='Wait for program.exe closing';
repeat
    sleep(1000);
until File is not open
ProgramHandle := Read Handle from File
WaitForSingleObject(ProgramHandle,INFINITE);
label.caption:='program.exe CLOSED';
Do updates

方式 1 使用 CreateProcess 启动更新程序: 程序.exe

FillChar(siInfo, SizeOf(siInfo), 0);
siInfo.cb := SizeOf(siInfo);

saProcessAttributes.nLength := SizeOf(saProcessAttributes);
saProcessAttributes.lpSecurityDescriptor := nil;
saProcessAttributes.bInheritHandle := TRUE;

saThreadAttributes.nLength := SizeOf(saThreadAttributes);
saThreadAttributes.lpSecurityDescriptor := nil;
saThreadAttributes.bInheritHandle := True;

if CreateProcess(nil,
           PChar('updater.exe'),
           @saProcessAttributes,
           @saThreadAttributes,
           TRUE, NORMAL_PRIORITY_CLASS, nil,
           PChar(ExtractFilePath(Application.ExeName)),
           siInfo, piInfo) then
begin
DuplicateHandle(GetCurrentProcess, GetCurrentProcess,
                piInfo.hProcess, @MyHandle,
                PROCESS_QUERY_INFORMATION, TRUE,
                DUPLICATE_SAME_ACCESS) then
Write MyHandle in a File
end;
Close program

不做任何事情,仅当更新程序没有带有 requireAdministrator 的清单时才有效。 如果我以 explizit 管理员权限运行程序,它也可以工作。

方式 2 使用 ShellExecuteEx 启动更新程序: 程序.exe

  FillChar(Info, SizeOf(Info), Chr(0));
  Info.cbSize := SizeOf(Info);
  Info.fMask := SEE_MASK_NOCLOSEPROCESS;
  Info.lpVerb := PChar('runas');
  Info.lpFile := PChar('update.exe');
  Info.lpDirectory := nil;
  Info.nShow := SW_RESTORE;
  ShellExecuteEx(@Info);

  MyHandle:=OpenProcess(PROCESS_ALL_ACCESS, False, GetCurrentProcessId())));
  Write MyHandle in a File
  Close program

不起作用,MyHandle 每次运行此过程时都有不同的值(无需重新启动程序),因此更新程序无法使用它。

所以我不知道如何启动updater.exe并将program.exe的句柄写入文件中。

我对编程的这些部分不是很熟悉......有人对我的问题有想法吗?

【问题讨论】:

可能是枚举Updater中的进程,找到程序然后等到进程终止才是正确的方法。 也许这可以帮助***.com/questions/5608502/…。如果标题名称已知,您可以找到窗口并使用计时器检查句柄是否仍然有效 我的回答中有一个重要说明是关于泄漏的句柄。看看吧 您确实需要两个程序的清单。你甚至不需要requireAdministrator。您可以使用 runas。但最好显示 requireAdministrator。 【参考方案1】:

您的代码不起作用,因为句柄表是每个进程的,这意味着第二个进程可能具有指向另一个内核对象的相同句柄。下面是许多可能的解决方案之一:

创建进程2时,传入进程1的PID作为参数:

procedure CreateUpdater;
var
  Info: TShellExecuteInfo;
begin  
  FillChar(Info, SizeOf(TShellExecuteInfo), 0);
  Info.cbSize := SizeOf(TShellExecuteInfo);
  Info.fMask  := SEE_MASK_NOCLOSEPROCESS;
  Info.lpVerb := PChar('runas');
  Info.lpFile := PChar('Update.exe');
  Info.lpParameters := PChar(IntToStr(GetCurrentProcessId));
  Info.lpDirectory := nil;
  Info.nShow := SW_RESTORE;
  ShellExecuteEx(@Info);
  //NOTE: MISSING ERROR CHECKING!
end;

在Updater内部,等待process1终止:

procedure WaitForAndClose;
var
  PID: String;
  AHandle: Cardinal;
  Ret: longbool;
  ExitNumber: DWORD;
begin
  PID:= ParamStr(1);
  if PID <> '' then
  begin
    AHandle:= OpenProcess(PROCESS_QUERY_INFORMATION, False, StrToInt(PID));  
    //NOTE: MISSING ERROR CHECKING!
    try
      repeat
        Ret:= GetExitCodeProcess(AHandle, ExitNumber);
        //NOTE: MISSING ERROR CHECKING!
        Sleep(1000); //define a time to poolling
      until (ExitNumber <> STILL_ACTIVE);
    finally
      CloseHandle(AHandle);
    end;
    //Terminate the process;
    Application.Terminate;        
  end;
end;

你也可以使用WaitForSingleObject来避免轮询:

WaitForSingleObject(AHandle, INFINITE);
//NOTE: MISSING ERROR CHECKING!

但您需要 SYNCHRONIZE 访问权限才能打开进程:

AHandle:= OpenProcess(SYNCHRONIZE, False, StrToInt(PID));
//NOTE: MISSING ERROR CHECKING!

注意:这里没有错误检查。您应该阅读文档并正确检查错误。

注意 2:我想让您注意您正在泄漏句柄这一事实。 当您使用SEE_MASK_NOCLOSEPROCESS 时,调用者负责关闭被调用的句柄。在你的情况下,我认为你根本不需要那个面具。我会删除它。

【讨论】:

我认为这是最可靠的解决方案。 你也可以在AHandle上使用WaitForSingleObject来避免阻塞sleep 太好了,效果很好!非常感谢你!我把重点放在获取进程句柄上,并认为我的更新程序代码没问题..这是一个错误:-) 错误检查没问题,我的代码中有更多错误检查,我只是压缩它用于发布示例片段。【参考方案2】:

这是一个如何使用事件来实现此目的的基本示例:

program.exe:

  // manual-reset event, non-signaled 
  Event := CreateEvent(nil, True, False, 'MyUniqueName');
  ExecuteUpdater; // via ShellExecuteEx with runas
  // synchronize - wait for the event to be signaled
  WaitForSingleObject(Event, INFINITE);
  // WAIT_OBJECT_0 = The state of the specified object is signaled.  
  CloseHandle(Event);

updater.exe:

  Event := CreateEvent(nil, True, False, 'MyUniqueName');
  if Event = 0 then RaiseLastWin32Error;
  SetEvent(Event); // sets the event object to the signaled state
  CloseHandle(Event);

您还应该将清单添加到 program.exerequestedExecutionLevel 应该是 level="asInvoker")以避免虚拟化。

【讨论】:

【参考方案3】:

我在两个事件的不确定顺序中看到了主要问题:关闭程序和启动更新程序主代码。

一种可能的解决方法是使用事件 - https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms686670(v=vs.85).aspx

程序创建事件(带有继承它的选项),然后启动更新程序(将事件和程序进程的句柄传递为整数通过命令行给它!),然后在事件上冻结在 WaitForSingleObject 中。

这确保程序更新程序准备好监控它之前不会退出,因此PID不会变得无效。

更新程序然后在从命令行获得的程序 PID 上调用 OpenProcess,然后调用 SignalAndWait 敲击事件(从命令行获得)和冻结句柄(从 OpenProcess 获得) - https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms686293(v=vs.85).aspx

程序,现在正从等待事件中释放,终止。 进程的终止正在发出信号,所以现在更新程序被依次释放,可以开始做主要工作了。


C++, How to determine if a Windows Process is running? 建议的另一种方法是查询 程序 ProcessID 的退出代码 - 据说当 程序 仍在运行时会出现特定错误代码,你可以 Sleep(100) 然后再试一次。任何其他结果都意味着程序已经完成。

程序在启动更新程序后立即退出,无需等待它开始监控。

这看起来不错,只是我现在不保证不会重复使用 PID 值。机会微乎其微,但仍不为零。


我个人可能会使用标志文件。 CreateFile API 有一个非常有趣的标志——临时文件模式。这意味着,Windows 会在进程结束后自动删除该文件。那么

    程序使用 Windows API 创建一个新的 GUID(或使用 Crypto API 创建一个新的随机值)。 程序在临时文件夹中创建临时模式文件,其名称基于 GUID 或随机值。如果运气好的话,这样的文件已经存在 - 您只需获得一个新的 GUID 或随机值。 程序启动更新程序并通过命令行将文件名传递给它 程序在启动更新程序后立即退出,无需等待它开始监控。 更新程序不断检查文件是否存在(在尝试之间暂停)。当文件不再存在时 - 这意味着 程序 已完成并且 Windows 自动删除了它。

同样,在程序终止和更新程序再次检查之间,其他进程创建具有完全相同名称的标志文件的可能性很小,但这在实践中几乎是不可能的

【讨论】:

以上是关于未提升的程序启动提升的更新程序,更新程序应等待程序完成的主要内容,如果未能解决你的问题,请参考以下文章

程序员使用IDEA这些插件后,办公效率提升100%(持续更新中)

程序员使用IDEA这些插件后,办公效率提升100%(持续更新中)

程序员使用IDEA这些插件后,办公效率提升100%(持续更新中)

程序员使用IDEA这些插件后,办公效率提升100%(持续更新中)

SharePoint 2019 10 月 安全更新,提升安全漏洞的处理,请尽快更新

如何提升应用程序启动权限