我有2个应用程序,program.exe和updater.exe,都是用Delphi5编写的.
程序在没有admin-rights(没有manifest)的情况下运行,updater有一个带有“requireAdministrator”的清单,因为他必须能够在Program-Folder中写入以更新program.exe.
程序在没有admin-rights(没有manifest)的情况下运行,updater有一个带有“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启动更新程序:
Program.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,DUPLICATE_SAME_ACCESS) then Write MyHandle in a File end; Close program
什么都不做,只有当updater没有带有requireAdministrator的清单时才有效.
如果我使用explizit admin-rights运行程序,它也可以.
方式2
使用ShellExecuteEx启动更新程序:
Program.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的句柄.
我不太熟悉编程的这些部分……有人对我的问题有所了解吗?
解决方法
您的代码无法正常工作,因为句柄表是每个进程,这意味着第二个进程可能具有指向另一个内核对象的相同句柄.下面是许多可能的解决方案之一:
创建进程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,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,StrToInt(PID)); //NOTE: MISSING ERROR CHECKING!
注2:我想请你注意你正在泄漏手柄的事实.当您使用SEE_MASK_NOCLOSEPROCESS时,调用者负责关闭calee的句柄.在你的情况下,我认为你根本不需要那个面具.我会删除它.