Sharing function that takes input variables between installer and uninstaller - nsis

How can I modifiy the below syntax such that my library function can be used in both the installer and the uninstaller? So far I have only managed to find an example for functions that do not take an input value. A good example of which can be found here:
http://nsis.sourceforge.net/Sharing_functions_between_Installer_and_Uninstaller
I haven't been able to work out the syntax should you want to pass varaibles and still utilise the function for both installer and uninstaller.
/* FILE: MyFunctionLibrary.nsh */
Function MyFunction
!define MyFunction"!insertmacro MyFunctionCall"
!macro MyFunctionCall _VAR1
Push "${_VAR1}"
Call MyFunction
!macroend
Exch $0
MessageBox MB_OK $0
Pop $0
FunctionEnd
/* FILE: MyInstallerScript.nsi */
/*...mui setup stuff...*/
!include "MyFunctionLibrary.nsh"
Section Install SEC01
${MyFunction} "install section"
SectionEnd
Section UnInstall SEC02
${MyFunction} "uninstall section"
SectionEnd
/*...other stuff...*/

There are two things going on here. You need to generate both functions and that is often done with a macro. You can also (optionally) provide a helper macro you use to call the function.
The most important step is creating the functions:
; --- MyFunctionLibrary.nsh ---
!macro DeclareMyFunction un
Function ${un}MyFunction
Exch $0
DetailPrint "MyFunction: parameter=$0"
Pop $0
FunctionEnd
!macroend
; --- MyInstallerScript.nsi ---
!insertmacro DeclareMyFunction ""
Section
Push "Hello World"
Call MyFunction
SectionEnd
!insertmacro DeclareMyFunction "un."
Section Uninstall
Push "Hello World"
Call un.MyFunction
SectionEnd
There is also a less common way you can use by providing just the functions code in the macro:
!macro MyFunctionCode
Exch $0
DetailPrint "MyFunction: parameter=$0"
Pop $0
!macroend
Function MyFunction
!insertmacro MyFunctionCode
FunctionEnd
Function un.MyFunction
!insertmacro MyFunctionCode
FunctionEnd
The macro that helps you call the function just needs to perform x number of Push's and Call YourFunction or un.YourFunction:
!macro DeclareMyFunction un
Function ${un}MyFunction
Exch $0
DetailPrint "MyFunction: parameter=$0"
Pop $0
FunctionEnd
!macroend
!define MyFunction "!insertmacro CallMyFunction"
!macro CallMyFunction Param1
Push "${Param1}"
!ifdef __UNINSTALL__
Call un.MyFunction
!else
Call MyFunction
!endif
!macroend
!insertmacro DeclareMyFunction ""
Section
${MyFunction} "Hello World"
SectionEnd
!insertmacro DeclareMyFunction "un."
Section Uninstall
${MyFunction} "Hello World"
SectionEnd
Some of the details can be abstracted away by using util.nsh:
!include Util.nsh
!macro MyFunction
Exch $0
DetailPrint "MyFunction: parameter=$0"
Pop $0
!macroend
!define MyFunction "!insertmacro CallMyFunction"
!macro CallMyFunction Param1
Push "${Param1}"
!insertmacro CallArtificialFunction MyFunction
!macroend
Section
${MyFunction} "Hello World"
SectionEnd
Section Uninstall
${MyFunction} "Hello World"
SectionEnd

Related

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 move NSD_OnBack function from the section in NSIS

