Толик Панков
hex_laden
............ .................. ................
October 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Толик Панков [userpic]
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

Это репост с сайта http://tolik-punkoff.com
Оригинал: https://tolik-punkoff.com/2023/03/15/nsis-poluchenie-spiska-fajlov-s-md5-heshami/

Tags: ,