Назад к списку статей

Использование библиотеки ESSvcControl.dll в составе InnoSetup

 

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

Одной из лучших программ для создания установок является программа InnoSetup, которая имеет в своем составе все, что требуется для создания качественной и функциональной программы установки:
- пошаговый мастер, позволяющий быстро создать простую программу установки;
- редактор сценария, позволяющий включить дополнительные возможности установки (создание ярлыков, запуск программ, добавление значений в реестр и в INI-файлы и т.д.);
- секция "Code" в сценарии установки, позволяющая использовать множество встроенных в InnoSetup функций, предназначенных для работы с файлами и папками, получения системной информации, работы с реестром, INI-файлами, строками и массивами, создания диалогов и многие другие функции, подробно описанные в документации к InnoSetup.

Синтаксис, применяемый в секции "Code", аналогичен синтаксису языка Pascal (а объектно-ориентированные вещи аналогичны Delphi) с некоторыми дополнениями InnoSetup. Таким образом, это дает возможность писать программу установки как обычную программу, используя встроенные функции InnoSetup.

К сожалению, среди этого множества функций не присутствуют некоторые достаточно важные функции, без которых зачастую бывает неудобно создавать полноценную программу установки. К ним, например, относятся функции управления службами Windows. Конечно, можно использовать встроенные приложения Windows для этих целей (например, "Net.exe") с соответствующими параметрами командной строки, но это не самый лучший вариант, поскольку у конечного пользователя может и не быть указанного приложения или установлен запрет на его запуск.

Для того, чтобы иметь возможность управлять службами без использования сторонних компонентов, автором была создана библиотека "ESSvcControl.dll", в которой уже имеются все необходимые функции. Скачать библиотеку можно здесь.

Поддерживаемые функции:

ServiceStart(aMachine, aServiceName : string): boolean - предназначена для запуска службы;
ServiceStop(aMachine,aServiceName : string): boolean - предназначена для остановки службы;
ServicePause(aMachine,aServiceName : string): boolean - выполняет приостановку работы службы;
ServiceContinue(aMachine,aServiceName : string): boolean - выполняет возобновление работы службы;
ServiceGetStatus(sMachine, sService: PChar): DWORD - возвращает состояние службы ("работает", "остановлена", "пауза" и т.д.).

Для всех функций в качестве входных аргументов используются строки "aMachine" - имя компьютера и "aServiceName" - имя службы (для последней функции используется тип PCHAR). Следует отметить, что в качестве имени службы следует использовать реальное имя, а не то, которое выводится в списке служб в оснастке "Службы". Для получения реального имени необходимо два раза щелкнуть по соответствующей службе в списке, необходимая информация находится в поле "Имя службы". На рисунке ниже показан пример (имя службы подчеркнуто красным).

Имя службы

В качестве имени компьютера используется строка нулевой длины (не nil!), если запрашивается информация о службе на локальном компьютере, или имя компьютера, если запрашивается состояние службы на удаленном компьютере.

Все функции, кроме последней, возвращают логическое значение "true" (истина), если функция выполнилась успешно, либо "false" (ложь), если в процессе выполнения функции возникли какие-либо ошибки. Последняя функция (получение информации о состоянии службы) возвращает состояние службы в виде числового значения либо значения типа DWORD. Все возможные значения приведены в таблице ниже.

Значения, возвращаемые функцией ServiceGetStatus
Числовое значение Значение DWORD Состояние службы
-1 - Ошибка доступа к службе
1 SERVICE_STOPPED Служба остановлена
2 SERVICE_START_PENDING Ожидание запуска службы
3 SERVICE_STOP_PENDING Ожидание остановки службы
4 SERVICE_RUNNING Служба запущена
5 SERVICE_CONTINUE_PENDING Ожидание возобновления работы службы
6 SERVICE_PAUSE_PENDING Ожидание приостановки службы
7 SERVICE_PAUSED Служба приостановлена

Использовать библиотеку "ESSvcControl.dll" в составе InnoSetup несложно. Для этого необходимо добавить ссылку на файл библиотеки в разделе "Files", проинициализировать необходимые функции в начале секции "Code" и вызвать их в требуемом месте. Единственная сложность, которая может возникнуть - как использовать библиотеку при удалении приложения. Для этого используется следующий прием: библиотека при установке приложения копируется в определенное место (например, в папку с приложением), и при удалении приложения инициализируется именно из этой папки.

Удобно рассмотреть указанные процедуры на примере.

Первым делом необходимо добавить ссылку на библиотеку в разделе "Files":

[Files]
Source: "ESSvcControl.dll"; DestDir: {tmp}; Flags: dontcopy
Source: "ESSvcControl.dll"; DestDir: {app}\Uninst; Flags: ignoreversion overwritereadonly

...
остальные файлы приложения
...

