首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >非高架程序启动提升更新程序,更新程序应等待程序完成。

非高架程序启动提升更新程序,更新程序应等待程序完成。
EN

Stack Overflow用户
提问于 2015-06-10 14:29:39
回答 3查看 437关注 0票数 8

我有两个应用程序,program.exe和updater.exe,都是用Delphi5编写的。程序运行时没有管理员权限(也没有清单),更新程序有一个带有"requireAdministrator“的清单,因为他必须能够在程序文件夹中写入来更新program.exe。

问题是启动更新程序,让他等到程序关闭。我在网络上发现了不同的方法,但没有一种方法有效(在大多数情况下,第一个应用程序启动第二个应用程序,等待第二个应用程序的结束,在我的例子中,第二个应用程序应该等待1nd应用程序的结束)。

更新者应该等待,这很容易

updater.exe

代码语言:javascript
复制
{$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启动更新程序:

program.exe

代码语言:javascript
复制
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 into的清单时才有效。如果我运行程序与明确的管理权限,它也工作。

使用ShellExecuteEx启动更新程序的方法2:

program.exe

代码语言:javascript
复制
  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句柄。

我对程序的这些部分不太熟悉。有人对我的计划有什么想法吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-06-10 15:51:18

您的代码无法工作,因为句柄表是每个进程,这意味着第二个进程可以有指向另一个内核对象的相同句柄。以下是许多可能的解决办法之一:

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

代码语言:javascript
复制
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;

在更新程序中,等待process1终止:

代码语言:javascript
复制
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来避免轮询:

代码语言:javascript
复制
WaitForSingleObject(AHandle, INFINITE);
//NOTE: MISSING ERROR CHECKING!

但是,您需要同步访问才能打开进程:

代码语言:javascript
复制
AHandle:= OpenProcess(SYNCHRONIZE, False, StrToInt(PID));
//NOTE: MISSING ERROR CHECKING!

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

注2:,我想提请您注意,您正在泄漏一个句柄。当您使用SEE_MASK_NOCLOSEPROCESS 时,调用方负责关闭calee的句柄。在你的情况下,我认为你根本不需要那个面具。我会把它移除的。

票数 7
EN

Stack Overflow用户

发布于 2015-06-11 08:50:18

下面是一个使用事件实现此操作的基本示例:

program.exe

代码语言:javascript
复制
  // 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

代码语言:javascript
复制
  Event := CreateEvent(nil, True, False, 'MyUniqueName');
  if Event = 0 then RaiseLastWin32Error;
  SetEvent(Event); // sets the event object to the signaled state
  CloseHandle(Event);

您还应该向program.exe添加一个清单(requestedExecutionLevel应该是level="asInvoker"),以避免虚拟化。

票数 1
EN

Stack Overflow用户

发布于 2015-06-10 15:50:44

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

修复它的一种可能方法是使用事件- 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的退出代码--据说当程序仍在运行时,会有一个特定的错误代码,您可以睡眠(100),然后再试一次。任何其他结果都意味着程序已经完成。

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

这似乎很好的方法,除了我现在没有任何保证PID值将不会被重用。机会微乎其微,但仍然不是零。

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

  1. 该程序使用Windows创建一个新的GUID (或使用Crypto创建一个新的随机值)。
  2. 程序在临时文件文件夹中根据GUID或随机值创建具有名称的临时模式文件。如果幸运的话,这样的文件已经存在-您只需要获得一个新的GUID或随机值。
  3. 程序启动更新程序并通过命令行将文件名传递给它。
  4. 程序在启动更新程序后立即退出,而无需等待它启动监视。
  5. 更新程序不断检查文件是否存在(尝试之间暂停)。当文件不存在时--这意味着程序已经完成并且Windows自动删除了它。

同样,在程序终止和更新程序之间创建完全相同名称的标志文件的可能性微乎其微,但在实践中这几乎是不可能的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30759365

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档