Fermez la version en cours d'exécution du programme avant d'installer la mise à jour (Inno Setup)

cela devrait être simple, j'ai besoin d'empêcher toute version précédente de mon programme de fonctionner lorsque l'installateur démarre.

la Plupart des gens ont suggéré de faire un exe qui fait ceci et l'appelle avant que la configuration Inno ne commence. J'ai créé un exe en utilisant AutoIt qui tue tous les processus de mon programme. Le problème est que je ne sais pas comment faire pour que Inno Setup l'appelle avant qu'il n'installe quoi que ce soit.

comment appeler un exécutable avant de l'installer fichiers?

alternativement, si je peux juste détecter si un programme est en cours d'exécution et dire à l'utilisateur de le fermer, cela fonctionnerait aussi.

18
demandé sur Martin Prikryl 2010-08-18 03:46:41

9 réponses

si l'application a un Mutex, vous pouvez ajouter un AppMutex valeur dans votre installateur de Setup Inno et il affichera un message disant à l'utilisateur d'arrêter le programme. Vous pourriez être en mesure de trouver le Mutex (s'il y en a un) en utilisant L'Explorateur de processus SysInternals et en sélectionnant le programme / processus et en regardant les poignées (CTRL-H) dans le volet inférieur.

voici un lien vers L'article de A KB qui en mentionne plusieurs methods:

http://www.vincenzo.net/isxkb/index.php?title=Detect_if_an_application_is_running

alternativement, vous pouvez essayer ce code (non testé) dans le InitializeSetup:

[Setup]
;If the application has  Mutex, uncomment the line below, comment the InitializeSetup function out, and use the AppMutex.
;AppMutex=MyApplicationMutex

[Code]
const
  WM_CLOSE = 16;

function InitializeSetup : Boolean;
var winHwnd: Longint;
    retVal : Boolean;
    strProg: string;
begin
  Result := True;
  try
    //Either use FindWindowByClassName. ClassName can be found with Spy++ included with Visual C++. 
    strProg := 'Notepad';
    winHwnd := FindWindowByClassName(strProg);
    //Or FindWindowByWindowName.  If using by Name, the name must be exact and is case sensitive.
    strProg := 'Untitled - Notepad';
    winHwnd := FindWindowByWindowName(strProg);
    Log('winHwnd: ' + IntToStr(winHwnd));
    if winHwnd <> 0 then
      Result := PostMessage(winHwnd,WM_CLOSE,0,0);
  except
  end;
end;
29
répondu mirtheil 2016-06-21 06:02:42

dans la version 5.5.0 (sorti en mai 2012) Inno Setup a ajouté le support pour le Gestionnaire De Redémarrage API sur Windows Vista et les versions plus récentes.

