Skip to content

Flow, errors and messages

NSIS’s Goto-and-label instructions have no equivalent. IfErrors, IfFileExists and the rest are predicates — they read as questions inside an ordinary if, and the compiler writes the labels.

NSIS Installua
Goto if, while, for, break, continue() and return
Return return
Quit os.exit()

os.exit() in onInit ends the process with exit code 2, NSIS’s “aborted by the script”, before any page is shown. An onInit that quits because there is nothing left to do, or because it has answered a /help switch, calls setErrorLevel(0) first.

installer {
onInit(function()
if fileExists(INSTDIR .. "/app.exe") then
setErrorLevel(0)
os.exit()
end
end),
}

Skips to the next iteration of the innermost loop — the one jump you do write, because Lua has no continue and what 5.4 added to write the idiom is goto, which is rejected. It wears a call’s syntax and takes no arguments; it is a jump, so anything after it in the same block is dead.

Usage continue() → nothing

local handle = fileOpen(INSTDIR .. "/app.ini", "r")
for line in lines(handle) do
if line == "" then
continue()
end
detailPrint(line)
end
handle:close()

Every run-time loop carries a target: while, the numeric for, for … in lines(…) and a walker’s for. A for … in glob(…) or ipairs(…) does not — that loop is unrolled at build time, so it is not a loop continue() can see. With no target at all it is an error.

Stops the current section or callback. In a page callback it keeps the user on the page; in .onInit it cancels the install.

Usage abort([message]) → nothing

abort("stopped")

Adds a line to the details view — the log the “Show details” button reveals. The only logging a stock makensis has.

Usage detailPrint(message) · setDetailsPrint("none" | "listonly" | "textonly" | "both" | "lastused")

detailPrint("installing")

The error flag, which most instructions set on failure instead of raising. It is sticky: clear it immediately before the thing you mean to test.

Usage errors() → boolean · clearErrors() · setErrors()

clearErrors()
writeReg(HKLM, "Software/Example", "Path", INSTDIR)
if errors() then detailPrint("could not write the key") end

The exit code the installer’s process returns.

Usage setErrorLevel(n) · getErrorLevel() → int

True when the path exists on the target at install time. Wildcards allowed; a directory matches.

Usage fileExists(filename) → boolean

if fileExists(INSTDIR .. "/app.exe") then detailPrint("present") end

True when the installer was started with /S. A silent install must not open a dialog, which is what this is for.

Usage silent() → boolean · setSilent("silent" | "normal")

NSIS Installua
IfAbort aborted() → boolean
GetInstDirError getInstDirError() → int

Shows a dialog and returns the button the user pressed, by name. Written with a table rather than positionally, because the flags NSIS fuses into one argument are separate decisions.

Usage messageBox { text = …, buttons = …, icon = …, defaultAnswer = …, silentAnswer = … } → string

local answer = messageBox {
text = "Remove Example and all of its files?",
buttons = "YESNO",
icon = "QUESTION",
defaultAnswer = "NO",
silentAnswer = "NO",
}
if answer == "NO" then
os.exit()
end

The answers are the button names in capitals — OK, CANCEL, YES, NO, RETRY, ABORT, IGNORE — and comparing one against a string the dialog cannot give is a warning, not a dead branch that builds. answer ~= "yes" is the one to watch: == is StrCmpS, so the case is part of the value.

buttons is one of OK, OKCANCEL, YESNO, RETRYCANCEL, ABORTRETRYIGNORE and YESNOCANCEL. defaultAnswer focuses that button when the dialog opens (MB_DEFBUTTON2 and so on); both it and silentAnswer must be an answer the set can give.

Usage setAutoClose(true) → nothing