How can I define more than one directory variable in NSIS script? - nsis

Please note that I have created a single variable without a problem as described here, but when I try to create a second variable, I get this error:
!define: "MUI_DIRECTORYPAGE_VARIABLE" already defined!
Here is what I have setup which works for a single variable:
Var HW_DATA_DIR
!define MUI_DIRECTORYPAGE_VARIABLE $HW_DATA_DIR
// ...
!define HW_DATA "HW-Data"
## This is the title on the MyApp Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "$(MUI_DIRECTORYPAGE_TEXT_TOP_HW)"
;Directory for App files and where config.dat will point to
!define MUI_PAGE_CUSTOMFUNCTION_PRE wel_pre
!define MUI_PAGE_CUSTOMFUNCTION_SHOW dir_pre
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
LangString MUI_DIRECTORYPAGE_TEXT_TOP_HW ${LANG_ENGLSH} "Setup will install \
${HW_DATA} in the following folder..."
## Sections Group 0
Function wel_pre
StrCpy $APP_DIR "C:\${HW_DATA}"
strcmp '$0' '1' noabort
messagebox mb_yesno|mb_defbutton2|mb_iconquestion "Leave MyApp directories at default values of C:\${HW_DATA} and C:\${HW_WORKSPACE}?" idno noabort
strcpy $0 2
abort
noabort:
strcpy $0 1
Functionend
Function dir_pre
GetDlgItem $1 $HWNDPARENT 1037
CreateFont $2 "$(^Font)" "8" "700"
SendMessage $1 ${WM_SETFONT} $2 0
SetCtlColors $1 '0x000000' '0xFFFFFF'
GetDlgItem $1 $HWNDPARENT 1038
CreateFont $2 "$(^Font)" "8" ""
SendMessage $1 ${WM_SETFONT} $2 0
SetCtlColors $1 '0x000000' '0xFFFFFF'
Functionend
!define PROG0_InstDir "C:\${HW_DATA}"
!define PROG0_StartIndex ${PROG0}
!define PROG0_EndIndex ${SEC0}
SectionGroup /e "MyApp" PROG0
Section "Main" SEC0
CreateDirectory "$HW_DATA"
CreateDirectory "$HW_DATA\Plugins"
CreateDirectory "$HW_DATA\Plugins\ComputePlugin"
CreateDirectory "$HW_DATA\Plugins\ExtensionPlugin"
File /oname=$HW_DATA\Plugins\ComputePlugin\computeplugin.xplot.dll computeplugin.xplot.dll
File /oname=$HW_DATA\Plugins\ExtensionPlugin\hwProxyInterface.MyProApp.dll hwProxyInterface.MyProApp.dll
SectionEnd
This is how I am attempting to add a second variable:
Var HW_DATA_DIR
!define MUI_DIRECTORYPAGE_VARIABLE $HW_DATA_DIR
Var HW_WORKSPACE_DIR
!define MUI_DIRECTORYPAGE_VARIABLE $HW_WORKSPACE_DIR
// ...
!define HW_DATA "HW-Data"
!define HW_WORKSPACE "HW-Workspaces"
## This is the title on the MyApp Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "$(MUI_DIRECTORYPAGE_TEXT_TOP_HW)"
;Directory for MyApp files and where config.dat will point to
!define MUI_PAGE_CUSTOMFUNCTION_PRE wel_pre
!define MUI_PAGE_CUSTOMFUNCTION_SHOW dir_pre
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
// ...
LangString MUI_DIRECTORYPAGE_TEXT_TOP_HW ${LANG_ENGLSH} "Setup will install \
${HW_DATA} in the following folder..."
## Sections Group 0
Function wel_pre
StrCpy $HW_DATA_DIR "C:\${HW_DATA}"
StrCpy $$HW_WORKSPACE_DIR "C:\${HW_WORKSPACE}"
strcmp '$0' '1' noabort
messagebox mb_yesno|mb_defbutton2|mb_iconquestion "Leave Headwave directories at default values of C:\${HW_DATA} and C:\${HW_WORKSPACE}?" idno noabort
strcpy $0 2
abort
noabort:
strcpy $0 1
Functionend
Function dir_pre
GetDlgItem $1 $HWNDPARENT 1037
CreateFont $2 "$(^Font)" "8" "700"
SendMessage $1 ${WM_SETFONT} $2 0
SetCtlColors $1 '0x000000' '0xFFFFFF'
GetDlgItem $1 $HWNDPARENT 1038
CreateFont $2 "$(^Font)" "8" ""
SendMessage $1 ${WM_SETFONT} $2 0
SetCtlColors $1 '0x000000' '0xFFFFFF'
Functionend
!define PROG0_InstDir "C:\${HW_DATA}"
!define PROG0_StartIndex ${PROG0}
!define PROG0_EndIndex ${SEC0}
SectionGroup /e "MyApp" PROG0
Section "Main" SEC0
CreateDirectory "$HW_DATA"
CreateDirectory "$HW_DATA\Plugins"
CreateDirectory "$HW_DATA\Plugins\ComputePlugin"
CreateDirectory "$HW_DATA\Plugins\ExtensionPlugin"
File /oname=$HW_DATA\Plugins\ComputePlugin\computeplugin.xplot.dll computeplugin.xplot.dll
File /oname=$HW_DATA\Plugins\ExtensionPlugin\hwProxyInterface.MyProApp.dll hwProxyInterface.MyProApp.dll
CreateDirectory "$HW_WORKSPACE_DIR"
SectionEnd
See also
https://nsis.sourceforge.io/Demonstrating_Page%27s_Custom_Functions_Pre_Show_Leave
https://nsis.sourceforge.io/Two_installations_in_one_installer
Does anyone have any suggestions? In my case I happen to need to create a data directory and a workspace directory for a particular application, with default values but also allowing the user to change the DATA directory and the WORKSPACE directory to custom directories in case, say, they need to be on a network drive instead of being on the default C:\ drive. TIA.

