Close running version of program before installing update (Inno Setup)

前端 未结 10 1194
囚心锁ツ
囚心锁ツ 2020-12-23 12:24

This should be simple, I need to stop any previous version of my program from running when the installer starts.

Most people suggested making an exe wh

相关标签:
10条回答
  • 2020-12-23 12:50

    In version 5.5.0 (Released on May 2012) Inno Setup added support for the Restart Manager API on Windows Vista and newer.

    Quote from MSDN linked documentation (emphasis mine):

    The primary reason software installation and updates require a system restart is that some of the files that are being updated are currently being used by a running application or service. Restart Manager enables all but the critical applications and services to be shut down and restarted. This frees the files that are in use and allows installation operations to complete. It can also eliminate or reduce the number of system restarts that are required to complete an installation or update.

    The good thing is: you don't need to write custom code in the installer or your application to ask the user to close it, or close it automatically.

    If you want your application to restart after the update is complete, you have to call the RegisterApplicationRestart function from your application first.

    The default values for the new directives closes all the .exe, .dll and .chm files contained within the [Files] section of the installer.

    The changes related to it are (from release notes):

    • Added new [Setup] section directive: CloseApplications, which defaults to yes. If set to yes and Setup is not running silently, Setup will now pause on the Preparing to Install wizard page if it detects applications using files that need to be updated by the [Files] or [InstallDelete] section, showing the applications and asking the user if Setup should automatically close the applications and restart them after the installation has completed. If set to yes and Setup is running silently, Setup will always close and restart such applications, unless told not to via the command line (see below).
    • Added new [Setup] section directive: CloseApplicationsFilter, which defaults to *.exe,*.dll,*.chm. Controls which files Setup will check for being in use. Setting this to *.* can provide better checking at the expense of speed.
    • Added new [Setup] section directive: RestartApplications, which defaults to yes. Note: for Setup to be able to restart an application after the installation has completed, the application needs to be using the Windows RegisterApplicationRestart API function.
    • Added new command line parameters supported by Setup: /NOCLOSEAPPLICATIONS and /NORESTARTAPPLICATIONS. These can be used to override the new CloseApplications and RestartApplications directives.
    • Added new [Code] support function: RmSessionStarted.
    • TWizardForm: Added new PreparingMemo property.
    0 讨论(0)
  • 2020-12-23 12:50

    Well, I think the easier way to perform this may be creating a DLL in Delphi that detects if your program is running and ask the user to close it, put that DLL in your setup and use the flag "dontcopy" (check in http://www.jrsoftware.org/ishelp/ under Pascal Scripting \ Using DLLs for an example).

    Btw, next time use mutexes, Inno Setup also support that and is far more easier.

    EDIT: and for extracting a file (if you want to use that .exe you mention), just use ExtractTemporaryFile().

    0 讨论(0)
  • 2020-12-23 12:51

    InnoSetup allows you to attach Pascal scripts to various places in the build process. Try attaching a script that calls ShellExecute. (Which you may have to import to the script engine if it doesn't already have it.)

    0 讨论(0)
  • 2020-12-23 12:54

    If you're using InnoSetup, you could look into getting your InnoSetup installer to do a Windows SendBroadcastMessage, and get your application to listen for that message. When your application receives the message, it should shut itself down.

    I've done this myself with an InnoSetup installer, and it works very well.

    0 讨论(0)
  • 2020-12-23 12:56

    If you are happy to write your own DLL, you can use the tool help API for TlHelp32.pas to determine what applications are running, and then get a window handle for them using EnumWindows, then send a WM_CLOSE to the window handle.

    It's a bit of a pain, but it should work: I have some utility wrapper classes I developed with a friend a while back. Can't remember if we based it on someone else's code.

    TWindows.ProcessISRunning and TWindows.StopProcess may help.

    interface
    
    uses
      Classes,
      Windows,
      SysUtils,
      Contnrs,
      Messages;
    
    type
    
    
    TProcess = class(TObject)
      public
        ID: Cardinal;
        Name: string;
    end;
    
    TWindow = class(TObject)
      private
        FProcessID: Cardinal;
        FProcessName: string;
        FHandle: THandle;
        FProcessHandle : THandle;
        function GetProcessHandle: THandle;
        function GetProcessID: Cardinal;
        function GetProcessName: string;
      public
        property Handle : THandle read FHandle;
        property ProcessName : string read GetProcessName;
        property ProcessID : Cardinal read GetProcessID;
        property ProcessHandle : THandle read GetProcessHandle;
    end;
    
    TWindowList = class(TObjectList)
      private
        function GetWindow(AIndex: Integer): TWindow;
      protected
    
      public
        function Add(AWindow: TWindow): Integer; reintroduce;
        property Window[AIndex: Integer]: TWindow read GetWindow; default;
    end;
    
    TProcessList = class(TObjectList)
      protected
        function GetProcess(AIndex: Integer): TProcess;
      public
        function Add(AProcess: TProcess): Integer; reintroduce;
        property Process[AIndex: Integer]: TProcess read GetProcess; default;
    end;
    
    TWindows = class(TObject)
      protected
      public
        class function GetHWNDFromProcessID(ProcessID: Cardinal; BuildList: Boolean = True): THandle;
        class function GetProcessList: TProcessList;
        class procedure KillProcess(ProcessName: string);
        class procedure StopProcess(ProcessName: string);
        class function ExeIsRunning(ExeName: string): Boolean;
        class function ProcessIsRunning(PID: Cardinal): Boolean;
    end;
    
    implementation
    
    uses
      Forms,
      Math,
      PSAPI,
      TlHelp32;
    
    const
      cRSPUNREGISTERSERVICE = 0;
      cRSPSIMPLESERVICE = 1;
    
    type
    
    TProcessToHWND = class(TObject)
      public
        ProcessID: Cardinal;
        HWND: Cardinal;
    end;
    
    function RegisterServiceProcess(dwProcessID, dwType: DWord): DWord; stdcall; external 'KERNEL32.DLL';
    function GetDiskFreeSpaceEx(lpDirectoryName: PChar;
      var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes: TLargeInteger;
      lpTotalNumberOfFreeBytes: PLargeInteger): Boolean; stdcall;external 'KERNEL32.DLL' name 'GetDiskFreeSpaceExA'
    
    var
      GProcessToHWNDList: TObjectList = nil;
    
    function EnumerateWindowsProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
    var
      proc: TProcessToHWND;
    begin
      if Assigned(GProcessToHWNDList) then
      begin
        proc := TProcessToHWND.Create;
        proc.HWND := hwnd;
        GetWindowThreadProcessID(hwnd, proc.ProcessID);
        GProcessToHWNDList.Add(proc);
        Result := True;
      end
      else
        Result := False; // stop enumeration
    end;
    
    { TWindows }
    
    class function TWindows.ExeIsRunning(ExeName: string): Boolean;
    var
      processList: TProcessList;
      i: Integer;
    begin
      Result := False;
    
      processList := GetProcessList;
      try
        for i := 0 to processList.Count - 1 do
        begin
          if (UpperCase(ExeName) = UpperCase(processList[i].Name)) or
              (UpperCase(ExeName) = UpperCase(ExtractFileName(processList[i].Name))) then
          begin
            Result := True;
            Break;
          end;
        end;
      finally
        processList.Free;
      end;
    end;
    
    class function TWindows.GetHWNDFromProcessID(
      ProcessID: Cardinal; BuildList: Boolean): THandle;
    var
      i: Integer;
    begin
      Result := 0;
    
      if BuildList or (not Assigned(GProcessToHWNDList)) then
      begin
        GProcessToHWNDList.Free;
        GProcessToHWNDList := TObjectList.Create;
        EnumWindows(@EnumerateWindowsProc, 0);
      end;
    
      for i := 0 to GProcessToHWNDList.Count - 1 do
      begin
        if TProcessToHWND(GProcessToHWNDList[i]).ProcessID = ProcessID then
        begin
          Result := TProcessToHWND(GProcessToHWNDList[i]).HWND;
          Break;
        end;
      end;
    end;
    
    
    class function TWindows.GetProcessList: TProcessList;
    var
      handle: THandle;
      pe: TProcessEntry32;
      process: TProcess;
    begin
      Result := TProcessList.Create;
    
      handle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
      pe.dwSize := Sizeof(pe);
      if Process32First(handle, pe) then
      begin
        while True do
        begin
          process := TProcess.Create;
          process.Name := pe.szExeFile;
          process.ID := pe.th32ProcessID;
          Result.Add(process);
          if not Process32Next(handle, pe) then
            Break;
        end;
      end;
      CloseHandle(handle);
    end;
    
    function EnumWindowsProc(Ahwnd : HWND;      // handle to parent window
      ALParam : Integer) : BOOL;stdcall;
    var
      List : TWindowList;
      Wnd : TWindow;
    begin
      Result := True;
      List := TWindowList(ALParam);
      Wnd := TWindow.Create;
      List.Add(Wnd);
      Wnd.FHandle := Ahwnd;
    end;
    
    
    class procedure TWindows.KillProcess(ProcessName: string);
    var
      handle: THandle;
      pe: TProcessEntry32;
    begin
      // Warning: will kill all process with ProcessName
      // NB won't work on NT 4 as Tool Help API is not supported on NT
    
      handle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
      try
        pe.dwSize := Sizeof(pe);
    
        if Process32First(handle, pe) then
        begin
          while True do begin
            if (UpperCase(ExtractFileName(pe.szExeFile)) = UpperCase(ExtractFileName(ProcessName))) or
               (UpperCase(pe.szExeFile) = UpperCase(ProcessName)) then
            begin
              if not TerminateProcess(OpenProcess(PROCESS_TERMINATE, False,
                                        pe.th32ProcessID), 0) then
              begin
                raise Exception.Create('Unable to stop process ' + ProcessName + ': Error Code ' + IntToStr(GetLastError));
              end;
            end;
            if not Process32Next(handle, pe) then
              Break;
          end;
        end;
      finally
        CloseHandle(handle);
      end;
    end;
    
    class function TWindows.ProcessIsRunning(PID: Cardinal): Boolean;
    var
      processList: TProcessList;
      i: Integer;
    begin
      Result := False;
    
      processList := GetProcessList;
      try
        for i := 0 to processList.Count - 1 do
        begin
          if processList[i].ID = PID then
          begin
            Result := True;
            Break;
          end;
        end;
      finally
        processList.Free;
      end;
    end;
    
    class procedure TWindows.StopProcess(ProcessName: string);
    var
      processList: TProcessList;
      i: Integer;
      hwnd: THandle;
    begin
      // Warning: will attempt to stop all process with ProcessName
      if not Assigned(GProcessToHWNDList) then
        GProcessToHWNDList := TObjectList.Create
      else
        GProcessToHWNDList.Clear;
    
      // get list of all current processes
      processList := GetProcessList;
      // enumerate windows only once to determine the window handle for the processes
      if EnumWindows(@EnumerateWindowsProc, 0) then
      begin
        for i := 0 to processList.Count - 1 do
        begin
          if UpperCase(ExtractFileName(processList[i].Name)) = UpperCase(ExtractFileName(ProcessName)) then
          begin
            hwnd := GetHWNDFromProcessID(processList[i].ID, False);
            SendMessage(hwnd, WM_CLOSE, 0, 0);
          end;
        end;
      end;
    end;
    
    
    { TProcessList }
    
    function TProcessList.Add(AProcess: TProcess): Integer;
    begin
      Result := inherited Add(AProcess);
    end;
    
    function TProcessList.GetProcess(AIndex: Integer): TProcess;
    begin
      Result := TProcess(Items[AIndex]);
    end;
    
    { TWindowList }
    
    function TWindowList.Add(AWindow: TWindow): Integer;
    begin
      Result := inherited Add(AWindow);
    end;
    
    function TWindowList.GetWindow(AIndex: Integer): TWindow;
    begin
      Result := TWindow(Items[AIndex]);
    end;
    
    { TWindow }
    
    function TWindow.GetProcessHandle: THandle;
    begin
      if FProcessHandle = 0 then
        FProcessHandle := OpenProcess(Windows.SYNCHRONIZE or Windows.PROCESS_TERMINATE,
         True, FProcessID);
      Result := FProcessHandle;
    end;
    
    function TWindow.GetProcessID: Cardinal;
    var
      Pid : Cardinal;
    begin
      if FProcessID = 0 then
      begin
        Pid := 1;
        GetWindowThreadProcessId(Handle, Pid);
        FProcessID := Pid;
      end;
      Result := FProcessID;
    end;
    
    
    function TWindow.GetProcessName: string;
    var
      Buffer : packed array [1..1024] of char;
      len : LongWord;
    begin
      FillChar(Buffer, SizeOf(Buffer), 0);
      if FProcessName = '' then
      begin
        len := GetWindowModuleFileName(Handle, @Buffer[1], 1023);
        FProcessName := Copy(Buffer, 1, Len);
      end;
      Result := FProcessName;
    end;
    
    end.
    
    0 讨论(0)
  • 2020-12-23 12:57

    Add CloseApplications=true in [Setup] section.

    If set to yes or force and Setup is not running silently, Setup will pause on the Preparing to Install wizard page if it detects applications using files that need to be updated by the [Files] or [InstallDelete] section, showing the applications and asking the user if Setup should automatically close the applications and restart them after the installation has completed.

    0 讨论(0)
提交回复
热议问题