Archive: My understanding to date - Basic Installer


My understanding to date - Basic Installer
I hope this info can potentially help someone who is starting out. It's a culmination of help from many sources, including Cheryll, RedWine, and AfrowUK. Thanks to the other guys as well :) I'm bound to perhaps misrepresent a feature or element somehow, so please just post away with the correction, as the edit feature will not allow for late edits to the OP.

------------------------------------------------------

This is a list of functions that I think people will be able to make use of when trying to learn NSIS.

1.) User Account Type Check.

Function .onInit

UserInfo::GetAccountType
Pop $0
StrCmp $0 "Admin" 0 +3 ;
;MessageBox MB_OK 'You seem to be an admin!'
Goto done
StrCmp $0 "Power" 0 +3
MessageBox MB_OK 'You must be logged in as Administrator!'
Quit ;
StrCmp $0 "User" 0 +3
MessageBox MB_OK 'You must be logged in as Administrator!'
Quit ;
StrCmp $0 "Guest" 0 +3
MessageBox MB_OK 'You must be logged in as Administrator!'
Quit ;
MessageBox MB_OK "Unknown error (Perhaps unknown account type?)"
Quit ;

Win9x:
# This one means you don't need to care about admin or
# not admin because Windows 9x doesn't either
MessageBox MB_OK "(Checking Account Type) Error! This DLL can't run under Windows 9x!"

done:
This is obviously an NSIS function that will check the current user account type. Use the working example to check if the user is allowed to mess with your installer/uninstaller.
This should really go in your initialisation, note the example doesn't close the function, and that's simply because you'll want to put some other things in your initialisation too.


2.) Custom Page (Login Details)

!insertmacro MUI_INSTALLOPTIONS_EXTRACT_AS "Custom.ini" "Custom"
Functionend

LangString CUSTOM_TITLE ${LANG_ENGLISH} "Enter your Login Details."
LangString CUSTOM_SUBTITLE ${LANG_ENGLISH} " "

Page custom CustomPage MyCustomLeave

#==================================
Function CustomPage

!insertmacro MUI_HEADER_TEXT "$(CUSTOM_TITLE)" "$(CUSTOM_SUBTITLE)"
# Display the page.
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "Custom"

FunctionEnd
This is actually the section cut after the last. So the Functionend is the end of the initialisation.
This example assumes that you want your custom page to appear before your main sections. Notice that the 'Page custom' wants to run two functions, 'CustomPage' and 'MyCustomLeave'. The CustomPage function simply adds info to the header text and of course, displays the page (renders your .ini file).


Function MyCustomLeave

ReadINIStr $0 "Custom.ini" "Settings" "State"
StrCmp $0 0 extract ; Next button?
;Abort

extract:
# Get the user entered values. ---------------==============================
!insertmacro MUI_INSTALLOPTIONS_READ $Username "Custom" "Field 1" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $Password "Custom" "Field 2" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $Server "Custom" "Field 3" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $Autoupdate "Custom" "Field 4" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $AUpdatePath "Custom" "Field 5" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $DatabaseName "Custom" "Field 6" "State"

done:
FunctionEnd
I don't know everything (anything? ^^) about how the .ini files work, but it would seem that the .ini file can be read for the state of a button which is automatically added by NSIS, the next button.
The Leave function simply wants to do stuff when you continue past the page, which is the same as saying that you click the next button. The details that user's have input on our custom page now get extracted to variables.


WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "MySQLUser" $Username ;As per install instruction file.
WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "MySQLPass" $Password
WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "MySQLADDR" $Server
WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "AutoUpdatePath" $AUpdatePath
WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "MySQLDBName" $DatabaseName
WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "DBAccessMode" "1"
WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "Status" "1"

;MessageBox MB_OK '0 is off and 1 is on! :: $Autoupdate' -====== Check!

