понедельник, 18 марта 2019 г.

Как сделать инсталлятор ZDLL модуля на InnoSetup

Как сделать инсталлятор ZDLL модуля на InnoSetup для MiniM Database Server

Для того чтобы сделать инсталлятор, надо определить его цели. В нашем случае это будут:
  1. Предложить выбрать один из имеющихся установленный на компьютере серверов MiniM.
  2. Если этот сервер не запущен, то запустить его.
  3. Выложить в его подкаталог /bin файл zdll модуля и рутину поддержки, если она есть.
  4. Выполнить импорт рутины на сервер.
Создаем в InnoSetup файл описания инсталляции iss и заполняем заголовок. В примере приводится инсталляция для модуля NTLM авторизации.

Секция [Setup] заполняется учетной информацией общего плана.
[Setup]
AppName=MiniM ZDLL module for NTLM autorization
AppVerName=MiniM ZDLL NTLM module
OutputBaseFilename=setup-zntlm
DefaultDirName={pf}\MiniM
DefaultGroupName=MiniM
AppPublisher=Eugene Karataev
AppPublisherURL=http://karataev.nm.ru
AllowNoIcons=yes
OutputDir=W:\MiniM\zdll\install
Compression=lzma
SolidCompression=yes
Uninstallable = no
В секции [Files] указываем два устанавливаемых файла:
[Files]
Source: "w:\MiniM\bin\zntlm.dll"; DestDir: "{app}\bin"
Source: "w:\MiniM\zdll\zntlm\%ntlm.rou"; DestDir: "{app}\bin"
Далее, нам надо не использовать предлагаемый по умолчанию инсталлятором выбор каталога, а запросить пользователя выбрать один из имеющихся установленных на компьютере серверов MiniM. Перечень установленных серверов можем получить из реестра, а вместо страницы выбора каталога нам нужно сделать свою страницу для очередного шага инсталлятора.

В секции [CustomMessages] прописываем константные строки:
[CustomMessages]
SelectInstanceCaption=Select available MiniM instance to install to
SelectInstanceDescription=
В секции [Code] описываем функцию создания свой страницы для шага инсталлятора и в функцию InitializeWizard добавляем ее вызов:
[Code]
var
  lblInstance: TLabel;
  lbInstanceList: TListBox;
  InstallName: String;

procedure SelectInstance_Activate(Page: TWizardPage);
begin
  lbInstanceList.ItemIndex := -1;
  WizardForm.NextButton.Enabled := False;
end;

function SelectInstance_ShouldSkipPage(Page: TWizardPage): Boolean;
begin
  Result := False;
end;

function SelectInstance_BackButtonClick(Page: TWizardPage): Boolean;
begin
  Result := True;
end;

function SelectInstance_NextButtonClick(Page: TWizardPage): Boolean;
begin
  Result := True;
end;

procedure SelectInstance_CancelButtonClick(Page: TWizardPage; var Cancel, 
Confirm: Boolean);
begin
end;

procedure SelectInstance_Click( Sender: TObject);
var
  Index: Integer;
  Dir: String;
  Group: String;
begin
  Index := lbInstanceList.ItemIndex;
  if Index = - 1 then
  begin
    WizardForm.NextButton.Enabled := False;
  end
  else
  begin
    InstallName := lbInstanceList.Items.Strings[ Index];
    InstallName := Copy( InstallName, 1, Pos( ' ', InstallName) - 1);
    RegQueryStringValue( HKEY_LOCAL_MACHINE,
      'SOFTWARE\MiniM Group\MiniM\Instance\' + 
      InstallName, 'Directory', Dir);
    WizardForm.DirBrowseButton.Visible := False;
    WizardForm.DirEdit.Enabled := False;
    WizardForm.DirEdit.Text := Dir;
    RegQueryStringValue( HKEY_LOCAL_MACHINE,
      'SOFTWARE\MiniM Group\MiniM\Instance\' + 
      InstallName, 'Group', Group);
    WizardForm.GroupEdit.Text := Group;
    WizardForm.NextButton.Enabled := True;
  end;
end;

function SelectInstance_CreatePage(PreviousPageId: Integer): Integer;
var
  Page: TWizardPage;
  Names: TArrayOfString;
  I: Integer;
  Ver: String;
