NSIS: Удалить каталог полностью с файлами и подкаталогами (RMDir)

Синтаксис:

RMDir [/r] [/REBOOTOK] directory_name

Удалить указанный каталог (полный путь без подстановочных знаков). Без /r каталог будет удален только в том случае, если он полностью пуст. Если указан параметр /r, каталог будет удален рекурсивно, поэтому будут удалены все каталоги и файлы в указанном каталоге. Если указан параметр /REBOOTOK, любой файл или каталог, которые не удалось удалить во время процесса, будут удалены при перезагрузке — если какой-либо файл или каталог будут удалены при перезагрузке, будет установлен флаг перезагрузки. Флаг ошибки устанавливается, если какой-либо файл или директория не могут быть удалены.

Пример:

RMDir $INSTDIR
RMDir $INSTDIR\data
RMDir /r /REBOOTOK $INSTDIR
RMDir /REBOOTOK $INSTDIR\DLLs

Обратите внимание, что текущий рабочий каталог (с экзешником) нельзя удалить. Текущий рабочий каталог устанавливается SetOutPath. Например, следующий пример не удалит каталог:

SetOutPath $TEMP\dir
RMDir $TEMP\dir

В следующем примере удастся удалить каталог:

SetOutPath $TEMP\dir
SetOutPath $TEMP
RMDir $TEMP\dir

Предупреждение: использование RMDir /r $INSTDIR в деинсталляторе небезопасно. Хотя это маловероятно, пользователь может выбрать установку непосредственно в папку Program Files, и поэтому эта команда сотрет всю папку Program Files, включая другие программы, которые не имеют ничего общего с установленной программой.

Доступны решения для более безопасного простого удаления только тех файлов, которые были установлены установщиком.

Перевод: Kolyan Maloy aka Dzhan for tolik-punkoff.com

Источник: NSIS Wiki

NSIS: поиск и завершение (kill) внешнего процесса.

Делается с помощью плагина nsProcess.

Установка плагина

1. Качаем архив
2. Распаковываем содержимое архива по соответствующим каталогам NSIS

NsProcess.zip\Example --> C:\Program Files (x86)\NSIS\Examples\nsProcess
NsProcess.zip\Include --> C:\Program Files (x86)\NSIS\Include\
NsProcess.zip\Plugin\nsProcess.dll --> C:\Program Files (x86)\NSIS\Plugins\x86-ansi\nsProcess.dll
NsProcess.zip\Plugin\nsProcessW.dll --> C:\Program Files (x86)\NSIS\Plugins\x86-unicode\nsProcess.dll

Пример

Начало:

Unicode true
!include LogicLib.nsh

Name "KillProcessExample"
OutFile "KillProcessExample.exe"
ShowInstDetails show
RequestExecutionLevel User

Немного дополнил пример из официального мануала, вставив макрос для расширенного вывода сообщений об ошибках:

!macro PrintProcError ErrCode

Код разлапистый, потому на PasteBin: !macro PrintProcError ErrCode

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

1. Запускаем тестовую программу (calc.exe, которая обычно есть в любой винде и лежит она обычно в $SYSDIR, т.е. в C:\Windows\System32\) и ждем 500 миллисекунд:

DetailPrint "Start calc.exe..."
Exec "$SYSDIR\calc.exe"
Sleep 500

2. Спрашиваем у пользователя, будем ли убивать процесс. Если нет — идем на завершение:

MessageBox MB_YESNO|MB_ICONQUESTION "Kill process calc.exe?" IDYES 0 IDNO "EndProg"

3. Ищем процесс(ы) по имени экзешника:

DetailPrint "Find process calc.exe..."
nsProcess::_FindProcess "calc.exe"

Да, можно хоть 10 калькуляторов назапускать.

4. Вытаскиваем из стека результат функции _FindProcess:

Pop $R0

5. Выводим на псевдоконсоль расшифровку кодов возврата:

!insertmacro PrintProcError $R0

6. Если код возврата = 0, значит, процесс найден, пытаемся его убить, выводим расшифровку кодов возврата, и опять подождем 500 миллисекунд:

${If} $R0 = 0
	DetailPrint "Process calc.exe found. Kill it..."
	nsProcess::_KillProcess "calc.exe"
	!insertmacro PrintProcError $R0
	Sleep 500
${EndIf}

Вывод на экран

Исходник примера

На GitHub

Источники

Официальная справка плагина (на буржуйском)
Перевод от Leha Silent (копия)

Плагин NsProcess

Ссылки

Версия 1.5:
nsProcess.zip (25 KB)

Версия 1.6 (поддержка NSIS UNICODE от brainsucker, переименуйте библиотеку nsProcessW.dll):
nsProcess.zip (14 KB)
— Зеркало: nsProcess_1_6.7z

Обсуждение:
На форуме

Описание

Возможности:

— Поиск процесса по имени.
— Завершение всех процессов с указанным именем (все найденные экземпляры).
— Закрытие всех процессов с указанным именем. Сначала происходит попытка закрыть все окна процессов, ожидание 3 секунд для завершения процессов. Если процесс(ы) все еще активны, используйте функцию _CloseProcess.
— Имя процесса нечувствительно к регистру
— Поддерживаемые ОС: Win95/98/ME/NT/2000/XP/Win7
— Поиск процессов других пользователей при запуске с правами администратора или при переключении на другого пользователя.
— Маленький размер плагина (4 Кб)
— Поддержка Unicode (просто переименуйте nsProcessW.dll в nsProcess.dll)

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