I'm writing an NSIS script, in this using some sections to execute EXE files. on depending output, I need to go back from the section to other custom pages but here nsis is moving to another section even though keeping of NSD_OnBack function or just calling the particular function
I have tried below 2 methods.
${NSD_OnBack} "callbackfunction"
call callbackfunction
//Section started
Section "validation" VALIDATION
DetailPrint "Executing Validation"
File "Folder_name\Validation.exe"
nsExec::Exec '"$INSTDIR\Validation.exe" $arg1 $arg2 $arg3'
IfFileExists "$INSTDIR\Output.txt" pass fail
pass:
FileOpen $chk "$INSTDIR\Output.txt" r
FileRead $chk $1
MessageBox MB_OK|MB_ICONSTOP "Validation_Output : in 1 $1"
Push $1
Push "true"
Call StrContains
Pop $3
${If} $3 == "true"
call someotherfunction
${ELSE}
goto fail
${ENDIF}
FileClose $chk
Delete $chk
fail:
MessageBox MB_OK|MB_ICONSTOP "fail"
//Here this call is not working
${NSD_OnBack} "callbackfunction"
SectionEnd
Function callbackfunction
GetDlgItem $0 $HWNDPARENT 2
${IF} $portalname == "centralised"
${IF} $username == ""
call CentralisedPage
${ENDIF}
${ELSE}
${IF} $username == ""
call SetCustom
${ENDIF}
${ENDIF}
Functionend
I am expecting to move other page based on EXE results.
${NSD_OnBack} is a callback for nsDialogs custom pages and it is invoked when the user presses the back button on that page, it is not relevant here.
Ideally you should collect all information before you get to the InstFiles page but if you can't do that then I would recommend that you just show a custom page after the InstFiles page if required.
If you absolutely need to execute sections multiple times you can use more than one InstFiles page:
!include LogicLib.nsh
!include WinMessages.nsh
!include nsDialogs.nsh
!include Sections.nsh
!include MUI2.nsh
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_PRE Init1stStage
!insertmacro MUI_PAGE_INSTFILES
Page Custom MyCustomPageCreate
!define MUI_PAGE_CUSTOMFUNCTION_PRE Init2ndStage
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Var Needs2ndStage
Section "1st stage" SID_1
DetailPrint "1st stage"
MessageBox mb_yesno "Needs 2nd stage?" IDNO nope
StrCpy $Needs2ndStage 1
nope:
SectionEnd
Section "-2nd stage" SID_2
DetailPrint "2nd stage"
SectionEnd
Function Init1stStage
!insertmacro UnselectSection ${SID_2}
FunctionEnd
Function Init2ndStage
!insertmacro UnselectSection ${SID_1}
${IfThen} $Needs2ndStage = 0 ${|} Abort ${|}
FunctionEnd
Function MyCustomPageCreate
${IfThen} $Needs2ndStage = 0 ${|} Abort ${|}
!insertmacro SelectSection ${SID_2}
GetDlgItem $0 $hWndParent 1
SendMessage $0 ${WM_SETTEXT} "" "STR:C&ontinue"
GetDlgItem $0 $hWndParent 3
ShowWindow $0 0 ; Hide back
GetDlgItem $0 $hWndParent 2
EnableWindow $0 0 ; Disable cancel
!insertmacro MUI_HEADER_TEXT "Blah" "Blah blah blah"
nsDialogs::Create 1018
Pop $0
${NSD_CreateLabel} 0 0 100% 12u "Enter blah blah before you can enter the 2nd stage"
Pop $0
nsDialogs::Show
FunctionEnd

Add link to Abort page

As a follow-up to this answer, I'm trying to add the link after issuing Abort command, but for some reason it does not appear, no trace of it when viewed in Spy++.
The idea is to add the link above the progress bar, but somehow the macro does not work. Is there a reason for this that I'm missing and is it possible to add that link after calling Abort? I've read somewhere that Abort command can have different effect so I'm guessing this is one of it.
I've tried to make this sample script as concise as best I can and would greatly appreciate any help as I'm still learning NSIS.
!include "MUI2.nsh"
;--------------------------------
;General
ShowInstDetails hide
SetCompressor /SOLID lzma
;Request application privileges for Windows Vista
RequestExecutionLevel user
;--------------------------------
;Interface Configuration
!define MUI_ABORTWARNING
!define MANUAL_DOWNLOAD_TEXT "Automatic download not working? Click here to download manually."
;--------------------------------
;Macros
!macro AddDownloadLink yCoord
FindWindow $0 "#32770" "" $HWNDPARENT ; Find the inner dialog
System::Call 'USER32::CreateWindowEx(i0, t "STATIC", t "${MANUAL_DOWNLOAD_TEXT}", i${WS_CHILD}|${WS_VISIBLE}|${SS_NOTIFY}, i 1, i ${yCoord}, i 500, i 50, p $0, i 0x666, p 0, p 0)p.s'
Pop $0
SetCtlColors $0 0000ff transparent
CreateFont $1 "$(^Font)" "$(^FontSize)" "400" /UNDERLINE
SendMessage $0 ${WM_SETFONT} $1 1
GetFunctionAddress $1 fnLinkClicked
ButtonEvent::AddEventHandler 0x666 $1
!macroend
;--------------------------------
;Pages
!insertmacro MUI_PAGE_INSTFILES
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English"
;--------------------------------
;Installer Sections
Section
Var /global Filename
StrCpy $Filename "test100Mb.db"
Var /global DownloadUrl
StrCpy $DownloadUrl "http://speedtest.ftp.otenet.gr/files/$Filename"
!insertmacro AddDownloadLink 70
inetc::get /caption "Downloading package" $DownloadUrl "$Filename" /end
Pop $R0 ;Get the return value
StrCmp $R0 "OK" 0 dlfailed
Goto quit
dlfailed:
DetailPrint "Download failed: $R0 $DownloadUrl"
SetDetailsView show
Abort
!insertmacro AddDownloadLink 1
quit:
Quit
SectionEnd
Function fnLinkClicked
ExecShell "open" "$DownloadUrl"
FunctionEnd
Abort stops executing the section(s) code, you must do whatever you need to do before calling Abort.
Adding controls in a section can be problematic because it executes on a different thread and windows are tied to the thread that created them. If you need the window to stick around longer than the install thread you can create it as a hidden window in the instfiles page show callback and simply call ShowWindow in the section when you need to display it...