begin
  Page := CreateCustomPage(
    PreviousPageId,
    ExpandConstant('{cm:SelectInstanceCaption}'),
    ExpandConstant('{cm:SelectInstanceDescription}')
  );

  { lblInstance }
  lblInstance := TLabel.Create(Page);
  with lblInstance do
  begin
    Parent := Page.Surface;
    Left := ScaleX(16);
    Top := ScaleY(8);
    Width := ScaleX(150);
    Height := ScaleY(13);
    Caption := 'Select available MiniM instance:';
  end;

  { lbInstanceList }
  lbInstanceList := TListBox.Create(Page);
  with lbInstanceList do
  begin
    Parent := Page.Surface;
    Left := ScaleX(24);
    Top := ScaleY(24);
    Width := ScaleX(321);
    Height := ScaleY(201);
    TabOrder := 0;
    OnClick := @SelectInstance_Click;
  end;

  // заполняем список перечнем имеющихся инсталляций
  RegGetSubkeyNames( HKEY_LOCAL_MACHINE,
    'SOFTWARE\MiniM Group\MiniM\Instance', Names);
  for I := 0 to GetArrayLength(Names)-1 do
  begin
    RegQueryStringValue( HKEY_LOCAL_MACHINE,
      'SOFTWARE\MiniM Group\MiniM\Instance\' + 
      Names[ I], 'Version', Ver);
    lbInstanceList.Items.Add( Names[ I] + 
      '          (ver. ' + Ver + ')');
  end;

  lbInstanceList.ItemIndex := -1;

  with Page do
  begin
    OnActivate := @SelectInstance_Activate;
    OnShouldSkipPage := @SelectInstance_ShouldSkipPage;
    OnBackButtonClick := @SelectInstance_BackButtonClick;
    OnNextButtonClick := @SelectInstance_NextButtonClick;
    OnCancelButtonClick := @SelectInstance_CancelButtonClick;
  end;

  Result := Page.ID;
end;

procedure InitializeWizard();
begin
  SelectInstance_CreatePage(wpWelcome);
end;
Для того, чтобы инсталлятор не смог продолжить работу пока не выбран сервер MiniM, укажем это в функции SelectInstance_Activate
procedure SelectInstance_Activate(Page: TWizardPage);
begin
  lbInstanceList.ItemIndex := -1;
  WizardForm.NextButton.Enabled := False;
end;
И в функции-обработчике выбора в списке SelectInstance_Click укажем разрешение продолжить работу:
  if Index = - 1 then
  begin
    WizardForm.NextButton.Enabled := False;
  end
  else
  begin
    ...
    WizardForm.NextButton.Enabled := True;
  end;
Шаги изменения состояния DirBrowseButton и DirEdit используем, чтобы указать инсталлятору что каталог инсталляции мы выбрали самостоятельно:
    
    WizardForm.DirBrowseButton.Visible := False;
    WizardForm.DirEdit.Enabled := False;
    WizardForm.DirEdit.Text := Dir;
После того, как файл dll и rou установлены инсталлятором в нужный каталог (а это выбранный нами программно каталог, соответствующий переменной {app} и его подкаталог /bin), надо запустить сервер. Если он уже запущен, то игнорировать результат. Это выполняется в функции CurStepChanged на шаге CurStep = ssPostInstall.
procedure CurStepChanged(CurStep: TSetupStep);
var
  AppDir: String;
  ErrorCode: Integer;
begin
  if CurStep = ssPostInstall then
  begin
    AppDir := ExpandConstant('{app}');
    // start mnmsvc.exe
    ShellExec( 'open', 'net', 
      'start "MiniM Service for ' + InstallName, AppDir + '\bin',
      SW_HIDE, ewWaitUntilTerminated, ErrorCode);
    // import routines
    ShellExec( 'open', AppDir + 
      '\bin\minim.exe', 
      '-x "zn \"%sys"" d imp^boot0(\"%ntlm.rou\")" -h', 
      AppDir + '\bin',
      SW_SHOW, ewWaitUntilTerminated, ErrorCode);
  end;
end;
В результате компиляции получаем exe с инсталлятором, который при запуске выводит приветственное информационное сообщение о том, что он устанавливает, предлагает выбрать один из имеющихся серверов MiniM, установленных на компьютере, устанавливает в подкаталог /bin два файла - dll + rou и проводит импорт рутины в нужную область. По окончании работы инсталлятора ZDLL модуль готов к работе.

Итого, из ключевых нестандартных элементов мы использовали:
  • Создали свою дополнительную страницу в шаги визарда
  • Отменили использование штатной страницы выбора каталога
  • Вставили в пост-инсталл шаг запуск сервера через вызов net start
  • Вставили в пост-инсталл шаг импорт рутины в том же виде, в котором он используется собственно инсталлятором сервера.
По окончании инсталляции у нас остаются в подкаталоге /bin файл dll, который используется для работы и файл rou, который в принципе более не нужен, но его администратор впоследствии может использовать для импорта самостоятельно.

Комментариев нет:

Отправить комментарий