!include "LogicLib.nsh"
 
  Section ""
    StrCpy $1 "YOURAPP.exe"
 
    nsProcess::_FindProcess "$1"
    Pop $R0
    ${If} $R0 = 0
      nsProcess::_KillProcess "$1"
      Pop $R0
 
      Sleep 500
    ${EndIf}
 
  SectionEnd

Обычно _FindProcess возвращает:

0 если процесс найден
603 если процесс(ы) не найдены

// Коды возврата:
  // 0 = успех
  // 601 = Нет разрешения на завершение процесса
  // 602 = Не все процессы завершились успешно
  // 603 = Процесс в данный момент не запущен
  // 604 = Невозможно определить тип ОС
  // 605 = ОС не поддерживается
  // 606 = Невозможно загрузить NTDLL.DLL
  // 607 = Не удалось получить адрес процедуры из NTDLL.DLL
  // 608 = ошибка NtQuerySystemInformation
  // 609 = Невозможно загрузить KERNEL32.DLL
  // 610 = Не удалось получить адрес процедуры из KERNEL32.DLL
  // 611 = Ошибка CreateToolhelp32Snapshot

Благодарности

Ravi Kochhar (исходная функция FIND_PROC_BY_NAME на основе его кода)
iceman_k (Find Process By Name) и DITMan (KillProcDLL Manual).

Перевел Leha Silent специально для tolik-punkoff.com

Источник

Патч на NSIS (с возможностью отмены).

Преамбула

На самом деле, многими реверсерскими командами создана куча генераторов автопатчеров, подсовываешь ему оригинальный и измененный экзешник, и тебе генерируется автопатчер, даже с красивой картинкой и восьмибитной музыкой. Но «есть проблема» (ц) Патриарх Кирилл:

— Не на все автопатчеры хорошо реагируют антивирусы
— Автопатчеры нестандартны, кто в лес, кто по дрова.

Неплохо бы сделать так, чтоб и антивирусы не ругались, и оно хоть как-то относительно стандартно выглядело. И такое решение есть — модуль VPatch для системы установки NSIS. Далее расскажу, как этим модулем воспользоваться, чтобы пропатчить уже установленную программу, а также, немного расширю пример, покажу как в патч «зашить» еще и отмену патча. Естественно, чтобы все повторить, у вас должен быть установлен NSIS хотя бы в минимальной комплектации.

Подготовка #1

Устанавливаем модуль VPatch, лучше воспользоваться ссылкой на установщик, потом скачать родной пример и попытаться его скомпилировать. Если все сработало — модуль установился правильно, можно работать дальше.

Для генерации данных для патча, VPatch использует свои утилиты командной строки, которые в комплекте идут, но устанавливаются криво, так что качаем утилиты отдельно и устанавливаем их (установятся в %WINDIR%).

Скачать установщик здесь

Подготовка #2

Теперь нужна лабораторная крыса, т.е. программа, которую будем патчить, возьмем оригинальный CrackMe из предыдущего примера (копия) и напишем ему стандартный инсталлятор:

Исходник инсталлятора
Готовый инсталлятор тестовой программы
Исходник CarckMe

Устанавливаем ее.

Для патча нужен оригинальный экзешник

И пропатченный вручную

Подробное описание патча

Проверка

Патч:

Восстановление:

Источники

VPatch — Free Patch Generator
VPatch plug-in
Документация и исходники оригинального примера
NSIS: контрольная сумма (MD5), сравнение файлов. (копия)
NSIS: Получение списка файлов с MD5-хешами. (копия)
NSIS: Получение даты и времени. (копия)

Исходник примера

Пример полностью на GitHub
Основной исходник

NSIS: Получение даты и времени.

Преамбула

Иногда бывает нужно получить в скрипте установки локальную дату и время, или дату и время, связанную с файлом (модификации, создания или последнего доступа к файлу). В NSIS это делает одна стандартная функция, точнее, макрос ${GetTime}.

Подключаемые файлы и макрос

Для того, чтобы использовать GetTime, необходимо подключить заголовочный файл FileFunc.nsh (должен идти в стандартной поставке) и сам макрос GetTime:

!include "FileFunc.nsh"
!insertmacro GetTime

Вот начало инсталляционного скрипта:

Unicode true

!include "FileFunc.nsh"
!insertmacro GetTime

Name "GetLocalTime"
OutFile "GetLocalTime.exe"
ShowInstDetails show
RequestExecutionLevel User
;Далее будет тестовая секция

Синтаксис GetTime

${GetTime} "[File]" "[Option]" $var1 $var2 $var3 $var4 $var5 $var6 $var7

где:

"[File]" — файл (параметр игнорируется при использовании опций "L" или "LS")
"[Option]" — опции (см. далее)
$var1 — День
$var2 — Месяц
$var3 — Год
$var4 — День недели (наименование на английском языке)
$var5 — Час
$var6 — Минута
$var7 — Секунда

Опции:

L — локальное время компьютера
A — время последнего доступа к файлу
C — время создания файла
M — время последнего изменения файла
LS — системное время (то же самое, что и L, только используется часовой пояс UTC)
AS — время последнего доступа к файлу (UTC)
CS — время создания файла (UTC)
MS — время последнего изменения файла (UTC)

Пример получения локального времени

Section "Test"
	
     ; Получаем время компьютера
	${GetTime} "" "L" $0 $1 $2 $3 $4 $5 $6
     ; Выводим его в окошко лога инсталлятора
	DetailPrint "Local date and time:"
	DetailPrint "$2/$1/$0 ($3) $4:$5:$6"
     ; Можно преобразовать время в строку
     ; чтоб добавить к имени файла
	DetailPrint "String to add time in filename:"
	StrCpy $0 "$2-$1-$0-$4-$5-$6"
     ; и вывести ее
	DetailPrint "$0"
	