The directory variable define is a per-page setting and must be set just before the page macro:
Var foo
Var bar
!define MUI_DIRECTORYPAGE_VARIABLE $foo
insertmacro MUI_PAGE_DIRECTORY
!define MUI_DIRECTORYPAGE_VARIABLE $bar
!insertmacro MUI_PAGE_DIRECTORY

Related

How to create shortcut for desktop and startmenu after installation in NSIS?

I am new to NSIS. I am trying to add this below style of the page in post-installation (i.e. before the finish page). Could anyone suggest, whether we can do a single custom page and add the necessary checkbox, radio button, and program groups ? or any other suggestions.
Below is my page sequence:
!insertmacro MUI_PAGE_DIRECTORY
;Custom page for selecting service name to restart.
Page custom nsDialogsSelectService ngDialogSelectServicePageLeave
; variable and text for the app data dir
!define MUI_DIRECTORYPAGE_VARIABLE $appDataDir
!define MUI_PAGE_HEADER_TEXT "Choose Data Directory"
!define MUI_PAGE_HEADER_SUBTEXT "Choose the folder in which to install application data for ${PRODUCT_FULL} ${PVERSION}."
!define MUI_DIRECTORYPAGE_TEXT_TOP "Setup will install data directory need todo. To install in a different folder, click Browse and select another folder. Click Next to Continue."
!define MUI_DIRECTORYPAGE_TEXT_DESTINATION "Destination Folder"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_TITLE_3LINES
Page custom nsDialogShortcut ngDialogShortcutPageLeave ;Page contain checkbox , based on the selection of checkbox I am showing Startmenu and then finish page.
!insertmacro MUI_PAGE_STARTMENU 0 $SMDir
!insertmacro MUI_PAGE_FINISH
Giving the user the choice of user/machine shortcuts is in conflict with how UAC works. When a non-admin user elevates with an administrator account the installer will end up running with the "wrong" profile.
The Windows guidelines say that only application suites (with individual major applications, like MS Office) should create Start menu folders. Regular applications should create their (single) shortcut directly in $SMPrograms. You should not create shortcuts to the uninstaller nor help-files. You should also refrain from creating a desktop shortcut.
This means you can simply use the components page to provide the shortcut option(s):
!include MUI2.nsh
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Section "Program files"
SectionIn RO
SetOutPath $InstDir
File "MyApp.exe"
SectionEnd
Section "Start menu shortcut"
CreateShortcut "$SMPrograms\$(^Name).lnk" "$InstDir\MyApp.exe"
SectionEnd
Section /o "Desktop shortcut"
CreateShortcut "$Desktop\$(^Name).lnk" "$InstDir\MyApp.exe"
SectionEnd
or as a checkbox on the Finish page:
!include MUI2.nsh
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_SHOWREADME ""
!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Start menu shortcut"
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateShortcuts
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Function CreateShortcuts
CreateShortcut "$SMPrograms\$(^Name).lnk" "$InstDir\MyApp.exe"
FunctionEnd
If you actually have a suite of applications then you can use the Start menu page to prompt for a folder name:
Var SMFolder
!include MUI2.nsh
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE STARTMENU Suite $SMFolder
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Section
!insertmacro MUI_STARTMENU_WRITE_BEGIN Suite
CreateDirectory "$SMPrograms\$SMFolder"
CreateShortcut "$SMPrograms\$SMFolder\App1.lnk" "$InstDir\MyApp1.exe"
CreateShortcut "$SMPrograms\$SMFolder\App2.lnk" "$InstDir\MyApp2.exe"
; TODO: Write $SMFolder to the registry or a .ini so your uninstaller can delete the folder
!insertmacro MUI_STARTMENU_WRITE_END
SectionEnd
In the unlikely event that you have a suite of applications and you also want to create desktop shortcuts then yes, you need to use a custom page:
Var SMDir
Var SMCheck
Var DeskCheck
Var SMList
Var SMDirEdit
!include LogicLib.nsh
!include nsDialogs.nsh
!include MUI2.nsh
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
Page Custom MyShortcutsPageCreate MyShortcutsPageLeave
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Function .onInit
StrCpy $SMDir "$(^Name)" ; Default
StrCpy $SMCheck ${BST_CHECKED}
FunctionEnd
Function MyShortcutsPageCreate
!insertmacro MUI_HEADER_TEXT "Shortcuts" "Shortcuts blah blah blah"
nsDialogs::Create 1018
Pop $0
${IfThen} $0 == error ${|} Abort ${|}
${NSD_CreateCheckbox} 0 0u 50% 12u "Create Start menu shortcuts"
Pop $R8
SendMessage $R8 ${BM_SETCHECK} $SMCheck ""
${NSD_CreateCheckbox} 0 14u 50% 12u "Create desktop shortcuts"
Pop $R9
SendMessage $R9 ${BM_SETCHECK} $DeskCheck ""
${NSD_CreateSortedListBox} 0 28u 100% -43u ""
Pop $SMList
${NSD_CreateText} 0 -13u 100% 12u "$SMDir"
Pop $SMDirEdit
${NSD_LB_AddString} $SMList "(Default)"
${NSD_LB_SetItemData} $SMList 0 1 ; Mark as special
SetShellVarContext Current
Call FillSMList
SetShellVarContext All
Call FillSMList
SetShellVarContext ? ; TODO: Restore to what you actually are installing as
${NSD_OnChange} $SMList OnSMListChanged
${NSD_OnClick} $R8 OnSMCheckChanged
Push $R8
Call OnSMCheckChanged
nsDialogs::Show
FunctionEnd
Function FillSMList
FindFirst $0 $1 "$SMPrograms\*"
loop:
StrCmp $1 "" done
${If} ${FileExists} "$SMPrograms\$1\*.*"
${AndIf} $1 != "."
${AndIf} $1 != ".."
${NSD_LB_FindStringExact} $SMList "$1" $2
${If} $2 < 0
${NSD_LB_AddString} $SMList $1
${EndIf}
${EndIf}
FindNext $0 $1
Goto loop
done:
FindClose $0
FunctionEnd
Function OnSMCheckChanged
Pop $0
${NSD_GetChecked} $0 $0
EnableWindow $SMList $0
EnableWindow $SMDirEdit $0
FunctionEnd
Function OnSMListChanged
Pop $0
${NSD_LB_GetSelection} $SMList $0
${NSD_SetText} $SMDirEdit "$0\$(^Name)"
${NSD_LB_GetSelectionIndex} $SMList $0
${NSD_LB_GetItemData} $SMList $0 $0
${If} $0 <> 0
${NSD_SetText} $SMDirEdit "$(^Name)"
${EndIf}
FunctionEnd
Function MyShortcutsPageLeave
${NSD_GetChecked} $R8 $SMCheck
${NSD_GetChecked} $R9 $DeskCheck
${NSD_GetText} $SMDirEdit $SMDir
FunctionEnd
Section
${If} $SMCheck <> 0
CreateDirectory "$SMPrograms\$SMDir"
CreateShortcut "$SMPrograms\$SMDir\App1.lnk" "$InstDir\App1.exe"
CreateShortcut "$SMPrograms\$SMDir\App2.lnk" "$InstDir\App2.exe"
${EndIf}
${If} $DeskCheck <> 0
CreateShortcut "$Desktop\App1.lnk" "$InstDir\App1.exe"
CreateShortcut "$Desktop\App2.lnk" "$InstDir\App2.exe"
${EndIf}
SectionEnd

