Подтвердить что ты не робот

Закройте запущенную версию программы перед установкой обновления (Inno Setup)

Это должно быть просто, мне нужно остановить любую предыдущую версию моей программы, начиная с запуска установщика.

Большинство людей предложили сделать exe, который делает это и называет его до запуска Inno Setup. Я создал exe, используя AutoIt, который убивает все процессы моей программы. Проблема в том, что я не знаю, как заставить Inno Setup вызвать его до того, как он установит что-нибудь.

Как вызвать исполняемый файл перед установкой файлов?

В качестве альтернативы, если я могу просто определить, запущена ли программа и сказать пользователю закрыть ее, это тоже сработает.

4b9b3361

Ответ 1

Если приложение имеет Mutex, вы можете добавить значение AppMutex в установщик Inno Setup и отобразит сообщение, в котором пользователь должен остановить программу. Вы можете найти Mutex (если он есть) с помощью SysInternals Process Explorer и выбора программы/процесса и просмотра ручек (CTRL-H) в нижней панели.

Здесь ссылка на статью KB, в которой упоминаются несколько методов:
http://www.vincenzo.net/isxkb/index.php?title=Detect_if_an_application_is_running

В качестве альтернативы вы можете попробовать этот код (UNTESTED) в 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;

Ответ 2

В версии 5.5.0 (выпущен в мае 2012 г.) Inno Setup добавила поддержку Restart Manager API в Windows Vista и новее.

Цитата из связанной с MSDN документации (выделение мое):

Основная причина установки и обновления программного обеспечения требует перезагрузки системы, что некоторые из файлов, которые обновляются, в настоящее время используются запущенным приложением или службой. Restart Manager позволяет отключать и перезапускать все критические приложения и службы. Это освобождает файлы, которые используются, и позволяет завершить установку. Он также может устранить или уменьшить количество перезапусков системы, которые необходимы для завершения установки или обновления.

Хорошо, что вам не нужно писать собственный код в установщике или приложении, чтобы попросить пользователя закрыть его или закрыть его автоматически.

Если вы хотите, чтобы ваше приложение перезапустилось после завершения обновления, вы должны сначала вызвать функцию RegisterApplicationRestart из вашего приложения.

Значения по умолчанию для новых директив закрывают все файлы .exe,.dll и .chm, содержащиеся в разделе [Files] установщика.

Связанные с ним изменения (из примечаний к выпуску):

  • Добавлена ​​новая директива раздела [Setup]: CloseApplications, которая по умолчанию равна yes. Если установлено "да", а "Настройка" не работает тихо, программа установки приостанавливается на странице "Подготовка к установке", если она обнаруживает приложения с использованием файлов, которые необходимо обновить секцией [Files] или [InstallDelete], показывая приложения и запрашивая если программа установки должна автоматически закрыть приложения и перезапустить их после завершения установки. Если установлено "да", а "Настройка" работает тихо, программа установки всегда закрывает и перезапускает такие приложения, если не будет сказано через командную строку (см. Ниже).
  • Добавлена ​​новая директива раздела [Setup]: CloseApplicationsFilter, которая по умолчанию равна *.exe,*.dll,*.chm. Контролирует, какие файлы Setup будут проверяться на предмет использования. Установка этого параметра на *.* может обеспечить лучшую проверку за счет скорости.
  • Добавлена ​​новая директива раздела [Setup]: RestartApplications, которая по умолчанию равна yes. Примечание. Чтобы программа установки могла перезапустить приложение после завершения установки, приложение должно использовать функцию Windows RegisterApplicationRestart API.
  • Добавлены новые параметры командной строки, поддерживаемые установкой: /NOCLOSEAPPLICATIONS и /NORESTARTAPPLICATIONS. Они могут использоваться для переопределения новых директив CloseApplications и RestartApplications.
  • Добавлена ​​новая функция поддержки [Code]: RmSessionStarted.
  • TWizardForm: добавлено новое свойство PreparingMemo.

Ответ 3

Я пробовал использовать принятый ответ (и следить за jachguate), но он не убьет мое приложение. Похоже, что причина в том, что в моем окне приложения не было никакого связанного с ним текста, но что бы ни была реальной причиной, я использовал команду оболочки, чтобы убить ее, и это сработало. В разделе [code] вы хотите добавить следующую функцию. Он вызывается непосредственно перед копированием файлов установки.

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

Ответ 4

Если вы используете InnoSetup, вы можете посмотреть, как установить установщик InnoSetup на Windows SendBroadcastMessage и заставить приложение прослушивать это сообщение. Когда ваше приложение получает сообщение, оно должно закрыться.

Я сделал это сам с установщиком InnoSetup, и он работает очень хорошо.

Ответ 5

Здесь приведена ссылка на Inno Setup script, в которой пользователю предлагается закрыть целевую программу, если она обнаруживает, что программа запущена. После того, как пользователь закроет программу, они могут нажать кнопку "Повторить", чтобы продолжить установку:

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

Этот script основан на более простом script, найденном в базе знаний расширения Inno Setup:

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

Ответ 6

Если вы счастливы написать свою собственную DLL, вы можете использовать API-интерфейс справки для TlHelp32.pas, чтобы определить, какие приложения запущены, а затем получить дескриптор окна для них с помощью EnumWindows, затем отправить WM_CLOSE в дескриптор окна,

Это немного боль, но она должна работать: У меня есть некоторые классы оболочки утилиты, которые я разработал с другом некоторое время назад. Не могу вспомнить, основывались ли мы на чей-то еще код.

TWindows.ProcessISRunning и TWindows.StopProcess могут помочь.

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.

Ответ 7

У меня был успех с использованием 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;

Вы также можете сделать это в InitializeSetup, но если вы это сделаете, имейте в виду, что у вас еще нет доступа к константе {app}. Моя программа не запрашивает путь установки, но ваш может.

Ответ 8

InnoSetup позволяет присоединять скрипты Pascal к различным местам процесса сборки. Попробуйте подключить script, который вызывает ShellExecute. (Что вам может потребоваться импортировать в движок script, если он еще не имеет его.)

Ответ 9

Ну, я думаю, что более простой способ выполнить это может быть создание DLL в Delphi, которая определяет, запущена ли ваша программа, и попросить пользователя закрыть ее, поместить эту DLL в вашу настройку и использовать флаг "dontcopy" (проверьте в http://www.jrsoftware.org/ishelp/ в разделе Pascal Scripting\Использование DLL для примера).

Btw, в следующий раз, используя мьютексы, Inno Setup также поддерживает это и намного проще.

EDIT: и для извлечения файла (если вы хотите использовать этот .exe, который вы упомянули), просто используйте ExtractTemporaryFile().