SectionEnd

Результат

Ссылки

Пример на GitHub
how to get the date and time?
E.1.6 GetTime

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

Утилиты для создания автоматического патча на NSIS

Один из пользователей NSIS создал проект VPatch, который позволяет на NSIS написать автоматический патчер (после покажу пример, а может даже парочку). Но устанавливается он как-то криво, консольные утилиты, которыми генерятся данные для будущего патча устанавливаются абы куда, а не в какой-нибудь %PATH%-каталог, что безумно бесит. Поправил. У меня все утилиты устанавливаются в %WINDIR%.

Утилиты отдельно (RAR-архив), 468Кб
Установщик (NSIS, EXE), 666Кб
Родной пример использования VPatch (7ZIP), 475Кб
Исходник моего инсталлятора

Отключение обновления Foxit PDF Reader (v. 8.3.2.25013)

Преамбула

Когда-то писал о том, что в новых версиях Foxit Reader пропал PDF-принтер и Foxit PDF Creator (копия). Так вот, новая гадость пришла откуда не ждали, теперь он сносит PDF-принтер при обновлениях.

Штатного способа отключить обновления нет

Решение

Сама программа обновления имеется в двух экземплярах:

В каталоге Foxit:
C:\Program Files (x86)\Foxit Software\Foxit Reader\FoxitUpdater.exe

И в каталоге Application Data:
C:\Users\<username>\Application Data\Foxit Software\Addon\Foxit Reader\FoxitReaderUpdater.exe, вместо <username> — ваше имя пользователя.

Просто снести два экзешника не получится, точнее получится, но Foxit Reader будет при каждом запуске ругаться, что не нашел свой обновлятор. Зато не будет ругаться, если обновлятор (оба файла) просто заменить на экзешник, который ничего не делает, а такой я уже делал (копия), правда по другому поводу. Сделал еще и версию на MASM

Написал патч на NSIS

И для себя, если винды переустанавливать, и для клиентов, на всякий случай.

Репозиторий на GitHub
Исходник NSIS
Копия (без дополнительных файлов) на PasteBin
Скачать готовый патч

Дополнительно

В принципе, исходник достаточно простой, оставлю только ссылку на получение MD5-суммы файла в NSIS: NSIS: контрольная сумма (MD5), сравнение файлов. (копия).

Примечание: почему-то MD5-сумма, получаемая NSIS-плагином (md5dll.dll) отличается от MD5-суммы, полученной средствами Windows (копия). Почему, пока не разобрался. Может, позже напишу.

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

Преамбула

В принципе, патч или кряк для программы можно сделать с помощью NSIS, конечно, это будет не классический патч, т.е., когда меняешь в исходном экзешнике несколько байт, а архив с готовым экзешником и сценарием установки. Но зато и возможностей больше, можно Реестр или INI-файлы подправить, например.

Но одна из главных задач любого патча, перед началом работы проверить, а не пропатчен ли файл уже. Делается это обычно так — берется контрольная сумма MD5 или CRC32 оригинального файла и сравнивается с заранее известной суммой. Если совпадает — файл наш, можно патчить. Можно и наоборот — сравнить контрольную сумму оригинального файла с известной контрольной суммой пропатченного, если они равны — файл уже модифицирован, сообщаем пользователю и завершаем работу.

Получение MD5-суммы в NSIS.

Для получения MD5 в NSIS имеется плагин MD5 Plugin.

Скачиваем архив, копируем md5dll.dll из директории ANSI архива в директорию ANSI-плагинов NSIS (у меня C:\Program Files (x86)\NSIS\Plugins\x86-ansi\), а из директории UNICODE архива в директорию UNICODE-плагинов (C:\Program Files (x86)\NSIS\Plugins\x86-unicode). Понятно, что пути к вашим каталогам плагинов могут отличаться, т.к. зависят от того, куда установлен NSIS/

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

Вычисление контрольной суммы строки:

#generate MD5sum of string
md5dll::GetMD5String "TestString"
Pop $0
DetailPrint "MD5 (string): [$0]"

Вывод:
MD5 (string): [5b56f40f8828701f97fa4511ddcd25fb]

Получение случайного хэша MD5:

#generate random MD5sum
md5dll::GetMD5Random
Pop $0
DetailPrint "MD5 (random): [$0]"

Вывод:

MD5 (random): [864fd151525efad56947f25fa3daeb75]

Получение контрольной суммы файла:

#generate MD5sum of file
DetailPrint "MD5 sum of file $EXEDIR\test\1.exe"
md5dll::GetMD5File "$EXEDIR\test\1.exe"
Pop $0
DetailPrint "MD5 (file): [$0]"

Вывод:

MD5 sum of file C:\!\Installers\MD5Sample\test\1.exe
MD5 (file): [03f3ba055d9f325c9abc90181f82657b]

Сравнение файлов

Для более красивого кода сравнения, рекомендую подключить инклюд с макросами логических операторов LogicLib.nsh (есть в комплекте NSIS):

!include LogicLib.nsh

Пример:

md5dll::GetMD5File "$EXEDIR\test\1.exe"
Pop $0
md5dll::GetMD5File "$EXEDIR\test\3.exe"
Pop $1
DetailPrint "Files $EXEDIR\test\1.exe"
DetailPrint "and   $EXEDIR\test\3.exe"
DetailPrint "MD5: $0" 
DetailPrint "and   $1"
${If} $0 == $1
	DetailPrint "IS IDENTICAL!"
${Else}
	DetailPrint "IS DIFFERENT!"
${EndIf}

Пример вывода (сравниваются два одинаковых файла, а потом два разных):