В первой строчке указывается, что папкой назначения для файла библиотеки "ESSvcControl.dll" является временная папка, и автоматического копирования в эту папку производиться не будет (флаг "dontcopy"). Во второй строчке тот же файл библиотеки копируется в папку с приложением в подпапку "Uninst". Также в эту папку удобно скопировать остальные файлы, необходимые для удаления приложения. Это делается добавлением в раздел "Setup" параметра "UninstallFilesDir":

[Setup]
UninstallFilesDir={app}\Uninst

После того, как файл библиотеки включен в состав дистрибутива, можно приступать к инициализации функций. Переходим в секцию "Code" (или создаем ее, если такой секции нет) и производим инициализацию функций следующим образом:

[Code]

function ServiceGetStatus(sMachine, sService: PChar): DWORD;
external 'ServiceGetStatus@files:ESSvcControl.dll stdcall delayload setuponly';

function ServiceStart(aMachine, aServiceName : string): boolean;
external 'ServiceStart@files:ESSvcControl.dll stdcall delayload setuponly';

function ServiceStop(aMachine,aServiceName : string): boolean;
external 'ServiceStop@files:ESSvcControl.dll stdcall delayload setuponly';

Инициализация функций происходит так же, как и в Delphi, за исключением некоторых дополнительных флагов. Имена функций находятся после ключевого слова "function", и именно по этим именам и проиходит вызов функций. Флаг "external" предписывает программе установки загружать функцию из внешнего файла, флаг "stdcall" означает стандартный вызов функции. Флаги "delayload" и "setuponly" добавлены в InnoSetup.

Флаг "delayload" используется для инициализации функции только тогда, когда к ней будет обращение. Если этого флага не добавлять, то загрузка библиотеки будет происходить при запуске программы установки, и, в случае, если библиотека не будет найдена, программа установки не запустится. Флаг "delayload" позволяет избежать такой ситуации.

Флаг "setuponly" предписывает программе установки загружать соответствующую функцию только при установке (но не при удалении приложения).

После того, как инициализация функций успешно пройдена, их можно вызывать в требуемом месте следующим образом:

if not (ServiceGetStatus('', 'SharedAccess') = 1) then
begin
ServiceStop('', 'SharedAccess');
end;

Если требуется использовать функции из билиотеки при удалении приложения, то их также необходимо проинициализировать:

[Code]

function UnServiceGetStatus(sMachine, sService: PChar): DWORD;
external 'ServiceGetStatus@{app}\Uninst\ESSvcControl.dll stdcall delayload uninstallonly';

function UnServiceStart(aMachine, aServiceName : string): boolean;
external 'ServiceStart@{app}\Uninst\ESSvcControl.dll stdcall delayload uninstallonly';

function UnServiceStop(aMachine,aServiceName : string): boolean;
external 'ServiceStop@{app}\Uninst\ESSvcControl.dll stdcall delayload uninstallonly';

Для функций, используемых на этапе удаления приложения, желательно, чтобы имена отличались от имен функций, используемых на этапе установки. Путь к файлу библиотеки задается к папке, где находятся файлы, необходимые для удаления (файл был помещен в эту папку посредством директивы в секции "Files"). На этапе удаления приложения используется флаг "uninstallonly", который предписывает программе установки использовать данную функцию только во время удаления.

Последний вопрос, который остается - как удалить используемую библиотеку вместе с приложением? Для этой цели существует процедура "UnloadDLL", которую необходимо выполнить до начала непосредственно удаления. Поэтому представляется целесообразным использование библиотеки перед этапом удаления приложения следующим образом:

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
  ErrorCode: integer;
begin
  if CurUninstallStep = usUninstall then                           // непосредственно начало процесса удаления
  begin
    if not (UnServiceGetStatus('', 'SharedAccess') = 1) then       // остановка служб
    begin
      UnServiceStop('', 'SharedAccess');
    end;
    ShellExec('open', ExpandConstant('{app}\MyServer.exe'), '/unregister', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
    ...
    выполнение других действий, требующих остановленных служб
    ...
    if not (UnServiceGetStatus('', 'SharedAccess') = 4) then       // запуск служб после выполнения необходимых действий
    begin
      UnServiceStart('', 'SharedAccess');
    end;
    UnloadDLL(ExpandConstant('{app}\Uninst\ESSvcControl.dll'));    // выгрузка библиотеки из памяти
    DeleteFile(ExpandConstant('{app}\Uninst\ESSvcControl.dll'));   // удаление файла библиотеки
    RemoveDir(ExpandConstant('{app}\Uninst'));
  end;
end;

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

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

Таким образом, использование библиотеки "ESSvcControl.dll" существенно упрощает работу с InnoSetup, дополняет его новыми возможностями и расширяет круг задач, которые могу быть решены, не прибегая к дополнительным средствам.

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

Назад к списку статей

Сайт создан в системе uCoz