Skip to content

MUI2

The MUI2 part of the command reference: the nine pages, the settings on each, and the block-level settings that govern all of them.

Installua targets MUI2 and nothing else. It writes every !insertmacro and !define itself, in the order MUI2 requires — include order is never yours to get right, which is the single largest source of broken NSIS scripts.

Coverage. All 255 MUI2 names are accounted for: a setting below, one of the 17 rejected, or one of the 108 that are MUI2’s own internal state. Nothing is pending.

Everything else — attributes, sections, files, the registry, page.custom and its controls — is in the command reference.


A page is a member of page, listed in the block it belongs to, in the order it should appear.

The nine pages: welcome, license, components, directory, startMenu, instFiles, finish, confirm, installMode — plus custom. confirm is uninstaller-only, startMenu and installMode installer-only; the rest work in both the installer and the uninstaller, depending on which block lists them.

A page can also be bound to a local and listed by name, which lets it live in an included file: local details = page.custom { … } and return { details = details } there, then local pages = include "…" and pages.details, in the block.

installer {
page.welcome { title = "Welcome to Example" },
page.license { file = "LICENSE.txt" },
page.components {},
page.directory {},
page.instFiles {},
page.finish { run = INSTDIR .. "/Example.exe" },
}
MUI2 Installua
MUI_PAGE_HEADER_TEXT headerText
MUI_PAGE_HEADER_SUBTEXT headerSubText
MUI_PAGE_CUSTOMFUNCTION_PRE pre = function() … end
MUI_PAGE_CUSTOMFUNCTION_SHOW show = function() … end
MUI_PAGE_CUSTOMFUNCTION_LEAVE leave = function() … end
MUI_PAGE_CUSTOMFUNCTION_DESTROYED destroyed = function() … end (welcome, finish and installMode)
SubCaption / UninstallSubCaption subCaption

Every string a page takes may also name a global, or concatenate one with strings: MUI2 writes each of them into an instruction, so it is expanded when the page is shown. page.finish { readme = { path = "", text = "Visit " .. site } } shows whatever .onInit put in site. The one exception is the Start Menu page’s registry root, which is a keyword.

Usage page.welcome { title = …, text = …, pre = …, show = …, leave = …, destroyed = … }

title and text also take a table: title = { text = …, lines = 3 } is MUI_WELCOMEPAGE_TITLE_3LINES.

page.welcome {
title = { text = "Welcome to Example", lines = 3 },
text = "This wizard will install Example on your computer.",
}

Usage page.license { file = …, topText = …, bottomText = …, button = …, checkbox = …, radioButtons = { … } }

file is LicenseData, bottomText is LicenseText, and checkbox is LicenseForceSelection — page fields here, because MUI2 takes the file as the page macro’s own argument and a block-level license would be dropped in silence by a script that lists no license page.

file takes either one path, or a table keyed by locale — which is LicenseLangString, and needs a languages {} block declaring exactly those locales.

page.license {
file = "LICENSE.txt",
bottomText = "Press Page Down to see the rest of the agreement.",
checkbox = "I accept the terms",
}
languages {
locales = { English = {}, German = {} },
}
installer {
page.license { file = { English = "en.txt", German = "de.txt" } },
}

Usage page.components { topText = …, instTypeText = …, listText = …, descriptionTitle = …, descriptionText = … }

topText is ComponentText. The hover text for each entry is the section’s own description option (MUI_DESCRIPTION_TEXT).

page.components {
topText = "Choose which features to install.",
descriptionTitle = "Description",
}

Usage page.directory { topText = …, destinationText = …, variable = …, verifyOnLeave = …, colors = { text = …, background = … } }

topText is DirText and variable is DirVar — a global by name, which the page stores the chosen directory into. verifyOnLeave is DirVerify, and it is a page field for the reason makensis gives when you write the command outside a PageEx: it is not valid there.

page.directory {
topText = "Choose where to install Example.",
verifyOnLeave = true,
}

The one page that must be bound to a local: the local is the page’s id, and it is what menu.folder and menu.write name.

Usage local menu = page.startMenu { defaultFolder = …, topText = …, checkbox = …, registry = { root = …, key = …, value = … } } · menu.folder → string · menu.write(function() … end)

local menu = page.startMenu {
defaultFolder = "Example",
registry = { root = "HKCU", key = "Software/Example", value = "StartMenu" },
}
installer {
menu,
page.instFiles {},
section("Shortcuts", function()
menu.write(function()
createDirectory(SMPROGRAMS .. "/" .. menu.folder)
createShortcut(SMPROGRAMS .. "/" .. menu.folder .. "/Example.lnk",
INSTDIR .. "/Example.exe")
end)
end),
}

The page is installer-only, but menu.folder is not. MUI2 defines no MUI_UNPAGE_STARTMENU, so the uninstaller never ran a page and has nothing to read a variable from — it reads the folder back out of the registry instead. One name, compiled two different ways, and the block decides which:

local menu = page.startMenu {
defaultFolder = "Example",
registry = { root = "HKCU", key = "Software/Example", value = "StartMenu" },
}
installer {
menu,
}
uninstaller {
page.instFiles {},
section("Shortcuts", function()
delete(SMPROGRAMS .. "/" .. menu.folder .. "/Example.lnk")
rmDir(SMPROGRAMS .. "/" .. menu.folder)
end),
}