Added link to MUI_PAGE_INSTFILES does not show

I'm trying to create a downloader that also has a "Download manually" link, but the link does not seem to show.
I tried to follow instructions from this post but can't seem to make it work.
I'm copying the script here in case anyone can point out what I might be missing - I'm a noob in NSIS scripting, sorry.
!include "MUI2.nsh"
!define NAME "instfileslink"
Name "${NAME}"
OutFile "${NAME}.exe"
!define MUI_PAGE_CUSTOMFUNCTION_SHOW MyInstFilesShow
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Var hCtl_test_Link1
Section
Section
inetc::get /caption "Downloading package" "http://speedtest.ftp.otenet.gr/files/test100Mb.db" "test100Mb.db" /end
Pop $R0
StrCmp $R0 "OK" 0 dlfailed
Quit
dlfailed:
DetailPrint "Download failed: $R0"
Abort
SectionEnd
Function fnLinkClicked
ExecShell "open" "http://speedtest.ftp.otenet.gr/files/test100Mb.db"
FunctionEnd
Function MyInstFilesShow
${NSD_CreateLink} 120u 175u 100% 10u "Download manually"
Pop $hCtl_test_Link1
${NSD_OnClick} $hCtl_test_Link1 fnLinkClicked
FunctionEnd
You cannot use NSDialogs controls (${NSD_Create*}) outside a NSDialogs dialog!
You can use ChangeUI/MUI_UI to add controls to a built-in page or you can add the dynamically at run-time by manually creating a window. You need to use the ButtonEvent plug-in to catch the click events:
!include "MUI2.nsh"
!define MUI_PAGE_CUSTOMFUNCTION_SHOW MyInstFilesShow
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
!include nsDialogs.nsh ; For style defines
ShowInstDetails hide
Function MyInstFilesShow
FindWindow $0 "#32770" "" $HWNDPARENT ; Find the inner dialog
System::Call 'USER32::CreateWindowEx(i0, t "STATIC", t "Download manually", i${WS_CHILD}|${WS_VISIBLE}|${SS_NOTIFY}, i 100, i 200, i 300, i 50, p $0, i 0x666, p 0, p 0)p.s'
Pop $0
SetCtlColors $0 0000ff transparent
SendMessage $hwndparent ${WM_GETFONT} 0 0 $1
SendMessage $0 ${WM_SETFONT} $1 1
GetFunctionAddress $1 fnLinkClicked
ButtonEvent::AddEventHandler 0x666 $1
FunctionEnd
Function fnLinkClicked
ExecShell "open" "http://speedtest.ftp.otenet.gr/files/test100Mb.db"
FunctionEnd

NSIS unattended options

I want to build a NSIS-script, which has three section
section Main
section Minor
section Shared
Shared is invisible and would be installed, if Main or Minor is checked. If I start the installer, every section (Main, Minor) is checked.
Now it should be able to define the section (in silent install). What have I to change, to only install Main or Minor or Both?
Name "Test"
Outfile "Test.exe"
;RequestExecutionLevel ?
!include "Sections.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh" ;For GetOptions
Page Components "" "" EnforceSectionDependencies
Page InstFiles
Section /o "Main" SID_MAIN
DetailPrint Main
SectionEnd
Section /o "Minor" SID_MINOR
DetailPrint Minor
SectionEnd
Section "" SID_SHARED
DetailPrint Shared
SectionEnd
!macro CheckSectionSwitch sw sid
${GetOptions} $0 '${sw}' $9
${IfNot} ${Errors}
StrCpy $1 1
!insertmacro SelectSection ${sid}
${EndIf}
!macroend
Function .onInit
${GetParameters} $0
StrCpy $1 0 ;Any section swithes?
ClearErrors
!insertmacro CheckSectionSwitch '/Main' ${SID_MAIN}
!insertmacro CheckSectionSwitch '/Minor' ${SID_MINOR}
${If} $1 = 0
;Set defaults
!insertmacro SelectSection ${SID_MAIN}
!insertmacro SelectSection ${SID_MINOR}
${EndIf}
call EnforceSectionDependencies
FunctionEnd
Function EnforceSectionDependencies
!insertmacro UnselectSection ${SID_SHARED}
${If} ${SectionIsSelected} ${SID_MAIN}
${OrIf} ${SectionIsSelected} ${SID_MINOR}
!insertmacro SelectSection ${SID_SHARED}
${EndIf}
FunctionEnd
You should look at the Section management part of the documentation, notably the SectionSetFlags to change the sections selections.
Also, maybe that the How to control Section selections, while using SubSections & InstTypes example will be useful.

Resources