Create more than 1 uninstaller in a NSIS Section - installation

Can a NSIS Section create more than 1 uninstaller?
My installer can install plugins for 3 different versions of an Application - therefore theres 3 different directories where the installer will install the files.
In each of those directories I want to add an uninstaller file that will remove only the files in that directory.
Each of the 3 uninstall files are created within the same Section area, is this invalid? How can I get my script to create 3 uninstallers(if possible)?
The following Section only creates one uninstaller, the last one(Version 10 uninstaller):
Section "Install Plugin Files" MainSetup
CheckInstallVers8:
IntCmp $installVers8 1 InstallVersion8 CheckInstallVers9 InstallVersion8
CheckInstallVers9:
IntCmp $installVers9 1 InstallVersion9 CheckInstallVers10 InstallVersion9
CheckInstallVers10:
IntCmp $installVers10 1 InstallVersion10 MainInstallation InstallVersion10
InstallVersion8:
# install plugins...
SetOutPath $VERS8DIR
writeUninstaller "${APPNAME} Uninstall.exe"
GoTo CheckInstallVers9
InstallVersion9:
SetOutPath $VERS9DIR
writeUninstaller "${APPNAME} Uninstall.exe"
GoTo CheckInstallVers10
InstallVersion10:
SetOutPath $VERS10DIR
writeUninstaller "${APPNAME} Uninstall.exe"
SectionEnd

You can call WriteUninstaller as many times as you want but you should use the full path name (writeUninstaller "$VERSxDIR\${APPNAME} Uninstall.exe")
You did not post a full script so it is hard to tell what is wrong with the logic (You might want to use LogicLib.nsh so you can do {IF}s) but you should be able to "MessageBox debug" your way to the solution.
One thing you did not talk about that might be relevant is the uninstaller logic. If the 3 uninstallers all do the exact same task then this is not an issue but I'd expect at least a difference in the uninstaller registry registration.
There are two ways to deal with this:
Tag data to the end of the uninstaller (or a .ini in the same directory)
Use !system to call makensis.exe and generate uninstallers at compile time that you include as normal Files
A different solution that might be relevant for plugins in sub-directories is to use a component page in the uninstaller and only delete the uninstaller when all 3 plugins have been removed...

Related

How to set the ALL USER Directory as an output directory for a installer created with NSIS

I am trying to install a file to ALL USER DOCUMENTS Directory(windows 7) using NSIS.
In my code i am setting "SetShellVarContext all" but still the files are getting installed at current user directory
Please help
Here is my code
# define installer name
OutFile "installer.exe"
# set desktop as install directory
InstallDir $DOCUMENTS
# default section start
Section
# define output path
SetShellVarContext all
SetOutPath $INSTDIR
# specify file to go in output path
File test.txt
# define uninstaller name
WriteUninstaller $INSTDIR\uninstaller.exe
#-------
# default section end
SectionEnd
# create a section to define what the uninstaller does.
# the section will always be named "Uninstall"
Section "Uninstall"
# Always delete uninstaller first
Delete $INSTDIR\uninstaller.exe
# now delete installed file
Delete $INSTDIR\test.txt
SectionEnd
SetShellVarContext does not affect the InstallDir attribute, you must manually set $InstDir:
Function .onInit
SetShellVarContext all
StrCpy $InstDir $Documents
FunctionEnd

Adding extra command to cpack - NSIS packager/installer

