NSIS: Получение списка файлов с MD5-хешами.
Как-то показывал, как в NSIS получить MD5-хэши (копия), файлов в т.ч. Решил расширить пример, а как получить список файлов в каталоге и их MD5-хэши. Мне пригодится для следующего примера по 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} "[Path]" "[Options]" "Function"
где:
"[Path]"
- путь, стартовый каталог для поиска"[Options]"
- опции (см. далее)"Function"
- callback-функция (см. далее)/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} "C:\SOMEDIR" "/L=F" "Userfunction"
Где мы указали каталог, с которого нужно начать поиск, указали хоть одну опцию, нужно указать хотя бы одну, и указали функцию обратного вызова, пока имя условное, но ниже объясню.
Это пользовательская функция, которая должна получать данные от
${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"'
Код секции целиком
После секции организуем пользовательскую функцию:
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
Это репост с сайта http://tolik-punkoff.com
Оригинал: https://tolik-punkoff.com/2023/03/15/nsi