Installing two apps but second app gets first app name appended on browse for directory [duplicate]

This question already has an answer here:
NSIS Directory Page after Browse for install dir "${PRODUCT_NAME}" is added after selection
(1 answer)
Closed 2 years ago.
Please note that I initially created an installer for two applications based on https://nsis.sourceforge.io/Two_installations_in_one_installer where the first application is the main one while the second application is the license manager. That said, I did remove the nested checkboxes with only one layer of checkboxes. FYI, I have also been referencing this article: https://www.codeproject.com/Articles/24187/Creating-an-Installer?msg=5782957#xx5782957xx
The issue that I am having is that when I get to the second license manager app, where I click to browse for a custom folder, and create a new custom folder, when I press OK on the browse it then returns with this custom folder but then appends the application name after that. So for example when I return from the browse for custom directory dialog, it would not return with, say:
C:\Tools\FlexLM
It would instead return with the following even though I didn't add the "MyApp" portion:
C:\Tools\FlexLM\MyApp
Please find the full source code here:
!include "LogicLib.nsh"
!include "Sections.nsh"
;Include Modern UI
!include "MUI2.nsh"
!define MAJOR_VERSION "1"
!define MINOR_VERSION "2"
!define PATCH_VERSION "3"
!define BUILD_VERSION "4"
!define APP_COPYRIGHT "MyApp © MyCompany 2021"
!define COMPANY_NAME "MyCompany"
!define FLEX_LM "FlexLM"
!define FLEX_DIR "FlexSQI"
!define LANG_ENGLSH "English"
!define PRODUCT_NAME "MyApp"
!define PRODUCT_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}.${BUILD_VERSION}"
!define SETUP_NAME "MyAppSetup.exe"
BrandingText "MyCompany"
OutFile ${SETUP_NAME}
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}"
InstallDirRegKey HKLM "Software\$PRODUCT_NAME" ""
ShowInstDetails hide
ShowUnInstDetails hide
SetCompressor /SOLID lzma
SetCompressorDictSize 12
Var MyAppCheckboxState
Var FlexLmCheckboxState
;Request application privileges for Windows
RequestExecutionLevel admin
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "MyAppLicense.txt"
!define MUI_PAGE_CUSTOMFUNCTION_PRE SelectFilesCheck
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE ComponentsLeave
!insertmacro MUI_PAGE_COMPONENTS
## This is the title on the MyApp Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "$(MUI_DIRECTORYPAGE_TEXT_TOP_A)"
!define MUI_PAGE_CUSTOMFUNCTION_PRE SelectFilesA
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
## This is the title on the FlexLM Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "$(MUI_DIRECTORYPAGE_TEXT_TOP_B)"
!define MUI_PAGE_CUSTOMFUNCTION_PRE SelectFilesB
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE DeleteSectionsINI
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English"
;--------------------------------
LangString NoSectionsSelected ${LANG_ENGLSH} "You haven't selected any sections!"
LangString MUI_DIRECTORYPAGE_TEXT_TOP_A ${LANG_ENGLSH} "Setup will install \
${PRODUCT_NAME} in the following folder..."
LangString MUI_DIRECTORYPAGE_TEXT_TOP_B ${LANG_ENGLSH} "Setup will install \
${FLEX_LM} in the following folder..."
;--------------------------------
; Settings
!define PROG1_InstDir "$PROGRAMFILES64\${PRODUCT_NAME}"
!define PROG1_StartIndex ${SEC1}
!define PROG1_EndIndex ${SEC1}
!define PROG2_InstDir "C:\${FLEX_DIR}"
!define PROG2_StartIndex ${SEC3}
!define PROG2_EndIndex ${SEC3}
;--------------------------------
; Start sections
Section "MyApp" SEC1
StrCpy $MyAppCheckboxState ${BST_CHECKED}
${If} $MyAppCheckboxState == ${BST_CHECKED}
##All the files in Group 1 will be installed to the same location, $INSTDIR
SetOutPath "$INSTDIR"
# specify files to go in output path
File config.dat
File MyApp.exe
File ReleaseNotes.txt
File MyCompany_LandingPage_114.bmp
File MyAppLicense.txt
# create a shortcut named "new shortcut" in the start menu programs directory
CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" "$PROGRAMFILES64\${PRODUCT_NAME}\${PRODUCT_NAME}.exe"
# Add application to registry
ClearErrors
WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Company Name' "${COMPANY_NAME}"
WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Version' "${PRODUCT_VERSION}"
WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'AppID' "{0be21143-9089-47fa-9736-c45609d13d70}"
# Add program to Add/Remove programs
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"DisplayIcon" "$PROGRAMFILES64\${PRODUCT_NAME}\${PRODUCT_NAME}.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"AppID" "{0be21143-9089-47fa-9736-c45609d13d70}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"DisplayName" "${PRODUCT_NAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"InstallLocation" "$INSTDIR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"Publisher" "${COMPANY_NAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"UninstallString" "$\"$INSTDIR\uninstaller.exe$\""
# define uninstaller name
WriteUninstaller $INSTDIR\uninstaller.exe
${EndIf}
# messagebox mb_ok sec1
SectionEnd
Section "FlexLM" SEC3
StrCpy $FlexLmCheckboxState ${BST_CHECKED}
${If} $FlexLmCheckboxState == ${BST_CHECKED}
##All the files in Group 2 will be installed to the same location, $INSTDIR
SetOutPath "$INSTDIR"
File installs.exe
File lmdown.exe
File lmflex.exe
# define uninstaller name
WriteUninstaller $INSTDIR\uninstaller.exe
${EndIf}
# messagebox mb_ok sec3
SectionEnd
;--------------------------------
;Descriptions
;Language strings
LangString DESC_SecMyApp ${LANG_ENGLISH} "MyAppTM software is an easy-to-use suite of tools."
LangString DESC_SecFlexLM ${LANG_ENGLISH} "FlexSQI contains all the files necessary to implement the FlexLM license server."
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC1} $(DESC_SecMyApp)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC3} $(DESC_SecFlexLM)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
Section "Uninstall"
# Always delete uninstaller first
Delete $INSTDIR\uninstaller.exe
# now delete installed files and registry keys for MyApp
Delete $INSTDIR\config.dat
Delete $INSTDIR\MyApp.exe
Delete $INSTDIR\ReleaseNotes.txt
Delete $INSTDIR\MyCompany_LandingPage_114.bmp
Delete $INSTDIR\MyAppLicense.txt
Delete "$SMPROGRAMS\MyApp.lnk"
DeleteRegKey HKCU "SOFTWARE\${PRODUCT_NAME}"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
DeleteRegKey /ifempty HKCU "Software\Modern UI Test"
# now delete installed files and registry keys for FlexLM
Delete $INSTDIR\lmdown.exe
Delete $INSTDIR\lmflex.exe
Delete $INSTDIR\installs.exe
# Delete the QI Pro and FlexLM directories
RMDir $INSTDIR
SectionEnd
;--------------------------------
; Please don`t modify below here unless you`re a NSIS 'wiz-kid'
## Create $PLUGINSDIR
Function .onInit
InitPluginsDir
SetOutPath $TEMP
File /oname=spltmp.bmp "MyCompany_LandingPage_114.bmp"
splash::show 2000 $TEMP\spltmp
Pop $0 ; $0 has '1' if the user closed the splash screen early,
; '0' if everything closed normally, and '-1' if some error occurred.
Delete $TEMP\spltmp.bmp
FunctionEnd
## If user goes back to this page from 1st Directory page
## we need to put the sections back to how they were before
Var IfBack
Function SelectFilesCheck
StrCmp $IfBack 1 0 NoCheck
;Call ResetFiles
NoCheck:
FunctionEnd
## Also if no sections are selected, warn the user!
Function ComponentsLeave
Push $R0
Push $R1
Call IsPROG1Selected
Pop $R0
Call IsPROG2Selected
Pop $R1
StrCmp $R0 1 End
StrCmp $R1 1 End
Pop $R1
Pop $R0
MessageBox MB_OK|MB_ICONEXCLAMATION "$(NoSectionsSelected)"
Abort
End:
Pop $R1
Pop $R0
FunctionEnd
Function IsPROG1Selected
Push $R0
StrCpy $R0 ${PROG1_StartIndex} # Group 1 start
SectionGetFlags 0 $R0 # Get section flags
IntOp $R0 $R0 & ${SF_SELECTED}
StrCmp $R0 ${SF_SELECTED} 0 +3 # If section is selected, done
StrCpy $R0 1
Exch $R0
FunctionEnd
Function IsPROG2Selected
Push $R1
StrCpy $R1 ${PROG2_StartIndex} # Group 2 start
IntOp $R1 $R1 + 1
SectionGetFlags 1 $R1 # Get section flags
IntOp $R1 $R1 & ${SF_SELECTED}
StrCmp $R1 ${SF_SELECTED} 0 +3 # If section is selected, done
StrCpy $R1 1
Exch $R1
FunctionEnd
## Here we are selecting first sections to install
## by unselecting all the others!
Function SelectFilesA
# If user clicks Back now, we will know to reselect Group 2`s sections for
# Components page
StrCpy $IfBack 1
# We need to save the state of the Group 2 Sections
# for the next InstFiles page
Push $R0
Push $R1
StrCpy $R0 ${PROG2_StartIndex} # Group 2 start
; Loop:
; IntOp $R0 $R0 + 1
; SectionGetFlags $R0 $R1 # Get section flags
; WriteINIStr "$PLUGINSDIR\sections.ini" Sections $R0 $R1 # Save state
; !insertmacro UnselectSection $R0 # Then unselect it
; StrCmp $R0 ${PROG2_EndIndex} 0 Loop
# Don`t install prog 1?
Call IsPROG1Selected
Pop $R0
StrCmp $R0 1 +4
Pop $R1
Pop $R0
Abort
# Set current $INSTDIR to PROG1_InstDir define
StrCpy $INSTDIR "${PROG1_InstDir}"
Pop $R1
Pop $R0
FunctionEnd
## Here we need to unselect all Group 1 sections
## and then re-select those in Group 2 (that the user had selected on
## Components page)
Function SelectFilesB
Push $R0
;Push $R1
StrCpy $R0 ${PROG1_StartIndex} # Group 1 start
; Loop:
; IntOp $R0 $R0 + 1
; !insertmacro UnselectSection $R0 # Unselect it
; StrCmp $R0 ${PROG1_EndIndex} 0 Loop
; Call ResetFiles
# Don't install prog 2?
Call IsPROG2Selected
Pop $R0
StrCmp $R0 1 +4
Pop $R1
Pop $R0
Abort
# Set current $INSTDIR to PROG2_InstDir define
StrCpy $INSTDIR "${PROG2_InstDir}"
;Pop $R1
Pop $R0
FunctionEnd
## Here we are deleting the temp INI file at the end of installation
Function DeleteSectionsINI
FlushINI "$PLUGINSDIR\Sections.ini"
Delete "$PLUGINSDIR\Sections.ini"
FunctionEnd
Does anyone have any suggestions? TIA.
UPDATE:
Please note that I think the issue might be in this file:
~\NSIS\Contrib\Modern UI 2\Pages\Directory.nsh
Where I see this code:
!macro MUI_FUNCTION_DIRECTORYPAGE PRE SHOW LEAVE
Function "${PRE}"
!insertmacro MUI_PAGE_FUNCTION_CUSTOM PRE
!insertmacro MUI_HEADER_TEXT_PAGE $(MUI_${MUI_PAGE_UNINSTALLER_PREFIX}TEXT_DIRECTORY_TITLE) $(MUI_${MUI_PAGE_UNINSTALLER_PREFIX}TEXT_DIRECTORY_SUBTITLE)
FunctionEnd
Function "${SHOW}"
;Get control handles
FindWindow $mui.DirectoryPage "#32770" "" $HWNDPARENT
GetDlgItem $mui.DirectoryPage.Text $mui.DirectoryPage 1006
GetDlgItem $mui.DirectoryPage.DirectoryBox $mui.DirectoryPage 1020
GetDlgItem $mui.DirectoryPage.Directory $mui.DirectoryPage 1019
GetDlgItem $mui.DirectoryPage.BrowseButton $mui.DirectoryPage 1001
GetDlgItem $mui.DirectoryPage.SpaceRequired $mui.DirectoryPage 1023
GetDlgItem $mui.DirectoryPage.SpaceAvailable $mui.DirectoryPage 1024
!ifdef MUI_DIRECTORYPAGE_BGCOLOR
!insertmacro MUI_DEFAULT MUI_DIRECTORYPAGE_TEXTCOLOR ""
SetCtlColors $mui.DirectoryPage.Directory "${MUI_DIRECTORYPAGE_TEXTCOLOR}" "${MUI_DIRECTORYPAGE_BGCOLOR}"
!endif
!insertmacro MUI_PAGE_FUNCTION_CUSTOM SHOW
FunctionEnd
Function "${LEAVE}"
!insertmacro MUI_PAGE_FUNCTION_CUSTOM LEAVE
FunctionEnd
!macroend
But when I look at the above it still is not clear to me how the MyApp name could be added to the end of the directory string.
Found the solution here:
NSIS Directory Page after Browse for install dir "${PRODUCT_NAME}" is added after selection
So in my case I needed to add a :
InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}\"