Files C:\!\Installers\MD5Sample\test\1.exe
and C:\!\Installers\MD5Sample\test\2.exe
MD5: 03f3ba055d9f325c9abc90181f82657b
and 03f3ba055d9f325c9abc90181f82657b
IS IDENTICAL!
Files C:\!\Installers\MD5Sample\test\1.exe
and C:\!\Installers\MD5Sample\test\3.exe
MD5: 03f3ba055d9f325c9abc90181f82657b
and 7baeee10b3cd6c210c4fcd61b92b8e1e
IS DIFFERENT!

Ошибки при работе библиотеки MD5

Если файл отсутствует, то функция md5dll::GetMD5File может выдать непредсказуемый результат, строку случайных символов, поэтому, перед использованием md5dll::GetMD5File следует проверять наличие и доступность файлов.

Ссылки

Библиотека:
— Описание на официальном сайте: MD5 plugin
Скачать с официального сайта
Зеркало
Копия (v 0.5) на Mega.nz

Пример из заметки целиком на GitHub

NSIS: Сохранение лога установки в текстовый файл.

На самом деле, не совсем сохранение лога, если быть точным — сохранение текста из окна Details установщика.

1. Необходимо подключить dumplog.nsi (код взят из NSIS Wiki)
2. Вызов функции DumpLog:

StrCpy $0 "$TEMP\test.log" ;Записываем имя файла лога в переменную
Push $0 ;помещаем значение переменной в стек
Call DumpLog ;вызов функции

Примечание: Функция работать не будет, если инсталлятор запущен в тихом режиме.

С русским языком проблем тоже не возникает. Файл сохраняется в формате ANSI.

Источник

NSIS Wiki

Тестовый установщик

Код на GitHub
dumplog.nsi

NSIS. Проверка существования ключа Реестра.

Иногда бывает надо проверить, существует тот или иной ключ Реестра Windows

Решение

Для этого можно воспользоваться стандартной функцией EnumRegKey:

EnumRegKey <var> <root_key> <subkey> <index>

где:
<var> — переменная для названия ключа Реестра, которое будет возвращено функцией.
<root_key> — корневой ключ Реестра (HKCR|HKCU|HKLM|HKU|HKCC)
<subkey> — путь в Реестре, например Software\Microsoft\Windows
<index> — индекс следующего ключа.

Функция перебирает все подключи в указанной ветке Реестра. Пример для перечисления можно посмотреть здесь

Если ключ не существует, то функция EnumRegKey сгенерирует ошибку, которую можно отследить:

!include Logiclib.nsh ;подключаем библиотеку с нормальными логическими конструкциями.
;...
ClearErrors ;очищаем флаг ошибки
EnumRegKey $0 HKCU "Software\Microsoft\" 0
${If} ${Errors} ;ключ не существует
	DetailPrint "HKEY_CURRENT_USER\Software\Microsoft not EXIST!"
${Else} ;ключ существует
	DetailPrint "HKEY_CURRENT_USER\Software\Microsoft EXIST!"
${EndIf}
;...

Пример целиком на GitHub

NSIS: Создание точки восстановления системы.

Преамбула

В некоторых случаях, например, при установке драйверов, необходимых для работы вашей программы, или при внесении серьезных изменений в систему, хотелось бы задействовать механизм создания точек восстановления, чтобы в случае непредвиденной ситуации, пользователь мог бы безболезненно откатить все назад.
Как, например, поступает Driver Pack Solutions перед установкой драйверов.

Плагин SysRestore

Для создания точек восстановления в NSIS применяется плагин SysRestore, который можно скачать с официальной NSIS wiki (копия).

Установка тривиальна — надо распаковать архив в каталог NSIS, например в "C:\Program Files (x86)\NSIS\"

После подключения плагина становятся доступны 4 функции:

SysRestore::StartInstallPoint "<название>" — функция создает точку восстановления с типом «Установка», применяется для создания точки восстановления при установке программы.

SysRestore::StartUninstallPoint "<название>" — функция создает точку восстановления с типом «Удаление», применяется для создания точки восстановления при удалении программы.

В стек эти функции (как и другие функции данного модуля) помещают код возврата, который можно извлечь из стека командой

Pop <переменная>

И далее проанализировать:

0 — Функция завершена успешно, точка восстановления создана.
1 — Уже запущено создание точки восстановления, предыдущую точку восстановления надо закрыть или отменить (см. ниже).
10 — Windows запущена в «Безопасном режиме»
13 — The sequence number is invalid (сам автор плагина не знает, что это за ошибка).
80 — Ошибка с таким кодом встречается в Windows ME.
112 — Восстановление системы находится в режиме ожидания, поскольку закончилось место на жестком диске под точки восстановления.
1058 — Восстановление системы отключено.
1359 — Внутренняя ошибка при создании точки восстановления.
1460 — Время ожидания вызова истекло из-за ожидания мьютекса для установки точек восстановления (вот это уже я не знаю, что такое).

Впрочем, про коды ошибок, автор пишет следующее:

Я не знаю, являются ли эти коды актуальными кодами ошибок, я просто проанализировал соответствующие include-файлы в SDK. Я тестировал код 1058 — восстановление системы отключено, и провел тест в безопасном режиме — код ошибки 10, я предполагаю, что эти коды верны.

Так что возможно ограничиться анализом кодов 0 (ОК), 1 (см. ниже), 10 (безопасный режим) и 1058 (восстановление системы отключено). Если функция выдаст что-то кроме этих четырех понятных кодов, остальное можно (в первом приближении) отправить в великий класс Неизвестная Ошибка (Unknown Error). Хотя, если вы знаете больше, чем автор плагина, а тем более, чем я — добро пожаловать в комментарии.

