https://stackoverflow.com/questions/62725866/how-do-you-close-then-restart-explorer-exe-in-inno-setup-uninstall-using-the-res
作者:Miral
还没看,先存着。
{ Example how to use RestartManager and Offer Option to Delete App Data during Uninstall of Inno Setup } { Combined multiple open source ISS code found with some custom changes } { This could probably be cleaned up a bit, my goal was to get something to work. } { I also stripped it down a bit as I had multiple applications and data so hope } { everything removed without an issue, but I'm sure you can figure it out if so } {-------------------------------------------------------------------------} { Copies a NULL-terminated array of characters to a string. } function ArrayToString(Chars:array of Char):String; var Len,i:Longint; begin Len:=GetArrayLength(Chars); SetLength(Result,Len); i:=0; while (i<Len) and (Chars[i]<>#0) do begin Result[i+1]:=Chars[i]; i:=i+1; end; SetLength(Result,i); end; {-------------------------------------------------------------------------} type LONG = Longint; ULONG = Cardinal; IdList=array of DWORD; ProcessEntry=record ID:DWORD; Name:String; Restartable,ToTerminate:Boolean; end; ProcessList=array of ProcessEntry; { Code for Windows Vista and above } const { Return codes. } ERROR_SUCCESS = 0; ERROR_MORE_DATA = 234; INVALID_HANDLE_VALUE = -1; CCH_RM_SESSION_KEY = 32; CCH_RM_MAX_APP_NAME = 255; CCH_RM_MAX_SVC_NAME = 63; RmUnknownApp = 0; { The application cannot be classified as any other type. An application of this type can only be shut down by a forced shutdown. } RmMainWindow = 1; { A Windows application run as a stand-alone process that displays a top-level window. } RmOtherWindow = 2; { A Windows application that does not run as a stand-alone process and does not display a top-level window. } RmService = 3; { The application is a Windows service. } RmExplorer = 4; { The application is Windows Explorer. } RmConsole = 5; { The application is a stand-alone console application. } RmCritical = 1000; { A system restart is required to complete the installation because a process cannot be shut down. } RmStatusUnknown = $0000; RmStatusRunning = $0001; RmStatusStopped = $0002; RmStatusStoppedOther = $0004; RmStatusRestarted = $0008; RmStatusErrorOnStop = $0010; RmStatusErrorOnRestart = $0020; RmStatusShutdownMasked = $0040; RmStatusRestartMasked = $0080; RmForceShutdown = $0001; RmShutdownOnlyRegistered = $0010; type SessionKey=array[1..CCH_RM_SESSION_KEY+1] of Char; FILETIME=record dwLowDateTime,dwHighDateTime:DWORD; end; RM_UNIQUE_PROCESS=record dwProcessId:DWORD; ProcessStartTime:FILETIME; end; RM_APP_TYPE=DWORD; RM_PROCESS_INFO=record Process:RM_UNIQUE_PROCESS; strAppName:array[1..CCH_RM_MAX_APP_NAME+1] of Char; strServiceShortName:array[1..CCH_RM_MAX_SVC_NAME+1] of Char; ApplicationType:RM_APP_TYPE; AppStatus:ULONG; TSSessionId:DWORD; bRestartable:BOOL; end; RM_WRITE_STATUS_CALLBACK=DWORD; function RmStartSession(var pSessionHandle:DWORD;dwSessionFlags:DWORD;strSessionKey:SessionKey):DWORD; external 'RmStartSession@Rstrtmgr.dll stdcall delayload'; function RmEndSession(dwSessionHandle:DWORD):DWORD; external 'RmEndSession@Rstrtmgr.dll stdcall delayload'; function RmRegisterResources(dwSessionHandle:DWORD;hFiles:UINT;rgsFilenames:TArrayOfString;nApplications:UINT;rgApplications:array of RM_UNIQUE_PROCESS;nServices:UINT;rgsServiceNames:TArrayOfString):DWORD; external 'RmRegisterResources@Rstrtmgr.dll stdcall delayload'; function RmGetList(dwSessionHandle:DWORD;var pnProcInfoNeeded,pnProcInfo:UINT;rgAffectedApps:array of RM_PROCESS_INFO;var lpdwRebootReasons:DWORD):DWORD; external 'RmGetList@Rstrtmgr.dll stdcall delayload'; function RmShutdown(dwSessionHandle:DWORD;lActionFlags:ULONG;fnStatus:RM_WRITE_STATUS_CALLBACK):DWORD; external 'RmShutdown@Rstrtmgr.dll stdcall delayload'; function RmRestart(dwSessionHandle:DWORD;dwRestartFlags:DWORD;fnStatus:RM_WRITE_STATUS_CALLBACK):DWORD; external 'RmRestart@Rstrtmgr.dll stdcall delayload'; var Processes: ProcessList; RestartManagerHandle:DWORD; {-------------------------------------------------------------------------} { Returns a list of running processes that currectly use one of the specified modules. } { Each module has to be a full path and filename to a DLL. } function FindProcessesUsingModules(Modules:TArrayOfString;var Processes:ProcessList):DWORD; var Handle:DWORD; Name:SessionKey; Apps:array of RM_UNIQUE_PROCESS; Services:TArrayOfString; Path:String; PathLength:DWORD; Needed,Have,i:UINT; AppList:array of RM_PROCESS_INFO; RebootReason:DWORD; Success:DWORD; begin SetArrayLength(Processes,0); Result:=INVALID_HANDLE_VALUE; { NULL-terminate the array of chars. } Name[CCH_RM_SESSION_KEY+1]:=#0; if RmStartSession(Handle,0,Name)<>ERROR_SUCCESS then begin Exit; end; if RmRegisterResources(Handle,GetArrayLength(Modules),Modules,0,Apps,0,Services)=ERROR_SUCCESS then begin { Reallocate the arrays until they are large enough to hold the process information. } Needed:=1; repeat Have:=Needed; SetArrayLength(AppList,Have); Success:=RmGetList(Handle,Needed,Have,AppList,RebootReason); until (Success<>ERROR_MORE_DATA) or (Have>=Needed); if (Success=ERROR_SUCCESS) and (Needed>0) then begin for i:=0 to Needed-1 do begin { append to end of list } Have:=GetArrayLength(Processes); SetArrayLength(Processes,Have+1); { assign values to new entry } Processes[Have].ID:=AppList[i].Process.dwProcessId; Processes[Have].Name:=ArrayToString(AppList[i].strAppName); Processes[Have].Restartable:=AppList[i].bRestartable; Processes[Have].ToTerminate:=True; end; Result:=Handle; end; end; if (Result=INVALID_HANDLE_VALUE) then RmEndSession(Handle); end; {-------------------------------------------------------------------------} { Returns a list of running processes that currectly use the specified module. { The module has to be a full path and filename to a DLL. This starts the } { RestartManager and returns its handle or INVALID_HANDLE_VALUE if failed } { or nothing has lock } function FindProcessesUsingModule(Module:String;var Processes:ProcessList):DWORD; var Modules:TArrayOfString; begin SetArrayLength(Modules,1); Modules[0]:=Module; Result:=FindProcessesUsingModules(Modules,Processes); end; {-------------------------------------------------------------------------} var UninstallDelDataPage: TNewNotebookPage; UninstallConfirmPage: TNewNotebookPage; UninstallProcessListPage: TNewNotebookPage; UninstallBackButton: TNewButton; UninstallNextButton: TNewButton; UninstallAutoCloseRB: TNewRadioButton; DeleteAppDataCheckBox: TNewCheckBox; AppData: String; {-------------------------------------------------------------------------} procedure UpdateUninstallWizard; begin if UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage then begin UninstallProgressForm.PageNameLabel.Caption := 'Select Data Uninstall Options'; UninstallProgressForm.PageDescriptionLabel.Caption := 'What data, if any, should be deleted?'; end else if UninstallProgressForm.InnerNotebook.ActivePage = UninstallConfirmPage then begin UninstallProgressForm.PageNameLabel.Caption := 'Confirm Uninstall'; UninstallProgressForm.PageDescriptionLabel.Caption := 'Confirm the uninstall options below.'; end else if UninstallProgressForm.InnerNotebook.ActivePage = UninstallProcessListPage then begin UninstallProgressForm.PageNameLabel.Caption := 'Preparing to Uninstall'; UninstallProgressForm.PageDescriptionLabel.Caption := 'Uninstall is preparing to uninstall {#MyAppName} from your computer.' end; UninstallBackButton.Visible := (UninstallProgressForm.InnerNotebook.ActivePage = UninstallConfirmPage) or ((UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage) and (UninstallProcessListPage<>nil)); if (UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage) or ((UninstallProgressForm.InnerNotebook.ActivePage = UninstallProcessListPage) and (UninstallDelDataPage<>nil)) then begin UninstallNextButton.Caption := SetupMessage(msgButtonNext); UninstallNextButton.ModalResult := mrNone; end else begin UninstallNextButton.Caption := 'Uninstall'; { Make the "Uninstall" button break the ShowModal loop } UninstallNextButton.ModalResult := mrOK; end; end; {-------------------------------------------------------------------------} procedure CreateUninstallConfirmPage(); var PageText: TNewStaticText; begin { Create the second page } if (UninstallConfirmPage<>nil) then UninstallConfirmPage.Free(); UninstallConfirmPage := TNewNotebookPage.Create(UninstallProgressForm); UninstallConfirmPage.Notebook := UninstallProgressForm.InnerNotebook; UninstallConfirmPage.Parent := UninstallProgressForm.InnerNotebook; UninstallConfirmPage.Align := alClient; PageText := TNewStaticText.Create(UninstallProgressForm); PageText.Parent := UninstallConfirmPage; PageText.Top := UninstallProgressForm.StatusLabel.Top; PageText.Left := UninstallProgressForm.StatusLabel.Left; PageText.Width := UninstallProgressForm.StatusLabel.Width; PageText.Height := UninstallProgressForm.StatusLabel.Height; PageText.AutoSize := True; PageText.Wordwrap := True; PageText.ShowAccelChar := False; PageText.Font.Size := 9; PageText.Caption := 'Click Uninstall to continue with removal of {#MyAppName}.'; if DeleteAppDataCheckBox.Checked then PageText.Caption := PageText.Caption + #13#10#10 + 'WARNING: Application data will be deleted.'; end; {-------------------------------------------------------------------------} procedure UninstallNextButtonClick(Sender: TObject); begin if UninstallProgressForm.InnerNotebook.ActivePage = UninstallConfirmPage then begin UninstallNextButton.Visible := False; UninstallBackButton.Visible := False; end else begin if UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage then begin CreateUninstallConfirmPage(); UninstallProgressForm.InnerNotebook.ActivePage := UninstallConfirmPage; end else if UninstallDelDataPage<>nil then begin UninstallProgressForm.InnerNotebook.ActivePage := UninstallDelDataPage; end else begin UninstallNextButton.Visible := False; UninstallBackButton.Visible := False; end; UpdateUninstallWizard; end; end; {-------------------------------------------------------------------------} procedure UninstallBackButtonClick(Sender: TObject); begin if UninstallProgressForm.InnerNotebook.ActivePage = UninstallConfirmPage then begin UninstallProgressForm.InnerNotebook.ActivePage := UninstallDelDataPage; end else if (UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage) and (UninstallProcessListPage<>Nil) then begin UninstallProgressForm.InnerNotebook.ActivePage := UninstallProcessListPage; end; UpdateUninstallWizard; end; {-------------------------------------------------------------------------} procedure InitializeUninstallProgressForm(); var PageText: TNewStaticText; PageNameLabel: string; PageDescriptionLabel: string; CancelButtonEnabled: Boolean; CancelButtonModalResult: Integer; appinstalled: Boolean; top : Integer; ListBox : TNewListBox; i:Integer; UninstallNoCloseRB : TNewRadioButton; begin RestartManagerHandle:=FindProcessesUsingModule(ExpandConstant('{app}MyModule.dll'), Processes); if not UninstallSilent then begin { save labels / description } PageNameLabel := UninstallProgressForm.PageNameLabel.Caption; PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption; if RestartManagerHandle<>INVALID_HANDLE_VALUE then begin UninstallProcessListPage:=TNewNotebookPage.Create(UninstallProgressForm); UninstallProcessListPage.Notebook := UninstallProgressForm.InnerNotebook; UninstallProcessListPage.Parent := UninstallProgressForm.InnerNotebook; UninstallProcessListPage.Align := alClient; PageText := TNewStaticText.Create(UninstallProgressForm); PageText.Parent := UninstallProcessListPage; PageText.Top := UninstallProgressForm.StatusLabel.Top; PageText.Left := UninstallProgressForm.StatusLabel.Left; PageText.Width := UninstallProgressForm.StatusLabel.Width; PageText.Height := UninstallProgressForm.StatusLabel.Height; PageText.AutoSize := True; PageText.WordWrap := True; PageText.ShowAccelChar := False; PageText.Caption := 'The following applications are using files that need to be removed by the uninstaller. It is recommended that you allow Uninstall to automatically close these applications. After uninstall has completed, it will attempt to restart the applications.'; ListBox := TNewListBox.Create(UninstallProgressForm); ListBox.Parent := UninstallProcessListPage; ListBox.Top := PageText.Top + PageText.Height + 16; ListBox.Left := PageText.Left; ListBox.Width := PageText.Width; ListBox.Height := ScaleY(97); ListBox.TabStop:=False; for i:=0 to GetArraylength(Processes)-1 do begin ListBox.Items.Add(Processes[i].Name); end; ListBox.ItemIndex:=-1; UninstallAutoCloseRB := TNewRadioButton.Create(UninstallProgressForm); UninstallAutoCloseRB .Parent := UninstallProcessListPage; UninstallAutoCloseRB .Checked := True; UninstallAutoCloseRB .Top := ListBox.Top+ListBox.Height+8; UninstallAutoCloseRB .Left := ListBox.Left; UninstallAutoCloseRB .Width := ListBox.Width; UninstallAutoCloseRB.Font.Size := 9; UninstallAutoCloseRB.Caption := '&Automatically close the applications'; UninstallNoCloseRB := TNewRadioButton.Create(UninstallProgressForm); UninstallNoCloseRB .Parent := UninstallProcessListPage; UninstallNoCloseRB .Checked := False; UninstallNoCloseRB .Top := UninstallAutoCloseRB.Top+UninstallAutoCloseRB.Height+8; UninstallNoCloseRB .Left := UninstallAutoCloseRB.Left; UninstallNoCloseRB .Width := UninstallAutoCloseRB.Width; UninstallNoCloseRB .Font.Size := 9; UninstallNoCloseRB .Caption := '&Do not close the applications'; { make page active } UninstallProgressForm.InnerNotebook.ActivePage := UninstallProcessListPage; end; { work around elevated uninstaller for single user} AppData:=GetEnv('AppData'); Log('AppData reported as' + AppData); if AppData<>'' then begin appinstalled:=FileExists(ExpandConstant('{app}/myapp.exe')); if appinstalled then begin { Create the first page and make it active } UninstallDelDataPage := TNewNotebookPage.Create(UninstallProgressForm); UninstallDelDataPage.Notebook := UninstallProgressForm.InnerNotebook; UninstallDelDataPage.Parent := UninstallProgressForm.InnerNotebook; UninstallDelDataPage.Align := alClient; PageText := TNewStaticText.Create(UninstallProgressForm); PageText.Parent := UninstallDelDataPage; PageText.Top := UninstallProgressForm.StatusLabel.Top; PageText.Left := UninstallProgressForm.StatusLabel.Left; PageText.Width := UninstallProgressForm.StatusLabel.Width; PageText.Height := UninstallProgressForm.StatusLabel.Height; PageText.AutoSize := True; PageText.WordWrap := True; PageText.ShowAccelChar := False; PageText.Caption := 'You have the option to delete the data assoicated with App Name'; top:=PageText.Top + PageText.Height + 16; if appinstalled then begin DeleteAppDataCheckBox := TNewCheckBox.Create(UninstallProgressForm); DeleteAppDataCheckBox.Parent := UninstallDelDataPage; DeleteAppDataCheckBox.Checked := False; DeleteAppDataCheckBox.Top := top; DeleteAppDataCheckBox.Left := PageText.Left+10; DeleteAppDataCheckBox.Width := PageText.Width - 10; DeleteAppDataCheckBox.Font.Size := 9; DeleteAppDataCheckBox.Caption := 'Delete all App Data'; top:=DeleteAppDataCheckBox.Top + DeleteAppDataCheckBox.Height + 8; end; { set as first page if not already set } if UninstallProgressForm.InnerNotebook.ActivePage=UninstallProgressForm.InstallingPage then UninstallProgressForm.InnerNotebook.ActivePage:=UninstallDelDataPage; { next button } UninstallNextButton := TNewButton.Create(UninstallProgressForm); UninstallNextButton.Parent := UninstallProgressForm; UninstallNextButton.Left := UninstallProgressForm.CancelButton.Left - UninstallProgressForm.CancelButton.Width - ScaleX(10); UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top; UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width; UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height; UninstallNextButton.OnClick := @UninstallNextButtonClick; { back button } UninstallBackButton := TNewButton.Create(UninstallProgressForm); UninstallBackButton.Parent := UninstallProgressForm; UninstallBackButton.Left := UninstallNextButton.Left - UninstallNextButton.Width - ScaleX(10); UninstallBackButton.Top := UninstallProgressForm.CancelButton.Top; UninstallBackButton.Width := UninstallProgressForm.CancelButton.Width; UninstallBackButton.Height := UninstallProgressForm.CancelButton.Height; UninstallBackButton.Caption := SetupMessage(msgButtonBack); UninstallBackButton.OnClick := @UninstallBackButtonClick; { setup tab order } UninstallBackButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder; UninstallNextButton.TabOrder := UninstallBackButton.TabOrder + 1; UninstallProgressForm.CancelButton.TabOrder := UninstallNextButton.TabOrder + 1; { Run our wizard pages } UpdateUninstallWizard; CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled UninstallProgressForm.CancelButton.Enabled := True; { save value } CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult; { set value to use } UninstallProgressForm.CancelButton.ModalResult := mrCancel; if UninstallProgressForm.ShowModal = mrCancel then begin if RestartManagerHandle<>INVALID_HANDLE_VALUE then begin RmEndSession(RestartManagerHandle); RestartManagerHandle:=INVALID_HANDLE_VALUE; end; Abort; end; { Restore the standard page payout } UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled; UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult; UninstallProgressForm.PageNameLabel.Caption := PageNameLabel; UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel; UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage; end; end; end; end; {-------------------------------------------------------------------------} function IsDeleteAppData: Boolean; begin Result:=(DeleteAppDataCheckBox<>nil) and DeleteAppDataCheckBox.Checked; end; {-------------------------------------------------------------------------} procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); var ErrorCode: Integer; Res : Boolean; begin if CurUninstallStep = usUninstall then begin if (RestartManagerHandle<>INVALID_HANDLE_VALUE) and (UninstallAutoCloseRB<>nil) and (UninstallAutoCloseRB.Checked) then RmShutdown(RestartManagerHandle, 0, 0); end else if CurUninstallStep = usPostUninstall then begin if IsDeleteAppData then begin Res:=DelTree(AppData+'MyAppDataDir', true, true, true); Log('Delete '+AppData+'MyAppDataDir result: '+IntToStr(Integer(Res))); end; if (RestartManagerHandle<>INVALID_HANDLE_VALUE) and (UninstallAutoCloseRB<>nil) and (UninstallAutoCloseRB.Checked) then RmRestart(RestartManagerHandle, 0, 0); if (RestartManagerHandle<>INVALID_HANDLE_VALUE) then begin RmEndSession(RestartManagerHandle); RestartManagerHandle:=INVALID_HANDLE_VALUE; end; end; end;