I want to package a project of mine for windows with CPack and NSIS using an already existing
GeneratorConfig.cmake file where I want to add an extra command that will copy an .ini file called myProject.ini into %APPDATA%/myProject/myProject.ini .
This is GeneratorConfig.cmake
SET(INSTALL_AN_ALREADY_EXISTING_DIR ".")
##########################################################################
## Begin NSIS Specific options
##------------------------------------------------------------------------
if(CPACK_GENERATOR MATCHES NSIS)
# Additional NSIS commands to uninstall start menu shortcuts
SET(CPACK_NSIS_DELETE_ICONS_EXTRA
"Delete \"$SMPROGRAMS\\$MUI_TEMP\\${PROJECT_NAME}.lnk\"
StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2
Delete \"$DESKTOP\\${PROJECT_NAME}.lnk\" ")
# The display name string that appears in the Windows Add/Remove Program control panel
SET(CPACK_NSIS_DISPLAY_NAME "${PROJECT_NAME} ${PROJECT_VER_MAJOR}.${PROJECT_VER_MINOR}")
SET(CPACK_NSIS_DISPLAY_NAME_SET "TRUE")
# Extra NSIS commands that will be added to the end of the install Section, after your
# install tree is available on the target system.
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\${PROJECT_NAME}.lnk\" \"$INSTDIR\\.\\bin\\${PROJECT_EXE} \" -previousworkingdir \"$INSTDIR\\.\\bin\\app_icon.ico\"
StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2
CreateShortCut \"$DESKTOP\\${PROJECT_NAME}.lnk\" \"$INSTDIR\\.\\bin\\${PROJECT_EXE} \" -previousworkingdir \"$INSTDIR\\.\\bin\\app_icon.ico\"
")
# Extra commands to fix permissions of bin/licenses folder
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}
ExecWait 'icacls \\\"$INSTDIR\\\\bin\\\\licenses\\\" /grant:r Users:\\\(OI\\\)\\\(CI\\\)\\\(F\\\)'
")
# A path to the executable that contains the installer icon.
SET(CPACK_NSIS_INSTALLED_ICON_NAME "${PROJECT__DIR}\\bin\\${PROJECT_EXE}")
# The default installation directory presented to the end user by the NSIS installer
SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
# Title displayed at the top of the installer
SET(CPACK_NSIS_PACKAGE_NAME "${PROJECT_NAME} ${PROJECT_VER_MAJOR}.${PROJECT_VER_MINOR}")
SET(CPACK_NSIS_PAGE_COMPONENTS " ")
SET(CPACK_NSIS_MUI_FINISHPAGE_RUN ${PROJECT_EXE})
endif(CPACK_GENERATOR MATCHES NSIS)
##------------------------------------------------------------------------
## End NSIS Specific options
##########################################################################
I tried to do this with the code below but this builds the package but doesn't
copy myProject.ini anywhere.
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"CreateDirectory \"$ENV{APPDATA}\\myProject\",
SetOutPath \"$ENV{APPDATA}\\myProject\",
File \"myProject.ini\"
")
Any help or suggestions will be appreciated.
From what i can tell you are using not enough '\' symbols.
To clarify: that string is evaluated twice, once by CMake and once by CPack, and each time substitutions such as \->\ and \" -> " occur. Please check generated nsis project for what the actual generated commands are and whether all '"' are correctly set.
To summarize: try using this:
SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
"CreateDirectory \\\"$ENV{APPDATA}\\\\myProject\\\",
SetOutPath \\\"$ENV{APPDATA}\\\\myProject\\\",
File \\\"myProject.ini\\\"
")

Obtain Current Users %APPDATA% Path and not Admins

