varios tutorial studio setup script programas org jrsoftware isdl instalar innosetup inno help descargar con installer inno-setup

installer - tutorial - Cierre la versión en ejecución del programa antes de instalar la actualización(Inno Setup)



jrsoftware (9)

Esto debería ser simple, debo detener la ejecución de cualquier versión anterior de mi programa cuando se inicie el instalador.

La mayoría de las personas sugirieron hacer un exe que haga esto y llamarlo antes de que se inicie Inno Setup. exe un exe usando AutoIt que mata todos los procesos de mi programa. El problema es que no sé cómo hacer que Inno Setup lo llame antes de instalar nada.

¿Cómo llamo a un ejecutable antes de instalar archivos?

Alternativamente, si puedo detectar si un programa se está ejecutando y decirle al usuario que lo cierre, también funcionaría.


Aquí hay un enlace a una secuencia de comandos Inno Setup que le pide a un usuario que cierre el programa de destino, si detecta que el programa se está ejecutando. Después de que el usuario cierre el programa, puede hacer clic en el botón "Reintentar" para continuar con la instalación:

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

Esta secuencia de comandos se basa en una secuencia de comandos más simple, que se encuentra en la Base de conocimientos de Inno Setup Extensions:

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


Bueno, creo que la forma más fácil de realizar esto es crear una DLL en Delphi que detecte si su programa se está ejecutando y pedirle al usuario que lo cierre, ponga esa DLL en su configuración y use la bandera "dontcopy" (ver http://www.jrsoftware.org/ishelp/ bajo Pascal Scripting / Using DLLs para un ejemplo).

Por cierto, la próxima vez use mutexes, Inno Setup también admite eso y es mucho más fácil.

EDITAR: y para extraer un archivo (si desea usar ese .exe que menciona), simplemente use ExtractTemporaryFile ().


En la versión 5.5.0 (lanzada en mayo de 2012), Inno Setup agregó soporte para la API del Administrador de reinicio en Windows Vista y más reciente.

Cita de la documentación vinculada a MSDN (énfasis mío):

La razón principal por la que la instalación y las actualizaciones del software requieren un reinicio del sistema es que algunos de los archivos que se están actualizando están siendo utilizados actualmente por una aplicación o servicio en ejecución. Restart Manager permite que todas las aplicaciones y servicios, excepto los críticos, se cierren y reinicien . Esto libera los archivos que están en uso y permite que se completen las operaciones de instalación. También puede eliminar o reducir la cantidad de reinicios del sistema que se requieren para completar una instalación o actualización.

Lo bueno es que no necesita escribir código personalizado en el instalador o en su aplicación para pedirle al usuario que lo cierre o lo cierre automáticamente.

Si desea que su aplicación se reinicie después de que se complete la actualización, primero debe llamar a la función RegisterApplicationRestart desde su aplicación.

Los valores predeterminados para las nuevas directivas cierran todos los archivos .exe, .dll y .chm contenidos en la sección [Files] del instalador.

Los cambios relacionados con él son (de las notas de la versión):

  • Se agregó una nueva directiva de la sección [Setup] : CloseApplications , que por defecto es yes . Si se configura en sí y el programa de instalación no se ejecuta de forma silenciosa, el programa de instalación ahora se detendrá en la página del asistente de preparación para la instalación si detecta aplicaciones que utilizan archivos que deben actualizarse mediante la sección [Files] o [InstallDelete] , mostrando las aplicaciones y preguntando al usuario si el programa de instalación debería cerrar automáticamente las aplicaciones y reiniciarlas una vez que se haya completado la instalación. Si se configura en sí y el programa de instalación se ejecuta de forma silenciosa, el programa de instalación siempre cerrará y reiniciará dichas aplicaciones, a menos que se le indique que no lo haga a través de la línea de comandos (ver más abajo).
  • Se agregó una nueva directiva de la sección [Setup] : CloseApplicationsFilter , que por defecto es *.exe,*.dll,*.chm . Controla qué archivos de configuración comprobará que estén en uso. Establecer esto en *.* Puede proporcionar una mejor comprobación a expensas de la velocidad.
  • Se agregó una nueva directiva de la sección [Setup] : RestartApplications , que por defecto es yes . Nota: para que la instalación pueda reiniciar una aplicación después de que se haya completado la instalación, la aplicación debe estar usando la función de API RegisterApplicationRestart Windows.
  • Se agregaron nuevos parámetros de línea de comandos compatibles con la Configuración: /NOCLOSEAPPLICATIONS y /NORESTARTAPPLICATIONS . Se pueden usar para anular las nuevas directivas CloseApplications y RestartApplications .
  • Agregada nueva función de soporte [Code] : RmSessionStarted .
  • TWizardForm : Nueva propiedad PreparingMemo agregada.

He tenido éxito usando 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;

También puede hacerlo en el InitializeSetup pero si lo hace, tenga en cuenta que todavía no tiene acceso a la {app} constante. Mi programa no solicita la ruta de instalación, pero la tuya podría.


InnoSetup le permite adjuntar scripts de Pascal a varios lugares en el proceso de construcción. Intente adjuntar un script que llame a ShellExecute. (Es posible que tenga que importar al motor de script si aún no lo tiene).


Intenté usar la respuesta aceptada (y el seguimiento por jachguate) pero no mataría mi solicitud. Parece que parte de la razón fue que la ventana de mi aplicación no tenía texto asociado, pero sea cual sea la razón real, usé el comando de shell para matarlo y eso funcionó. En la sección [código], desea agregar la siguiente función. Se llama justo antes de que se copien los archivos de configuración.

function PrepareToInstall(var NeedsRestart: Boolean): String; var ErrorCode: Integer; begin ShellExec(''open'', ''taskkill.exe'', ''/f /im MyProg.exe'','''',SW_HIDE,ewNoWait,ErrorCode); end;


Si está feliz de escribir su propia DLL, puede usar la herramienta de ayuda de la API para TlHelp32.pas para determinar qué aplicaciones se están ejecutando, y luego obtener un identificador de ventana para ellas usando EnumWindows, luego enviar un WM_CLOSE al identificador de ventana.

Es un poco molesto, pero debería funcionar: Tengo algunas clases de envoltorios de utilidades que desarrollé con un amigo hace un tiempo. No puedo recordar si nos basamos en el código de otra persona.

TWindows.ProcessISRunning y TWindows.StopProcess pueden ayudar.

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.


Si está utilizando InnoSetup, podría intentar que su instalador InnoSetup realice un mensaje SendBroadcast de Windows y que su aplicación escuche ese mensaje. Cuando su aplicación recibe el mensaje, debería cerrarse.

Lo he hecho yo mismo con un instalador InnoSetup, y funciona muy bien.


Si la aplicación tiene un Mutex, puede agregar un valor de AppMutex en su instalador Inno Setup y mostrará un mensaje que le indica al usuario que detenga el programa. Es posible que pueda encontrar el Mutex (si lo tiene) utilizando SysInternals Process Explorer y seleccionando el programa / proceso y mirando los Mangos (CTRL-H) en el Panel Inferior.

Aquí hay un enlace al artículo de KB que menciona varios métodos:
http://www.vincenzo.net/isxkb/index.php?title=Detect_if_an_application_is_running

Alternativamente, puede probar este código (SIN PRUEBA) en el 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;