Кроме вышеперечисленных инициализирующих функций, существует «закрывающая» и «отменяющая» функции. «Закрывающая»:

SysRestore::FinishRestorePoint

Ее необходимо вызывать, после того, как все изменения в системе произведены, чтобы закрыть точку восстановления, и чтобы система получила нужные данные о том, что было изменено в системных/программных файлах и Реестре. Попытка создать новую точку восстановления без закрытия предыдущей, в одном и том же инсталляторе, приводит к тому что функции StartInstallPoint/StartUninstallPoint выдадут ошибку 1.

Как я понимаю, система все равно закроет restore point после завершения работы установщика, но лучше эту функцию использовать.

«Отменяющая» функция, это:

SysRestore::RemoveRestorePoint

Она позволяет удалить незакрытую точку восстановления из системы. Может понадобиться в случае, если пользователь отменил установку в процессе, и установщик откатил процесс установки.

Коды возврата последних двух функций:

0 — OK
2 — Не произведен запуск точки восстановления (т.е. предварительно не вызваны функции StartInstallPoint или StartUninstallPoint)

Пример кода

В первой секции… Вообще, для этого можно создать скрытую секцию, чтоб сначала создать restore point, а потом с ней работать далее, но пример простой, так что в первой секции, до начала изменений, создаем точку восстановления

Section "Create Restore Point Example"
	;Настраиваем секцию
	;Создаем точку восстановления
	DetailPrint "Create restore point..."
	SysRestore::StartRestorePoint "Test Restore Point"
	Pop $0 ;достаем из стека код восстановления в переменную $0
	${If} $0 = 0 ;анализируем его
		DetailPrint "OK"
	${ElseIf} $0 = 1
		DetailPrint "Start point already set (start function only)."
	${ElseIf} $0 = 10
		DetailPrint "The system is running in safe mode. "
	${ElseIf} $0 = 13
		DetailPrint "The sequence number is invalid."
	${ElseIf} $0 = 80
		DetailPrint "Windows Me code."
	${ElseIf} $0 = 112
		DetailPrint "System Restore is in standby mode because disk space is low."
	${ElseIf} $0 = 1058
		DetailPrint "System Restore is disabled."
	${ElseIf} $0 = 1359
		DetailPrint "An internal error with system restore occurred."
	${ElseIf} $0 = 1460
		DetailPrint "The call timed out due to a wait on a mutex for setting restore points."
	${Else}
		DetailPrint "Unknow error."
	${EndIf}
	
	;производим установку программы, копируем файлы, пишем в Реестр
	;...
SectionEnd

;в завершающей секции

Section -FinishSection
;...
; Генерируем uninstaller, пишем соотв. ключи в Реестр
;...	
	;Закрываем точку восстановления
	SysRestore::FinishRestorePoint
	DetailPrint "Finish restore point..."
	${If} $0 = 0
		DetailPrint "OK"
	${ElseIf} $0 = 2
		DetailPrint "No Start point set (finish function only)."
	${Else}
		DetailPrint "Unknow error."
	${EndIf}

SectionEnd

Готовый пример

Тестовый установщик создает точку восстановления, далее устанавливает во временный каталог системы программу, которая при запуске показывает приветственное окно и завершает работу, прописывает ее в автозагрузку Реестра, и генерирует uninstaller во временном каталоге.

Код на GitHub
Скомпилированный инсталлятор

Скачать плагин

1. NSIS Wiki
2. Копия (на 07.11.2020)

NSIS warning 7998: ANSI targets are deprecated, как исправить.

Преамбула

Обновил NSIS с v 3.04 до 3.06.1, в логе компилятора стал вылезать warning:

Processed 1 file, writing output (x86-ansi):
warning 7998: ANSI targets are deprecated
...
warning:
  7998: ANSI targets are deprecated

Т.е. ANSI ему устарело. Warning не error, но все равно неприятно. Впрочем, исправить не составило особого труда.

Исправление

Для исправления этого дела, в начало скрипта надо добавить команду:

Unicode true

Только команду надо добавлять в самое начало скрипта, во всяком случае до секций и до определения параметра InstallDir. Если добавить после InstallDir:

;...
Name "${APPNAMEANDVERSION}"
InstallDir "$TEMP"
Unicode true

OutFile "TempUnicode.exe"
;...

То это вызовет ошибку:

Error: Can't change target charset after data already got compressed or header already changed!

Невозможно изменить целевую кодировку после того, как данные уже сжаты или заголовок изменен!

А лучше добавлять Unicode true вообще в самое начало скрипта, первой строчкой, даже до !include, как пишут на форуме NSIS, т.к. от чего компилятор так реагирует на смену кодировки, в документации не описано.

Примечание: с русским языком в инсталляторе проблем при смене кодировки не случилось, хотя редактор передает компилятору скрипт на STDIN, а редактор сохраняет файл скрипта в ANSI (кодировка Win 1251). Во всяком случае, проблем не случилось при использовании Venis IX.

NSIS: Отслеживание кода возврата исполняемого приложения.

Преамбула

В процессе установки иногда приходится вызывать внешние приложения, например, какую-нибудь утилиту для настройки системы, и так далее.

При вызове внешней программы надо отследить ее работу, завершена ли она корректно, или же в процессе выполнения произошла ошибка. Могу привести довольно древний пример из своего опыта, когда-то программы распространялись на CD, и производитель материнской платы давал диск с «бонусным» софтом. Но в инсталлятор была встроена программа, определявшая модель материнской платы — если модель не совпадала, установщик «халявных» программ отказывался что-либо устанавливать. Когда я распаковал установщик и проанализировал это дело, выяснилось, что установщик сначала распаковывал и запускал внешнюю утилиту, которая проверяла модель мать-доски. И возвращала код выхода: 0 — правильная мать-доска, 1 — неправильная.