I am looking to get the path to the current users %APPDATA% folder.
Note: I am aware of the variable $APPDATA BUT if you run your installer with RequestExecutionLevel admin then $APPDATA will point to the admins roaming folder and NOT the current user's app data folder.
I need to find out the current users %APPDATA% path so I can write files to their roaming directory. Does anyone know how I can find this out?
RequestExecutionLevel admin
Section "Main"
MessageBox MB_OK "AppData is: $APPDATA" # knowtice that its the path to the admins folder not the current user's
SectionEnd
The term "Current User" is ambiguous, do you mean:
The user you get from WTSQueryUserToken()? (WinLogon)
The user that the shell's taskbar is running as? (GetShellWindow())
The user (parent process) that started your setup process?
All of those can be different users if you are having fun with runas!
The comment from Harry Johnston is spot on and once you start mixing %ProgramFiles% and %AppData% and/or HKLM and HKCU your setup is broken in multi-user scenarios. What happens when a different user starts the application? They are not going to have your files in their %AppData%.
If the addin is installed/registered in a global location you can install the AppData "template" files in %ProgramFiles%, %CommonProgramFiles% or %ALLUSERSPROFILE% and when your addin runs as a specific user for the first time you copy the files to %AppData%.
Active Setup could be used as a alternative but it will probably require a log-off/log-on cycle.
If you cannot implement the delayed copy/install for some reason you are left with hacks like the UAC plugin which gives you some access to the user that started your installer...
Ok, thanks for the advice but I found a nice plugin that tells me the location of all User directories. I still cant figure out which user is currently logged in but I can figure out all non-admin users which is very useful.
!include "NTProfiles.nsi"
!macro HandleUserProfiles
!define NTProfilePaths::IgnoreLocal
!ifndef __UNINSTALL__
${EnumProfilePaths} HandleUserProfile
!else
${EnumProfilePaths} un.HandleUserProfile
!endif
!macroend
!macro HandleUserProfile prefix
Function ${prefix}HandleUserProfile
Pop $R9
!ifndef __UNINSTALL__
# Copy files to user dir
SetOutPath "$R9\AppData\Roaming\Autodesk\Revit\Addins\2013" # $APPDATA = C:\ProgramData
FILE /r "${INSTALLFILEDIR}\Addins\Revit_2013\myAddin.addin"
!else
Delete "$R9\AppData\Roaming\Autodesk\Revit\Addins\2013\myAddin.addin"
!endif
# Continue Enumeration
Continue:
Push ""
Return
# Stop Enumeration
Stop:
Push "~" # Any value other than an empty string will abort the enumeration
FunctionEnd
!macroend
!insertmacro HandleUserProfile ""
!insertmacro HandleUserProfile "un."

Permanent customized folder icons with InnoSetup in any computer

