NSIS: Получение списка файлов с MD5-хешами.

Преамбула

Как-то показывал, как в NSIS получить MD5-хэши (копия), файлов в т.ч. Решил расширить пример, а как получить список файлов в каталоге и их MD5-хэши. Мне пригодится для следующего примера по NSIS, но и народ спрашивал, ибо в вышеуказанном примере нельзя выбрать произвольный файл, или сразу несколько. Исправляемся.

Поиск файлов на NSIS

Перед тем, как обработать какие-то файлы, надо их сначала найти и как-то передать в инсталлятор. В NSIS есть для этого несколько способов:

— Использовать стандартные функции FindFirst/FindNext, которые работают почти также, как соответствующие функции в WinAPI и требуют городить довольно неудобочитаемый цикл, постоянно проверять ошибки.
— Рекомендуемый разработчиками NSIS способ: использовать плагин Locate, работа с которым тоже отличается довольно неудобочитаемым кодом, и работает он в некоторых случаях не совсем стабильно.
— Функция Locate, которую вообще-то должен был заменить вышеуказанный плагин, но, как по мне, заменил ее криво. Ей проще воспользоваться, во всяком случае, для такой небольшой задачи. Далее будем работать именно с функцией Locate.

1. Создаем файл Locate.nsi в каталоге проекта, помещаем в него код функции, который можно взять их официальной Wiki NSIS. Раздел Function Code. (копия на PasteBin копия на GitHub)

2. Создаем основной файл (MD5List.nsi), формируем «болванку» проекта:

Unicode true
!include "Locate.nsi"

Name "MD5List"
OutFile "MD5List.exe"
ShowInstDetails show
RequestExecutionLevel User
InstallDir "$EXEDIR\"
DirText "Choose the folder to get list files and MD5 checksums."

Section "List"
SectionEnd

Не забываем подключить файл с кодом функции Locate (Locate.nsi): !include "Locate.nsi"

Синтаксис функции Locate

${Locate} "[Path]" "[Options]" "Function"

где:

"[Path]" — путь, стартовый каталог для поиска
"[Options]" — опции (см. далее)
"Function" — callback-функция (см. далее)

Опции функции Locate

/L=[FD|F|D|DE|FDE] — что искать:
/L=FD - Найти файлы и каталоги (по умолчанию)
/L=F - Найти только файлы
/L=D - Найти только каталоги
/L=DE - Найти только пустые каталоги
/L=FDE - Найти файлы и пустые каталоги

/M=[маска_файла].
По умолчанию /M=*.* (все файлы)
Можно изменить, например на /M=*.txt (текстовые файлы), и т.д.

/S=
Размер файла. Не буду заострять на этом внимание, синтаксис этой опции можно посмотреть оригинальной документации. По умолчанию размер файла игнорироуется.

/G=[1|0] — поиск с подкаталогами:
/G=1 — включить поиск с подкаталогами (включен по умолчанию)
/G=0 — поиск только в текущем каталоге

Пример вызова функции Locate

${Locate} "C:\SOMEDIR" "/L=F" "Userfunction"

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

Callback-функция

Это пользовательская функция, которая должна получать данные от ${Locate}, а далее уж сама их обрабатывать.

${Locate} передает в функцию следующие стандартные переменные:

$R9 — полный путь к файлу
$R8 — путь без имени файла
$R7 — имя файла
$R6 — размер (для каталога $R6==0)

Остальные глобальные переменные можно использовать для своих целей внутри функции.

Внимание: callback-функция для ${Locate} обязательно должна заканчиваться командой push <переменная>.

Чтобы завершить функцию нормально, необходимо положить любое значение в стек:

push $0

Всегда помещайте что-либо в стек (даже если вы не собираетесь останавливать поиск), иначе произойдет непредвиденное!

Чтобы прервать поиск до того, как он завершится сам, в стек надо поместить значение StopLocate:

StrCpy $0 StopLocate
Push $0

Внимание! Внутри callback-функции почему-то не работает DetailPrint.

Теперь, зная все вышеозначенное, можно писать основной код.

Код внутри секции

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

GetTempFileName $R0
FileOpen $R1 $R0 w

Начинаем поиск:

DetailPrint "Starting search files and get MD5..."
${Locate} "$INSTDIR" "/L=F /M=*.*" "GetMD5"

$INSTDIR — каталог, который выберет пользователь при запуске примера.
"/L=F /M=*.*" - /L=F — ищем только файлы /M=*.* — все файлы.
"GetMD5" — имя callback-функции (см. ниже)

По окончанию поиска закрываем файл:

FileClose $R1

И проверяем, не случилось ли ошибок. Если случилось, выводим окно с надписью Error, если не случилось — открываем полученный файл в Блокноие:

IfErrors 0 +2
MessageBox MB_OK "Error" IDOK +2
Exec '"notepad.exe" "$R0"'

Код секции целиком

Код callback-функции

После секции организуем пользовательскую функцию:

Function GetMD5
	; скармливаем MD5-плагину полное имя файла
	; про MD5-плагин написано по ссылке выше
	md5dll::GetMD5File "$R9"
	Pop $1 ; достаем из стека MD5
	; Записываем во временный файл (дескриптор в $R1)
	; имя найденного файла с путем: $R9
	; через табуляцию $\t MD5-хэш ($1)
	; и перевод строки $\r$\n
	FileWrite $R1 "$R9:$\t $1 $\r$\n"
	
	; завершаем функцию, помещая в стек
	; пустую переменную
	Push $0 
FunctionEnd

Проверка

Скормим программе ее же каталог.

В Блокноте откроется временный файл со следующим содержимым:

E:\Test\Locate.nsi:	      a8fb937f9509d7fc21f476b2ff59aaa3 
E:\Test\MD5List.exe:	 34edf2e42835380b7278e89315ec1a83 
E:\Test\MD5List.nsi:	 fb531c0dfa3bae4febe0b7cfbe7d4a88

Ссылки

NSIS: контрольная сумма (MD5), сравнение файлов. (копия)
Locate на NSIS Wiki
Пример на GitHub

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *