Как-то показывал, как в 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