Citation de MSDN liés à la documentation (l'emphase est mienne):

la principale raison pour laquelle l'installation et les mises à jour du logiciel nécessitent un redémarrage du système est que certains des fichiers qui sont mis à jour sont actuellement utilisés par une application ou un service en cours d'exécution. Restart Manager permet à tous mais les applications et les services critiques à arrêter et redémarrer. Cela libère les fichiers qui sont utilisés et permet aux opérations d'installation de se terminer. Il peut également éliminer ou réduire le nombre de redémarrages du système qui sont nécessaires pour terminer une installation ou une mise à jour.

la bonne chose n'est pas que vous n'avez pas besoin d'écrire du code personnalisé dans l'installateur ou dans votre application pour demander à l'utilisateur de le fermer, ou de le fermer automatiquement.

Si vous voulez votre demande de redémarrer après la mise à jour est terminée, vous devez appeler le RegisterApplicationRestart fonction de votre application.

les valeurs par défaut pour les nouvelles directives ferment tout .EXE. ,dll et .les fichiers chm contenus dans le [Files] section de l'installateur.

Les changements qui s'y rattachent sont (à partir de notes de publication):

  • ajout de nouveaux [Setup] section directive:CloseApplications par défaut yes. Si la valeur oui et Setup ne tourne pas en silence, Setup va maintenant s'arrêter sur la page de préparation à L'installation de l'assistant si elle détecte des applications utilisant des fichiers qui doivent être mis à jour par le [Files] ou [InstallDelete] section, montrant les applications et demandant à l'utilisateur SI Setup devrait fermer automatiquement les applications et les redémarrer une fois l'installation terminée. Si défini à yes et Setup fonctionne silencieusement, Setup fermera toujours et redémarrera de telles applications, à moins qu'on ne lui dise de ne pas le faire via la ligne de commande (voir dessous.)
  • ajout de nouveaux [Setup] section directive:CloseApplicationsFilter par défaut *.exe,*.dll,*.chm. Contrôle quels fichiers Setup vérifieront s'ils sont utilisés. Mettre ce paramètre à *.* permet de fournir une meilleure vérification au détriment de la vitesse.
  • ajout de nouveaux [Setup] section directive:RestartApplications par défaut yes. Note: pour que Setup puisse redémarrer une application Une fois l'installation terminée, L'application doit utiliser Windows RegisterApplicationRestart API fonction.
  • ajout de nouveaux paramètres de ligne de commande pris en charge par Setup:/NOCLOSEAPPLICATIONS et /NORESTARTAPPLICATIONS. Ceux-ci peuvent être utilisés pour remplacer la nouvelle CloseApplications et RestartApplications les directives.
  • ajout de nouveaux [Code] fonction support:RmSessionStarted.
  • TWizardForm: ajout de nouveaux PreparingMemo propriété.
17
répondu jachguate 2016-06-21 05:57:52

j'ai essayé d'utiliser la réponse acceptée (et le suivi par jachguate) mais cela ne tuerait pas mon application. Il semble qu'une partie de la raison était que ma fenêtre d'application n'avait pas de texte associé avec elle, mais quelle que soit la vraie raison, j'ai utilisé la commande shell pour la tuer et cela a fonctionné. Dans la section [code], vous voulez ajouter la fonction suivante. Il est appelé juste avant que les fichiers de configuration soient copiés.

function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ErrorCode: Integer;
begin
      ShellExec('open',  'taskkill.exe', '/f /im MyProg.exe','',SW_HIDE,ewNoWait,ErrorCode);
end;
11
répondu zar 2016-09-20 20:59:49

si vous utilisez InnoSetup, vous pouvez chercher à obtenir votre installateur InnoSetup pour faire un Windows SendBroadcastMessage, et obtenir votre application pour écouter ce message. Lorsque votre application reçoit le message, il devrait se fermer.

Je l'ai fait moi-même avec un installateur InnoSetup, et cela fonctionne très bien.

4
répondu Conor Boyd 2010-08-18 00:13:17

voici un lien vers un script de configuration Inno qui invite un utilisateur à fermer le programme cible, s'il détecte que le programme est en cours d'exécution. Une fois que l'utilisateur ferme le programme, ils peuvent cliquer sur "Réessayer" pour procéder à l'installation:

http://www.domador.net/extras/code-samples/inno-setup-close-a-program-before-reinstalling-it/

ce script est basé sur un script plus simple, trouvé dans la connaissance Inno Setup Extensions Base:

http://www.vincenzo.net/isxkb/index.php?title=Call_psvince.dll_on_install_and_uninstall

3
répondu Andres Cabezas Ulate 2011-05-18 21:20:01

si vous êtes heureux d'écrire votre propre DLL, vous pouvez utiliser l'API d'aide aux outils pour TlHelp32.pas pour déterminer quelles applications sont en cours d'exécution, puis obtenir une poignée de fenêtre pour eux en utilisant EnumWindows, puis envoyer un WM_CLOSE à la poignée de fenêtre.

C'est un peu douloureux, mais ça devrait marcher: J'ai des classes d'emballage que j'ai développées avec un ami il y a quelques temps. Je ne me souviens pas si on l'a basé sur le code de quelqu'un d'autre.

TWindows.ProcessISRunning et TWindows.StopProcess peut aider.

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.
1
répondu Gerry Coll 2010-08-18 09:48:00

j'ai eu du succès en utilisant WMIC:

procedure CurStepChanged(CurStep: TSetupStep);
var
    ResultCode: Integer;
    wmicommand: string;
begin
    // before installing any file
    if CurStep = ssInstall then
    begin
        wmicommand := ExpandConstant('PROCESS WHERE "ExecutablePath like ''{app}\%%''" DELETE');

        // WMIC "like" expects escaped backslashes
        StringChangeEx(wmicommand, '\', '\', True);

        // you can/should add an "if" around this and check the ResultCode
        Exec('WMIC', wmicommand, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
    end;
end;

Vous pouvez aussi le faire dans le InitializeSetup mais si vous le faites, gardez à l'esprit que vous n'avez pas encore accès à l' {app} constante. Mon programme ne demande pas de chemin d'installation, mais les vôtres.

1
répondu thomasb 2017-03-20 10:18:19

InnoSetup vous permet d'attacher des scripts Pascal à différents endroits du processus de construction. Essayez de joindre un script qui appelle ShellExecute. (Que vous devrez peut-être importer dans le moteur de script s'il ne l'a pas déjà.)

0
répondu Mason Wheeler 2010-08-18 00:12:35

Eh bien, je pense que la façon la plus facile d'effectuer cela peut être de créer une DLL dans Delphi qui détecte si votre programme est en cours d'exécution et demander à l'utilisateur de le fermer, mettre cette DLL dans votre configuration et utiliser le drapeau "dontcopy" (check in http://www.jrsoftware.org/ishelp/ under Pascal Scripting \ Using DLLs for an example).

Btw, la prochaine fois, utiliser les mutex, Inno Setup également soutien et est beaucoup plus facile.

Modifier: et pour extraire un fichier (si vous voulez l'utiliser .exe you mention), il suffit D'utiliser ExtractTemporaryFile ().

0
répondu someone 2010-08-18 00:41:19