Вызов внешнего приложения и получение его кода возврата из NSIS-установщика

Вызов внешнего приложения выполняется командой

ExecWait "путь_и_имя_файла параметры_программы" [переменная_для_кода_возврата]

Например:

ExecWait "$TEMP\retcode.exe 42"

Второй параметр — переменная для сохранения кода возврата вызванной программы, т.е. если мы хотим вызывать программу и анализировать ее код завершения, надо действовать так:

;...
ExecWait "$TEMP\retcode.exe 42" $0

Далее анализируем содержимое переменной $0.

Пример

Пример инсталлятора на GitHub

Источник

Reference/ExecWait

Преобразование REG-файла в код скрипта инсталлятора NSIS

Понадобилось тут добавить ключи из REG-файла в код своего инсталлятора на NSIS. Оказывается, все придумано до нас. Имеется утилита Reg2Nsis, которая этим и занимается.

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

reg2nsis REG-файл [ПАРАМЕТРЫ]
или
reg2nsis ключ_реестра [ПАРАМЕТРЫ]

Т.е. утилита может преобразовать в код NSIS, как содержимое REG-файла, так и данные непосредственно из реестра.

Параметры:
-r — обработать ключ реестра со всеми подключами, начиная с указанного. Срабатывает только если в качестве источника указан путь в реестре.
-o <файл> — код будет сохранен в указанный файл
Если вместо -o <файл> указать ключ -O, то код будет сохранен в файл с именем, аналогичным указанному REG-файлу, но с расширением .nsh

Без дополнительных параметров, код NSIS будет выведен на консоль (stdout)

Программа смотрит в настройки системы и автоматически преобразует стандартные системные пути в стандартные переменные NSIS. Например, встретившийся путь C:\Windows\ будет преобразован в $WINDIR\.

Это можно отключить, добавив в командной строке ключ -s.

При анализе REG-файла, программа распознает как добавление ключа/значения, что понятно, так и команду на его удаление.
Т.е. запись в REG-файле:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\oem-drv64]

будет преобразована в:

WriteRegStr HKEY_LOCAL_MACHINE "SYSTEM\CurrentControlSet\services\oem-drv64" "" ""
А запись:

[-HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\oem-drv64]

преобразуется в следующий код:

DeleteRegKey HKEY_LOCAL_MACHINE "SYSTEM\CurrentControlSet\services\oem-drv64"

Скачать

С официальной NSIS-wiki
Копия на mega.nz

NSIS: продолжение установки после перезагрузки.

Преамбула

Например, у нас есть софтина, которая в процессе своей установки требует перезагрузку, после чего доустанавливается. Например, софт устанавливает свой драйвер, обновление, или же регистрирует системный сервис. Как реализовать инсталлятор для такой программы?

Изначально я хотел обойтись параметрами командной строки, добавляемыми при запуске установщика, но с ними произошла жопа, поэтому будем действовать более надежными методами.

Алгоритм

0. Проверяем наличие уникального ключа Реестра, сигнализирующего о том, что надо выполнить вторую часть установки (после перезагрузки):

DetailPrint "Read After Reboot flag..."
ReadRegStr $R0 HKCU "Software\Microsoft\Windows\CurrentVersion\"
"${APPNAME}_afterreboot"
StrCmp "$R0" "1" AfterReboot

1. Производим все действия, которые необходимо произвести до перезагрузки (устанавливаем драйвера, регистрируем DLL и т.д.)
2. Записываем в однократную автозагрузку (ключ Реестра RunOnce) наш инсталлятор, путь к exe файлу инсталлятора, который можно взять из переменной $EXEPATH.
Автозагрузку прописываем для текущего пользователя (пользователя, запустившего установщик), т.е. в ключ HKEY_CURRENT_USER.

WriteRegStr HKCU
"Software\Microsoft\Windows\CurrentVersion\RunOnce"
"${APPNAME}" "$EXEPATH"

Примечание: В RunOnce могут записаться программы, которые требуется выполнить однократно при загрузке системы. После выполнения программ, ключ RunOnce очищается автоматически, что отличает его от ключа Run, где данные не очищаются, и его можно использовать, как постоянную автозагрузку.

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

DetailPrint "Write After Reboot flag..."
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\"
"${APPNAME}_afterreboot" "1"

4. Выдаем запрос на немедленную перезагрузку:

DetailPrint "Reboot request."
MessageBox MB_YESNO|MB_ICONINFORMATION
"Installation will be cobntinue after reboot. Press OK to reboot now." IDYES RebootID

Если пользователь ответил «Yes«, сразу перезагружаемся:

	RebootID:
		DetailPrint "Rebooting..."
		Reboot

Если нет, ждем перезагрузки вручную:

DetailPrint "Installation continue if user restart system."
Goto SecEnd

5. После перезагрузки удаляем флаг (проверив его наличие, см выше):
DetailPrint "Delete After Reboot flag..."
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\"
"${APPNAME}_afterreboot"

6. И продолжаем установку…
DetailPrint "Continue install after reboot..."

Скриншоты



Исходник примера

На GitHub

Глюк в NSIS: проверка параметров командной строки инсталлятора не работает

Вот серьезно, бьюсь (головой об стену) уже третий день, ну не работает функция GetOptions, так, как заявлено в NSIS Wiki, уже и по колесам стучал, и фары протирал, в смысле просто тупо копировал код из примера в тестовый инсталлятор, и еще десяток написал, перепробовав все что можно и нельзя, ну не выходит каменный цветок, хоть тресни. Функция в любом случае выдает, либо что никаких ошибок не случилось, для следующего кода, соответственно, всегда получается found:

Section
	${GetOptions} "/S /T" "/T"  $R0
 
	IfErrors 0 +2
	MessageBox MB_OK "Not found" IDOK +2
	MessageBox MB_OK "Found"
SectionEnd

Либо, для следующего кода всегда остается значение по умолчанию, даже если в командной строке явно задано иное:

Section
	${GetOptions} "/AR=N" "/AR=" $R0
	DetailPrint "R0: $R0"
	
SectionEnd

Например, выполняешь TestInstaller.exe /AR=Y, все равно в переменной $R0 оказывается N

Такие дела.

NSIS: Совсем коротко про секции

Преамбула

Секции — это удобный механизм, позволяющий обеспечить пользователю выбор компонентов программы, если такие присутствуют. В каждой секции можно создавать свои ярлыки, распаковывать файлы, и производить любые другие действия, практически независимо от других секций. Естественно, значения переменных, измененные в одной секции, в других тоже будут изменены, потому что в NSIS все переменные глобальные.

Отображение секций и выбор компонентов

Для самого простого выбора компонентов в NSIS, необходимо вставить в скрипт, перед, собственно, описанием секции, следующий код:

ComponentText "Какой-то текст", например:

ComponentText "Choose which features of ${APPNAMEANDVERSION} you want to install."

Естественно, ${APPNAMEANDVERSION} должна быть заранее определена:

!define APPNAME "SectionsExample"
!define APPNAMEANDVERSION " SectionsExample 0.1"


Окно инсталлятора с выбором компонентов

Простая секция

В любом NSIS-скрипте должна быть создана хотя бы одна секция, иначе компилятор будет ругаться, и файл установщика не создаст. В самом простом случае, когда не требуется выбор компонентов, создается единственная секция без названия:

Section
	DetailPrint "Hello, world!"	
SectionEnd


Примечание: После конструкции Section идет имя секции, заключенное в кавычки. Если кавычки пропустить, то имя секции будет некорректным, и неверно отобразится в окне выбора компонентов. Попробуйте вставить следующий код в пример, и посмотрите, как он будет работать. Ссылка на пример в конце заметки.

Section Component 4

	; Wrong section description
	SetOverwrite on
	
	DetailPrint "Component #4 installing..."
	
SectionEnd

Основные компоненты. Блокируем возможность снятия checkbox’а (галочки) пользователем

Для чего? Например, у нас есть программа, у которой, в свою очередь есть основные компоненты, например экзешник и DLL, а также дополнительные, которые можно устанавливать, а можно и не устанавливать. И если уж пользователь хочет поставить наше приложение, то он должен установить хотя бы основные компоненты.

Для того, чтобы заблокировать чекбокс, прописываем в начале секции конструкцию:

SectionIn RO

Section "Main Program and components"

	; Checkbox select and disable user changes
	SectionIn RO
	SetOverwrite on
	
	DetailPrint "Main program installing..."
	
SectionEnd

Чекбокс (галочка) выбранный по умолчанию

Для этого ничего делать не надо, только определить секцию с именем в кавычках и дефольными настройками. Пользователь может деактивировать checkbox (снять галочку) в окне выбора компонентов.

Section "Component #2"

	; Checkbox enabled by default & user will change it state
	SetOverwrite on
	
	DetailPrint "Component #2 installing..."
	
SectionEnd

Сделать, чтобы галочка была снята по умолчанию

Наверное, самый частый вопрос, задаваемый, что в рунете, что в Интернете вообще, по поводу NSIS. Так вот, ларчик открывается просто. Достаточно после Section поставить специальный флаг /o.

Пользователь также сможет активировать checkbox (галочку), если ему нужно будет установить данный компонент.

Section /o "Component #3"

	; Checkbox disabled by default & user will change it state
	SetOverwrite on
	
	DetailPrint "Component #3 installing..."
	
SectionEnd

Скрытые секции

Такие секции не будут отображены в окне выбора компонентов. Их названия пишутся без кавычек и без пробела, а перед названием ставится знак «-«. Код в таких секциях будет выполнен в любом случае, независимо от желания пользователя:

Section -HiddenSection
	DetailPrint "This is a hidden section"
	DetailPrint "This code running anyway."
SectionEnd

Специальные секции

К ним, наверное, относится одна — Uninstall, ну и еще не совсем секция, а встроенная функция, которую можно переопределить, дописав свой код, т.е. что-то типа обработчика событий в «больших» языках, например в C#

О секции Uninstall и рассказывать особо не надо, кроме ремарки, что в секции Uninstall «забываются» все значения глобальных переменных и значение переменной $INSTDIR

Исходник примера с секциями

На GitHub

Глюк (недоработка) uninstaller’а NSIS.

Или как поместить uninstaller не в директории программы.

Преамбула

Конечно, возникает вопрос «зачем?». Ну, например, мы устанавливаем некое системное приложение, драйвер, или хотим что-то пропатчить. В качестве каталога по умолчанию для такого приложения может служить, например, директория $WINDIR (обычно C:\Windows). Если мы хотим безболезненно удалить такое приложение, то нам нужно создать анинсталлер, но, совсем не обязательно чтоб он хранился в том же каталоге, что и установленное приложение. Зачем, например, загаживать каталог C:\Windows файлами uninstall.exe. Да в том то все и дело, что незачем, и вот тут мы сталкиваемся с глюками анинсталлера.

Баг uninstaller’а