I have modified some folder icons and I am including these folders in my InnoSetup installation. The problem is that once my program is installed, my customized folder icons are gone and what I see is just the oldfashioned "yellow" Windows folder icons.
EDIT
The answer was provided by the user TLama. It worked in my computer at first. I had some problems with different Windows versions at different computers. I will write now my working code after having tried sucessfully in several computer systems.
Icons used:
Ico1.ico
Ico2.ico
Ico3.ico
Modified folder icons:
c:\FDR1
c:\FDR2\FDR3
Step 1:
I have used the software "Folder icon changer" to have my icon in place for the three folders I wanted changed. You may use any other free software too. After execution, a desktop.ini appeared in each of the newly changed icon folders. For instance, the FDR1 has the content:
[.Shellclassinfo]
Iconfile=F:\Resource\Icons\Ico1.ico
Iconindex= 0
Step 2:
I have then erased the path above and saved "Ico1.ico" into the directory "c:\FDR1" I had just modified :
[.Shellclassinfo]
Iconfile=Ico1.ico
Iconindex= 0
I did the same for the Ico2.ico (inside the FDR2) and the Ico3.ico (inside the FDR3). The "Icon1, 2 and 3" and "desktop.ini" file attributes were all set to hidden. But, it is important NOT to set the icon properties to "read only".
Step 3:
Inside Inno repeat TLama's suggestion.
#define OutputDirectory_1 "c:\FDR1"
#define OutputDirectory_2 "c:\FDR2"
#define OutputDirectory_3 "c:\FDR2\FDR3"
[Dirs]
Name: {#OutputDirectory_1}; Attribs: system
Name: {#OutputDirectory_2}; Attribs: system
Name: {#OutputDirectory_3}; Attribs: system
[Files]
Source: "c:\FDR1\Ico1.ico"; DestDir: {#OutputDirectory_1}; Attribs: hidden system
Source: "c:\FDR2\Ico2.ico"; DestDir: {#OutputDirectory_2}; Attribs: hidden system
Source: "c:\FDR2\FDR3\Ico3.ico"; DestDir: {#OutputDirectory_3}; Attribs: hidden system
Step 4:
Compile !
Now, your folder icons will permanently work in any computer and system !!
Your target folder should have either read only or system attribute configured. To create such folder you can use, like Miral mentioned, [Dirs] section and its attributes. This will have an advantage, that after you run the installation process, InnoSetup automatically notifies Shell about changes, so the folder icon will be changed without an extra notification function call.
; this is a defined preprocessor variable used to simplify the script
; management; this variable contains the path, where the icon will be
; applied (it's used twice in a script, so it's easier to manage that
; from one place)
#define OutputDirectory "d:\TargetDirectory"
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
OutputDir=userdocs:Inno Setup Examples Output
[Files]
; here you need to use "hidden" and "system" values in Attribs parameter
; to include into the Desktop.ini file hidden and system file attributes
Source: "Desktop.ini"; DestDir: {#OutputDirectory}; Attribs: hidden system
[Dirs]
; here you need to use either "readonly" or "system" value in Attribs parameter
; to setup to the output directory read only or system file directory attribute
Name: {#OutputDirectory}; Attribs: readonly
Important:
Don't forget that you have to compile the script using CTRL + F9 before running, whenever you change the content of your input Desktop.ini file as well as when you change the value of the preprocessor path variable (I've been missing this few times and then wondering about the setup package content).
In order to activate custom folder icons you have to programmatically set the "read-only" attribute of the folder containing the desktop.ini file. (You can't do this from Explorer, but you can via the command line and from Inno.)
[Dirs]
Name: {app}; Attribs: readonly
Note that the path inside the desktop.ini file must be valid on the user's filesystem; you may want to use an [Ini] entry to create or modify this file to suit the installation path.
(This doesn't actually make the folder read-only -- this attribute is treated differently on folders by Windows because only files can meaningfully be read-only.)

Copy Files in NSIS

I am using the following command to copy files.
After setting output path...
File "Documents\*"
This action works flawlessly. There are no issues coping the files in the Documents directory until...
if there is a copy of an existing file (with a different name) in the directory only the first instance of the file gets copied regardless of name.
How do I make it so it will copy ALL files regardless if they are copies of other files?
Correction/better explanation (maybe)
I apologize for the confusion. Allow me to try to restate the problem. The files being extracted by using the FILE command is the issue here. The files consists of original files and copies of the same files (only with a different name).
example: MyDocument.txt and copyOfMyDocument.txt and so on..
When the File command is applied, in order to be extract the files to the current output path, only the first instance of the file is extracted (either the copy or the original... but not both). Again, I am sorry for the confusing but this is the first time I've had to work with NSIS. I need to extract ALL files.
The easiest way to do this will be to put it in a different directory which you've created. Then, if you need to worry about renaming (as the commentators have noted your question doesn't make much sense), you can attack it file by file.
# Extract the files to a directory which can't exist beforehand
CreateDirectory $PLUGINSDIR\extracting
SetOutPath $PLUGINSDIR\extracting
File Documents\*
# Now go through file by file
FindFirst $0 $1 $OUTDIR\*
${While} $1 != ""
${If} ${FileExists} $DOCUMENTS\$1
# This still isn't infallible, of course.
Rename $DOCUMENTS\$1 $DOCUMENTS\$1.local-backup
${EndIf}
Rename $OUTDIR\$1 $DOCUMENTS\$1
FindNext $0 $1
${Loop}
FindClose $0
SetOutPath $INSTDIR # Or somewhere else
RMDir $PLUGINSDIR\extracting
(Note that that's using LogicLib.)
This doesn't become a very neat way of doing it though and if you can avoid it, do.
i thought i understood what you were after, until i started reading the responses; i'll go with my initial interpretation: given a directory named "Documents", with a bunch of files in it (what they're called, and their contents should not matter), you want an installer that will copy the files into some output directory. I've created a test installer for this scenario here, and it works for me. What am I missing in what you're after?

Resources