${If} $Autoupdate == "1" ;control was selected
WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "AutoUpdateEnable" "1"
${EndIf}
${If} $Autoupdate == "0"
WriteRegStr HKCU "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "AutoUpdateEnable" "0"
${EndIf}
This writing to registry is placed in the main section, which in install time, is just after the last file is copied. The logic ${If} statements assume that you have included the Logic library (!include 'LogicLib.nsh').
You'll notice a message box line that is commented out. Remember to use these to check on the different stages of install of certain elements.


3.) Uninstaller Planning(a)

WriteRegStr HKLM "Software\VB and VBA Program Settings\${PRODUCT_NAME}\Main" "InstPathHere" "$INSTDIR"
When using the uninstaller, to correctly identify the install path, we cannot use $INSTDIR if our uninstaller is not placed in the $INSTDIR at install time. This suggests and means that the $INSTDIR used in the uninstaller is based on the location of the uninst.exe or whatever your uninstaller is called.


4.) Deleting Files(risky)

RMDIR /r "$R0"
Firstly, this is not recommended by the majority here it seems, so I shan't encourage it much, but rather offer how and why and when it can be useful.
Having taken steps similar to the previous, the correct install path will remain in the registry and be able to be extracted accurately and used UNLESS a user:
1) changes that registry
2) renames the install folder manually
3) or steps to a similar effect.
Therefore, when using remove directory /r with your install path, the folder and everything in it will be deleted properly. Because this is potentially risky, given that the user takes to changing details which shouldn't matter a whole lot (usually appropriate naming prevents needs to rename, and messing with the registry is never a good idea unless one knows exactly what they want) you can use the preferred NSIS method of deleting all files within a folder, and then removing the directory without the '/r' parameter. The reason I don't use this, is because users are much more likely to add or subtract files from the INSTDIR than do any other changes, and also when there are updates, certain files will be added which may become part of the application while not becoming part of the uninstaller, which will by default only remove the files it knows should be there. Leaving you with a not-quite-uninstalled application.


---------------------

And that's all :)

Here are some guidelines I follow:
Include a version number of your installer in the registry. You may find it invaluable when writing future upgrades or hotfixes that can perform special logic for certain versions.

Use "SetCompress off" at the beginning of your installer while debugging, simply to speed up execution and compilation of the installer. Useful during phases of rapid testing and updates.

If you have a suite of products or components, divide them up into seperate *.nsh files so that you are not overwhelmed by a large amount of irrelevant code when working on one particular product. Each product is divided into macros such as ProductName_Section and ProductName_Oninit that can be placed in the master *.nsi where appropriate. This also allows for easy reordering of sections by only copying/pasting !insertmacro statements rather than entire sections.

Use ifndef headers in all *.nsh files to avoid multiple includes:


!ifdef MYUTILITIES_NSH_USAGE
;=========Usage Example========


!endif
!ifndef MYUTILITIES_NSH
!define MYUTILITIES_NSH
;code here

!endif


Name global vars with a leading _ underscore followed by the nsh filename if they are used only internally to the nsh script. Name global vars intended for access outside of the nsh script the same way but without the leading underscore:

_MyUtilities_SomethingInternal
MyUtilities_SomethingAvailableToPublic

Use the same convention for function names and macros to avoid conflicts with other's *.nsh files. However, macro parameters do not have to be named this way.

Use GetTempFileName to place a name in a variable and File /oname=$TempFileVar for all temporary files! It may seem painful at first, but if you don't and try to make up some output filename in a random location, then you may really feel the pain when your users start running into problems because you are overwriting existing files, or trying to write/execute in a location they don't have permissions to.

Avoid repetitive hard coded strings. Use defines or registers. If you have some "magic value" as a string or number and it is used in more than one place, put it in a define so that typos don't cause problems, maintenance is easy, and readability is improved:
!define ProductVersion "1.01"

Organize your directory structure of files you plan to deploy into a structure similar to that which they will be deployed to the user's machine. I often have one of these "structures" in a seperate subdirectory for every Section. I will further divide these up into files which are blatantly copied, and files which are only copied if not existing, and then ini files which are to be merged.

I then make use of the File /r command to allow me to include large numbers of files with very little code. Experiment thoroughly with the /r option to make sure you understand how it works.

Oh :D So useful, thankyou!