В анинстайлере и инсталляторе переменная $INSTDIR определяется по-разному. В секции установки $INSTDIR можно задать, в т.ч. и автоматически, а в uninstaller’е она определяется, как директория, из которой запустился анинсталлер. Это довольно легко проиллюстрировать простым кодом, который на самом деле ничего не устанавливает, а просто предлагает пользователю выбрать каталог для установки, а сам выводит пути к каталогам и создает тестовый анинсталлер во временном каталоге. Анинсталлер тоже ничего не удаляет, просто выводит значения переменных, заданных в основном скрипте.

Любые пользовательские переменные при работе uninstall так же «забываются», вне зависимости от того, были ли они определены в секции, или в скрипте вообще.

Фрагмент кода из установщика:

; явно задаем директорию для установки
InstallDir "$PROGRAMFILES\TestUninst" 
;создаем тестовую переменную
Var TESTVAR
;[..]
;выводим окно для выбора директории для установки
DirText "Choose the folder in which to install ${APPNAME}."

Section "Inst"

	; Set Section properties
	SetOverwrite on
	
	StrCpy $TESTVAR "Test variable value"

	DetailPrint "INSTDIR (install): $INSTDIR"
	DetailPrint "TESTVAR (install): $TESTVAR"
	WriteUninstaller "$TEMP\TestUninst.exe"

SectionEnd

Секция установки:

1. Заполняем $TESTVAR
2. Выходим значение $TESTVAR
3. Выводим значение $INSTDIR
4. Создаем Uninstall’ер, причем не в $INSTDIR


В секции удаления выводим значения $INSTDIR и $TESTVAR, анинсталлер запускаем из временного каталога.

Section Uninstall
	
	SetDetailsView show
	DetailPrint "INSTDIR (uninstall): $INSTDIR"
	DetailPrint "TESTVAR (uninstall): $TESTVAR"
	
SectionEnd

Значение $INSTDIR изменилось на временный каталог, а значение $TESTVAR было потеряно. Если переместить анинсталлер в другой каталог, значение $INSTDIR опять поменяется.

Исходник, иллюстрирующий баг

Размещение uninstall в не в $INSTDIR

Из вышеизложенного напрашивается вывод, что значения $INSTDIR и нужных переменных надо сохранять на этапе установки, например в Реестр или INI-файл, а на этапе удаления восстанавливать.

В следующем примере я буду сохранять нужные значения в INI-файл, который будет располагаться в директории с uninstall.exe, а сама программа будет устанавливаться в другой каталог. Сгенерирую простой скрипт мастером скриптов и немного подправлю.

1. Меняем InstallDir на "$TEMP\TestApp"
InstallDir "$TEMP\TestApp"

2. Заводим переменную $UNINSTDIR
Var UNINSTDIR

3. В секции установки задаем ее значение, скажем "$PROGRAMFILES\TestApp"
StrCpy $UNINSTDIR "$PROGRAMFILES\TestApp"

4. Создаем каталог $UNINSTDIR
CreateDirectory "$UNINSTDIR"

5. Меняем где надо пути в создаваемых ярлыках
CreateShortCut "$SMPROGRAMS\TestApp\Uninstall.lnk" "$UNINSTDIR\uninstall.exe"

6. …путь для записи uninstall.exe
WriteUninstaller "$UNINSTDIR\uninstall.exe"

7. …и запись в Реестре для «Программ и компонентов»
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$UNINSTDIR\uninstall.exe"

8. После создания uninstall.exe в каталоге $UNINSTDIR необходимо создать INI-файл, в который сохранить пути к каталогам $UNINSTDIR и $INSTDIR

WriteINIStr "$UNINSTDIR\dirs.ini" "dirs" "I" "$INSTDIR"
WriteINIStr "$UNINSTDIR\dirs.ini" "dirs" "U" "$UNINSTDIR"

На этом с секцией установки закончили.

В секции удаления:

1. Читаем значения из INI-файла (который для uninstall.exe будет располагаться в каталоге из переменной $INSTDIR) в перезаписываемые переменные $0 и $1. Если чтение не удалось, необходимо будет обработать ошибку (см. описания функций ReadINIStr и конструкции IfErrors в справочнике копия).

ReadINIStr $0 "$INSTDIR\dirs.ini" "dirs" "I"
IfErrors Dupa 0
ReadINIStr $1 "$INSTDIR\dirs.ini" "dirs" "U"
IfErrors Dupa 0

2. Восстанавливаем значения $INSTDIR и $UNINSTDIR:

StrCpy $INSTDIR $0
StrCpy $UNINSTDIR $1

3. Удаляем uninstall.exe, INI-файл и каталог анинсталлера:

Delete "$UNINSTDIR\uninstall.exe"
Delete "$UNINSTDIR\dirs.ini"
RMDir "$UNINSTDIR\"

4. Удаляем компоненты программы и ярлыки.

5. В конце секции заводим метку OK:

6. После удаления программы переходим в конец секции
Goto OK

7. Перед меткой OK: заводим метку Dupa:, куда попадем если произошла ошибка чтения INI-файла. После метки Dupa: будет обработчик ошибки. В данном случае выводим пользователю сообщение о том, что INI-файл некорректный, и произвести автоматическое удаление программы нельзя.

Dupa:
	MessageBox MB_ICONSTOP|MB_OK "Error reading
        $INSTDIR\dirs.ini! Uninstall aborted!"

Исходники

1. Исходник, иллюстрирующий баг uninstaller’а
2. Исходник примера

Справочник по NSIS с OSZone

Не сильно хороший, но пусть будет.

Примечание: Если открываешь CHM-файл а страницы пустые, нажимаем на файле правой кнопкой мыши, заходим в свойства и разблокируем его (там кнопка будет)

Скачать с Mega.nz
На OSZone