Select section by variable

!include "MUI2.nsh"
!include "FileFunc.nsh"
!include "LogicLib.nsh"
;---------------------------------------------------------------------------
;---------------------------------------------------------------------------
ShowInstDetails show
RequestExecutionLevel admin
;---------------------------------------------------------------------------
Name "Test"
Outfile "Test.exe"
;---------------------------------------------------------------------------
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;---------------------------------------------------------------------------
!insertmacro MUI_LANGUAGE "English"
;---------------------------------------------------------------------------
LangString DESC_sec1 ${LANG_ENGLISH} "sec1 files"
Section /o "sec1" sec1
SectionEnd
LangString DESC_sec2 ${LANG_ENGLISH} "sec2 files"
Section /o "sec2" sec2
SectionEnd
LangString DESC_sec3 ${LANG_ENGLISH} "sec3 files"
Section /o "sec3" sec3
SectionEnd
Var test
Function .OnInit
StrCpy $test "sec2"
!insertmacro SelectSection $test
FunctionEnd
How to select section at runtime by section name?
In eample always selected 1st section (think its a bug)
But if i rewrite like this
!insertmacro SelectSection ${sec2}
All works fine...
Is there way to select section by name from variable?
some long text some long text some long text some long text
some long text some long text some long text some long text
Sections are accessed by their ID which is a number set at compile time:
Section "Foo"
SectionEnd
Section "Bar" SID_BAR
SectionEnd
Section "Baz"
SectionEnd
Page Components
Page InstFiles
!include Sections.nsh
Function .onInit
!insertmacro UnselectSection ${SID_BAR}
FunctionEnd
If you want to use the display name then you have to manually enumerate the sections:
Section "Foo"
SectionEnd
Section "Bar"
SectionEnd
Section "Baz"
SectionEnd
Page Components
Page InstFiles
!macro GetSectionIdFromName name outvar
Push "${name}"
Call GetSectionIdFromName
Pop ${outvar} ; ID of section, "" if not found
!macroend
Function GetSectionIdFromName
Exch $1 ; Name
Push $2
Push $3
StrCpy $2 -1
ClearErrors
loop:
IntOp $2 $2 + 1
SectionGetText $2 $3
IfErrors fail
StrCmp $3 $1 "" loop
StrCpy $1 $2
Goto done
fail:
StrCpy $1 ""
done:
Pop $3
Pop $2
Exch $1
FunctionEnd
!include Sections.nsh
!include LogicLib.nsh
Function .onInit
StrCpy $1 "Bar" ; The name we are looking for
!insertmacro GetSectionIdFromName $1 $0
${If} $0 != ""
!insertmacro UnselectSection $0
${EndIf}
FunctionEnd

