Перейти к основному содержимому
  1. Projects/

Inno Setup Archive Manager

·758 слов·4 минут· loading · loading ·
Sysadmin Delphi Innosetup Windows
This project is not in active development anymore

Техническое задание
#

Довольно часто мне приходится собирать установочные пакеты и в этом деле нет равных InnoSetup. Я пробовал много подобных систем — NSIS, InstallShield, InstallAware — все они уступают или по качеству, или по функционалу. InstallAware наверное единственный, кто уверенно дышит в спину IS, но тонна неприятных багов данной системы (от вылетов IDE, до Runtime-проблем — так я был неприятно удивлён невозможностью копирования файлов более 2х Гб) заставили меня отказаться от её использования.

InnoSetup имеет множество плагинов, но в одном из последних пакетов передо мной встала необходимость упаковки архивов во время установки. И если модуль распаковки с использованием 7zip есть (к сожалению ссылки на сайт автора не нашёл), то упаковщиков я не нашёл. Не знаю, будет ли это интересно еще кому-нибудь, но тем не менее — встречайте, InnoSetup Archive Manager.

И, да, чуть не забыл — за реализацию API для 7z.dll огромное спасибо Henri Gourvest.

Так как возможности IS, а вернее Pascal Script в Inno Setup, весьма ограничены, местами пришлось городить огород, но в целом получилось очень даже ничего. Из-за отсутствия указателей (да, я знаю о возможности использования uint32, но данный способ вызывает у меня внутреннее беспокойство), все объекты хранятся в памяти библиотеки, поэтому использовать описанные функции необходимо именно в рекомендуемой последовательности.

Загрузка
#

Или на BitBucket

Использование
#

API
#

SZNewArchive
#

1procedure SZNewArchive(const archTypeName: PAnsiChar; const libDir: PAnsiChar);
2external 'SZNewArchive@files:ArchMan.dll stdcall';

Данную процедуру необходимо вызывать непосредственно перед всеми остальными операциями. Здесь происходит выделение памяти под всю нашу кухню. Здесь:

  • archTypeName — тип будущего архива (7z, zip, rar). Первые два поддерживают дополнительные свойства, для rar-архивов не реализовано ничего, однако если кому-то будет нужно — допилить не проблема;
  • libDir — директория поиска библиотеки 7zip. Так как у пользователя он уже может быть установлен, рекомендуется явно передавать этот параметр. При передаче пустой строки — поиск будет произведён в PATH.

SZAddFile
#

1procedure SZAddFile(const fileName: PAnsiChar; const filePath: PAnsiChar);
2external 'SZAddFile@files:ArchMan.dll stdcall';

Данная процедура добавляет одиночный файл к будущему архиву. Здесь:

  • fileName — путь к файлу на диске (напр.: C:\somefile.dat);
  • filePath — путь и имя файла в архиве (напр.: somefolder\anotherfolder\somefile.dat).

SZAddFiles
#

1procedure SZAddFiles(const dir: PAnsiChar; const path: PAnsiChar; const wildCard: PAnsiChar; isRecurse: Boolean);
2external 'SZAddFiles@files:ArchMan.dll stdcall';

Добавление нескольких файлов к будущему архиву. Поддерживаются маски и вложенность каталогов. Здесь:

  • dir — директория-источник на диске (напр.: C:\somedir);
  • path — путь к файлам в архиве (напр.: somefolder/anotherfolder);
  • wildCard — маска добавления файлов (напр.: *);
  • isRecurse — добавлять ли файлы рекурсивно.

SZSetCompressionLevel
#

1procedure SZSetCompressionLevel(level: Cardinal);
2external 'SZSetCompressionLevel@files:ArchMan.dll stdcall';

Установка уровня сжатия. Для 7z и zip в качестве level могут выступать числа: 0, 1, 3, 5, 7, 9, где 9 — лучшее сжатие.

SZSetCompressionMethod
#

1procedure SZSetCompressionMethod(const method: PAnsiChar);
2external 'SZSetCompressionMethod@files:ArchMan.dll stdcall';

Установка метода сжатия. Здесь method может принимать следующие значения:

  • для zip: COPY, DEFLATE, DEFLATE64, BZIP2;
  • для 7z: COPY, LZMA, LZMA2, BZIP2, PPMD, DEFLATE, DEFLATE64.

SZSetProgressHandles
#

1procedure SZSetProgressHandles(inWindowHandle: THandle; inProgressBarHandle: THandle);
2external 'SZSetProgressHandles@files:ArchMan.dll stdcall';

Установка handle’оф для отображения прогресса сжатия. Если не выставлены — ход сжатия отображаться не будет. Здесь:

  • inWindowHandle — идентификатор формы, на которой расположен ProgressBar;
  • inProgressBarHandle — идентификатор индикатора прогресса.

SZSaveToFile
#

1function SZSaveToFile(const fileName: PAnsiChar): THandle;
2external 'SZSaveToFile@files:ArchMan.dll stdcall';

Запуск сжатия архива. Чтобы главная форма установщика не умирала на время сжатия — реализовано в отдельном потоке, поэтому после вызова — сразу же возвращает управление приложению. Сам InnoSetup порождать потоки не умеет, а делать это на чистом WinAPI в скрипте установщика — крутовато, поэтому пришлось пойти таким путём. Здесь:

  • fileName — путь и имя создаваемого архива.

SZIsThreadRunning
#

1function SZIsThreadRunning(handle: THandle; timeToWait: Cardinal): Boolean;
2external 'SZIsThreadRunning@files:ArchMan.dll stdcall';

Проверка, запущен ли поток сжатия архива. Возвращает истину, если поток запущен, ложь, если он был завершен или передан неправильный идентификатор. По-сути данная функция просто обертка для WaitForSingleObject. Здесь:

  • handle — идентификатор потока (его возвращает функция SZSaveToFile);
  • timeToWait — время ожидания ответа от потока в мс, рекомендуемое значение — 200.

Пример использования
#

 1function PackDirectory(dirName: string; fileName: string);
 2var
 3		ThreadHandle: THandle;
 4begin
 5		// Извлечение библиотеки 7-Zip
 6		if (not FileExists(ExpandConstant('{tmp}') + '7z.dll'))
 7				then ExtractTemporaryFile('7z.dll');
 8
 9		// Создание директории для архива
10		ForceDirectories(ExtractFileDir(fileName));
11
12		// Инициализация архива
13		SZNewArchive('zip', ExpandConstant('{tmp}'));
14		// Добавление всех файлов в директории
15		SZAddFiles(PAnsiChar(dirName), '', '*', true);
16		// Установка индикатора для отображения прогресса
17		SZSetProgressHandles(WizardForm.Handle, RepackProgressBar.Handle);
18
19		// Запуск потока создания архива
20		ThreadHandle := SZSaveToFile(PAnsiChar(fileName));
21		// Цикл, проверяющий, завершено ли сжатие
22		while (SZIsThreadRunning(ThreadHandle, 200)) do
23				// Если нет - просто обрабатываем сообщения формы
24				Application.ProcessMessages();
25				// Закрываем хэндл потока
26				CloseHandle(ThreadHandle);
27		end;
28...
29PackDirectory(ExpandConstant('{tmp}tmpFilesDir'), ExpandConstant('{app}archive.zip'));
@soar
Автор
@soar
Senior SRE/DevOps engineer

Related

HTTP Stream Benchmark
·372 слов·2 минут· loading · loading
Sysadmin Delphi Http Benchmark Windows
Win32 приложение для тестирования веб-сервера путем загрузки файлов