Two consequences. registry is what makes that read return the folder the user actually picked: without it MUI_STARTMENU_GETFOLDER falls back to defaultFolder, so an uninstaller for a program installed anywhere else removes a directory nobody created and leaves the real one behind. It is the field that makes the installer and the uninstaller agree, not decoration. And menu.write is the installer’s alone — the uninstaller had no page and so never chose a folder to write back.

A menu.folder read inside a func is refused rather than guessed: a func can be called from either the installer or the uninstaller, the two compile to different code, and picking wrong is silent because an empty variable copies without complaint.

Usage page.instFiles { finishHeaderText = …, finishHeaderSubText = …, abortHeaderText = …, abortHeaderSubText = … }

The two pairs are the page’s second state: what the header says once the install has finished, and what it says once it has been aborted.

page.instFiles {
finishHeaderText = "Installation complete",
abortHeaderText = "Installation aborted",
}

Usage page.finish { title = …, text = …, button = …, cancelEnabled = …, run = …, readme = …, link = { … }, reboot = … }

run and readme take a path, or a table: { path = …, text = …, parameters = …, checked = false }, or { call = function() … end } to run something of your own instead.

page.finish {
run = { path = INSTDIR .. "/Example.exe", text = "Run Example now" },
readme = { path = INSTDIR .. "/README.txt", checked = false },
link = { text = "Visit our website", url = "https://example.com" },
}

Uninstaller-only.

Usage page.confirm { topText = …, locationText = …, variable = … }

uninstaller {
page.confirm { topText = "Example will be removed from this computer." },
page.instFiles {},
}

MultiUser.nsh’s page, not MUI2’s, but it takes every MUI2 page setting above. It asks whether to install for all users or only the current one. Installer only, and it needs a multiUser {} block whose executionLevel is not "standard".

Usage page.installMode { headerText = …, pre = …, … }

attributes { name = "Example", outFile = "example-setup.exe" }
multiUser { executionLevel = "highest" }
installer {
page.installMode {},
page.directory {},
page.instFiles {},
section("Core", function() end),
}

Written on installer {} or uninstaller {}, because they apply to the whole installer or uninstaller rather than one page.

Four of them are NSIS commands rather than MUI2 inventions — checkBitmap is CheckBitmap, installColors is InstallColors, progressBar is InstProgressFlags, licenseBkColor is LicenseBkColor — and they look page-scoped and are not: MUI2 writes each one inside an !ifndef that runs on the first page of its kind and never again, so putting them on a page would be a lie a second page tells silently.

MUI2 Installua
MUI_ICON / MUI_UNICON icon = "app.ico"
MUI_HEADERIMAGE and its five relatives headerImage = "header.bmp", or { file = …, stretch = …, rtl = …, right = true, transparentText = true }
MUI_WELCOMEFINISHPAGE_BITMAP wizardImage = "wizard.bmp", or { file = …, stretch = … }
MUI_BGCOLOR / MUI_TEXTCOLOR headerColors = { text = …, background = … }
MUI_ABORTWARNING abortPrompt = true, or { text = …, default = "cancel" }
MUI_FINISHPAGE_NOAUTOCLOSE autoClose = false
MUI_COMPONENTSPAGE_SMALLDESC smallDescriptions = true
MUI_COMPONENTSPAGE_CHECKBITMAP checkBitmap = "check.bmp"
MUI_INSTFILESPAGE_COLORS installColors = "…"
MUI_INSTFILESPAGE_PROGRESSBAR progressBar = "smooth"
MUI_LICENSEPAGE_BGCOLOR licenseBkColor = "…"
MUI_CUSTOMFUNCTION_GUIINIT onGUIInit(function() … end)
MUI_CUSTOMFUNCTION_ABORT onUserAbort(function() … end)
MUI_CUSTOMFUNCTION_ONMOUSEOVERSECTION onMouseOverSection(function() … end)
installer {
icon = "assets/app.ico",
headerImage = { file = "assets/header.bmp", stretch = "AspectFitHeight" },
abortPrompt = { text = "Really cancel the installation?" },
onGUIInit(function() detailPrint("gui up") end),
}

MUI_UI · MUI_UI_HEADERIMAGE · MUI_UI_HEADERIMAGE_RIGHT · MUI_UI_COMPONENTSPAGE_NODESC · MUI_UI_COMPONENTSPAGE_SMALLDESC — they replace the dialog resources MUI2 picks from the settings you already set.

MUI_STARTMENUPAGE_BGCOLOR · MUI_STARTMENUPAGE_TEXTCOLOR — a typo in MUI2 3.12 makes them raise warning 6000, which cannot assemble under -WX.

MUI_FINISHPAGE_ABORTWARNINGCHECK · MUI_INSTFILESPAGE_ABORTWARNING_TEXT · MUI_INSTFILESPAGE_ABORTWARNING_SUBTEXT · MUI_LANGUAGEEX · MUI_LICENSEPAGE_CHECKBOX_TEXT_ACCEPT · MUI_LICENSEPAGE_CHECKBOX_TEXT_DECLINE — MUI 1 names that nothing in MUI2 reads.

MUI_FORCECLASSICCONTROLS · MUI_OPTIMIZE_ALWAYSLTR · MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH · MUI_DISABLE_INSERT_LANGUAGE_AFTER_PAGES_WARNING — deprecated by MUI2, or a warning this compiler cannot produce in the first place.

The other 108 MUI2 names are MUI2’s own internal state — variables, page declarations and function names it defines for itself — and were never settings.