How to set the checkbox by default checked in the windows installer Finish page using NSIS

I wanted to set the checkbox as checked by default in the windows installer Finish page using NSIS. For that I used the below code snipped. But it is not even displaying the checkbox in the Finish page. Please help me on this.
Var Checkbox
Var CheckState ; Stored globally so we remember the choice if the user presses the back button and goes back to our page
!define CheckHeight 28
!macro CreateNativeControl hParent cls style exstyle x y w h text ; Note: Only supports pixel coordinates
System::Call 'USER32::CreateWindowEx(i ${exstyle}, t "${cls}", ts, i ${style}, i ${x}, i ${y}, i ${w}, i ${h}, p ${hParent}, i0, i0, i0)p.s' "${text}"
!macroend
!define MUI_PAGE_CUSTOMFUNCTION_SHOW FinishShow
!insertmacro MUI_PAGE_FINISH
Function FinishShow
System::Call *(i,i,i,i)p.r0 ; NSIS 2.51+
System::Call 'USER32::GetWindowRect(p$mui.FinishPage.Text, pr0)'
System::Call 'USER32::MapWindowPoints(i0,p$mui.FinishPage,p$0,i2)'
System::Call '*$0(i.r2,i.r3,i.r4,i.r5)'
System::Free $0
IntOp $5 $5 - ${CheckHeight}
System::Call 'USER32::SetWindowPos(i$mui.FinishPage.Text,i,i,i,i$4,i$5,i0x6)'
; Create and initialize the checkbox
IntOp $5 $3 + $5 ; y = TextTop + TextHeight
!insertmacro CreateNativeControl $mui.FinishPage ${__NSD_CheckBox_CLASS} "${__NSD_CheckBox_STYLE}" "${__NSD_CheckBox_EXSTYLE}" 0 $5 300 ${CheckHeight} "CheckboxTest"
Pop $Checkbox
SendMessage $mui.FinishPage ${WM_GETFONT} 0 0 $0
SendMessage $Checkbox ${WM_SETFONT} $0 1
System::Call 'USER32::SetWindowPos(i$Checkbox,i0,i,i,i,i,i0x33)'
${IfThen} $CheckState == "" ${|} StrCpy $CheckState 1 ${|}
${NSD_SetState} $Checkbox $CheckState
FunctionEnd
The finish page has built-in support for two optional check-boxes:
!include MUI2.nsh
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN ""
!define MUI_FINISHPAGE_RUN_TEXT "Run Foo"
!define MUI_FINISHPAGE_RUN_FUNCTION MyRunFoo
;define MUI_FINISHPAGE_RUN_NOTCHECKED
!define MUI_FINISHPAGE_SHOWREADME "$InstDir\Bar.exe"
!define MUI_FINISHPAGE_SHOWREADME_TEXT "Run Bar"
;define MUI_FINISHPAGE_SHOWREADME_FUNCTION
!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Function MyRunFoo
; Exec '"$InstDir\Foo.exe"'
FunctionEnd
Section
SectionEnd
Your example code is also missing a important call to !insertmacro MUI_LANGUAGE.
The function specified by MUI_FINISHPAGE_RUN_FUNCTION is only executed if the checkbox is checked. This is normal expected behavior.
If you want to do custom handling you can do it in the leave page:
!include nsDialogs.nsh
!include LogicLib.nsh
!include MUI2.nsh
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN ""
!define MUI_FINISHPAGE_RUN_TEXT "Blah blah"
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE FinishLeave
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Function FinishLeave
${NSD_GetState} $mui.FinishPage.Run $0 ; or $mui.FinishPage.ShowReadme
${If} $0 <> 0
MessageBox mb_ok "Checkbox checked"
${Else}
MessageBox mb_ok "Checkbox not checked"
${EndIf}
FunctionEnd

