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.
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;
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éfautyes
. 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éfautyes
. Note: pour que Setup puisse redémarrer une application Une fois l'installation terminée, L'application doit utiliser WindowsRegisterApplicationRestart
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 nouvelleCloseApplications
etRestartApplications
les directives.- ajout de nouveaux
[Code]
fonction support:RmSessionStarted
.TWizardForm
: ajout de nouveauxPreparingMemo
propriété.
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;
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.
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
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.
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.
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à.)
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 ().