Uninstall only installed files NSIS

I want to find a way to uninstall only installed files.
I tried http://nsis.sourceforge.net/Uninstall_only_installed_files but I have around 10.000 files nested in many directories, therefore specifying each file separately in a nsis script is not a solution.
You can use the !system command to execute external tools at compile-time. This external tool/script should generate include files with install and uninstall instructions.
You can also use NSIS on the fly to generate the instructions but it is a bit messy when you put all of it in a single script:
; Generate some files for this example:
!system 'mkdir "$%temp%\testdir\subdir\sub2\sub3"'
!appendfile "$%temp%\testdir\file1.txt" ""
!appendfile "$%temp%\testdir\subdir\file2.txt" ""
!appendfile "$%temp%\testdir\subdir\sub2\file3.txt" ""
!appendfile "$%temp%\testdir\subdir\sub2\sub3\file4.txt" ""
!macro GENFILELIST_Enum SourceDir InstDir
Push "${SourceDir}"
Push "${InstDir}"
Call GENFILELIST_Enum
!macroend
### Start editing here ###
!macro GeneratePages
OutFile "test.exe"
Name "Test"
RequestExecutionLevel admin
InstallDir "$ProgramFiles\MyTestApp"
Page Directory
Page InstFiles
UninstPage UninstConfirm
UninstPage InstFiles
!macroend
!macro GENFILELIST
!insertmacro GENFILELIST_Enum "$%temp%\testdir" "" ; Files to install
!macroend
!macro GenerateInstallSections FileInstructions
Section
SetOutPath $InstDir
!include "${FileInstructions}"
WriteUninstaller "$InstDir\Uninst.exe"
SectionEnd
!macroend
!macro GenerateUninstallSections FileInstructions
Section Uninstall
SetOutPath $InstDir
!include "${FileInstructions}"
SetOutPath $Temp
Delete "$InstDir\Uninst.exe"
RmDir $InstDir
SectionEnd
!macroend
### Stop editing here ###
!ifndef GENFILELIST_IN
!tempfile GENFILELISTAPP
!tempfile GENFILELIST_IN
!tempfile GENFILELIST_UN
!appendfile "${GENFILELIST_IN}" '!define GENFILELIST_UN "${GENFILELIST_UN}"$\n'
!appendfile "${GENFILELIST_IN}" 'OutFile "${GENFILELISTAPP}"$\n'
!system '"${NSISDIR}/makensis" "/DGENFILELIST_IN=${GENFILELIST_IN}" "${__FILE__}"' = 0
!system '"${GENFILELISTAPP}" /S' = 0
!delfile "${GENFILELISTAPP}"
!insertmacro GeneratePages
!insertmacro GenerateInstallSections "${GENFILELIST_IN}"
!insertmacro GenerateUninstallSections "${GENFILELIST_UN}"
!delfile "${GENFILELIST_IN}"
!delfile "${GENFILELIST_UN}"
!else
!include LogicLib.nsh
!include "${GENFILELIST_IN}"
!delfile "${GENFILELIST_IN}"
!macro GENFILELIST_Append file string tmpvar
FileOpen ${tmpvar} "${file}" a
FileSeek ${tmpvar} 0 END
FileWrite ${tmpvar} '${string}'
FileClose ${tmpvar}
!macroend
!define DOLLAR "$$"
Function GENFILELIST_Enum
System::Store S
Pop $9 ; Relative path
Pop $8 ; Base path
${IfThen} $9 == "" ${|} StrCpy $9 "${DOLLAR}OutDir" ${|}
FindFirst $0 $1 "$8\*"
loop:
StrCmp $1 "" stop
StrCmp $1 "." next
StrCmp $1 ".." next
StrCpy $3 $1
${If} ${FileExists} "$8\$1\*"
!insertmacro GENFILELIST_Append "${GENFILELIST_IN}" 'SetOutPath "${DOLLAR}OutDir\$1"$\n' $2
Push $8\$1
Push $9\$1
Call GENFILELIST_Enum
${Else}
!insertmacro GENFILELIST_Append "${GENFILELIST_IN}" 'File "$8\$1"$\n' $2
!insertmacro GENFILELIST_Append "${GENFILELIST_UN}" 'Delete "$9\$1"$\n' $2
${EndIf}
next:
FindNext $0 $1
Goto loop
stop:
FindClose $0
!insertmacro GENFILELIST_Append "${GENFILELIST_UN}" 'RmDir "$9"$\n' $2
System::Store L
FunctionEnd
RequestExecutionLevel user
Section
!insertmacro GENFILELIST
SectionEnd
!endif

Resources