How to use REMOVE or REPAIR feature in NSIS script? - nsis

I have used nsis script for creating installer.When i run my installer second time with same name,REPAIR and REMOVE should be check and do the corresponding operation.I have find out my application already installed or not using following codes,
Function checkinstall
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\My app" "UninstallString"
IfFileExists $R0 +1 NotInstalled
call nsDialogpage
NotInstalled:
FunctionEnd
Function nsDialogpage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateRadioButton} 0 5u 100% 10u "Repair"
Pop $hwnd
${NSD_AddStyle} $hwnd ${WS_GROUP}
${NSD_OnClick} $hwnd ???
${NSD_CreateRadioButton} 0 25u 100% 56u "Remove"
Pop $hwnd
${NSD_OnClick} $hwnd ???
nsDialogs::Show
If the user select repair button it should overwrites existing installation path else uninstall existing installed and continue with new one.what am i need do to replace the (???) of the above code
page custom checkinstall
!insertmacro MUI_PAGE_DIRECTORY
My next page is Directory selection.so i need to call this page? How to achieve this?
1.How can i call un installer function if the user selects remove button?
Function un.Init, section /o -un.Main UNSEC000,section -un.post UNSE001
these are the un installer funtions.How can i call these functions? i have tried call method but it did not work.

You need to specify a callback function, like in the nsDialogs documentation, look for the nsDialogsPageLeave function in this example:
!include nsDialogs.nsh
!include LogicLib.nsh
Name nsDialogs
OutFile nsDialogs.exe
XPStyle on
Var Dialog
Var Label
Var Text
Page custom nsDialogsPage nsDialogsPageLeave
Page instfiles
Function nsDialogsPage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateLabel} 0 0 100% 12u "Hello, welcome to nsDialogs!"
Pop $Label
${NSD_CreateText} 0 13u 100% -13u "Type something here..."
Pop $Text
${NSD_OnChange} $Text nsDialogsPageTextChange
nsDialogs::Show
FunctionEnd
Function nsDialogsPageLeave
${NSD_GetText} $Text $0
MessageBox MB_OK "You typed:$\n$\n$0"
FunctionEnd
Function nsDialogsPageTextChange
Pop $1 # $1 == $ Text
${NSD_GetText} $Text $0
${If} $0 == "hello"
MessageBox MB_OK "right back at ya!"
${EndIf}
FunctionEnd
Section
DetailPrint "hello world"
SectionEnd

Related

Is there a way to link a part of text in DetailPrint in NSIS script to point to a file?

Example:
DetailPrint "Errors detected running post installation scripts. Click here to view the results."
Clicking on "here" should take user to specific file"
The log window is just a ListView control and it does not support this.
You can display a message to the user:
Section
; Post install steps...
MessageBox MB_YESNO|MB_ICONQUESTION "There was a problem, view results?" /SD IDNO IDNO skipresults
ExecShell "" "$Temp\InstallError.log"
skipresults:
SectionEnd
Or you can display a custom page with a link on it:
!include nsDialogs.nsh
!include LogicLib.nsh
Page Directory
Page InstFiles
Page Custom myPostFailureCreate
Var PostFailure
Function OpenPostReport
ExecShell ...
FunctionEnd
Function myPostFailureCreate
${If} $PostFailure = 0
Abort
${EndIf}
nsDialogs::Create 1018
Pop $0
${NSD_CreateLabel} 5u 0 80% 12u "Install scripts failed!"
Pop $0
${NSD_CreateLink} 5u 13u 80% 12u "View report"
Pop $1
${NSD_OnClick} $1 OpenPostReport
nsDialogs::Show
FunctionEnd
Section
; Post install steps...
${If} ${Errors}
StrCpy $PostFailure 1
${EndIf}
SectionEnd

${NSD_GetText} always returns the empty string

Per the manual, I should be able to get the text of a text control with code like this:
${NSD_GetText} $TextBox $0
MessageBox MB_OK "You typed:$\n$\n$0"
I always get the empty string out of this call. In the code below, the text box shows "correct" but the details always show Contents:; if I comment the call to ${NSD_GetText}, I get Contents: wrong.
!include nsDialogs.nsh
!include LogicLib.nsh
Var Dialog
Var TextBox
Page custom nsDialogsPage nsDialogsPageLeave
Page instfiles
Function nsDialogsPage
StrCpy $0 "wrong"
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateText} 0 12u 93% 12u "correct"
Pop $TextBox
nsDialogs::Show
FunctionEnd
Function nsDialogsPageLeave
FunctionEnd
Section
${NSD_GetText} $TextBox $0
DetailPrint "Contents: $0"
SectionEnd
So I thought maybe the control didn't exist when I was trying to print its contents, and tried updating the text as it was typed into the control; that didn't help. It's implausible that NSIS is broken in this way, so what am I doing wrong?
!include nsDialogs.nsh
!include LogicLib.nsh
Var Dialog
Var TextBox
Var Text
Page custom nsDialogsPage nsDialogsPageLeave
Page instfiles
Function nsDialogsPage
StrCpy $0 "wrong"
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateText} 0 12u 93% 12u "correct"
Pop $TextBox
${NSD_OnChange} $TextBox UpdateText
nsDialogs::Show
FunctionEnd
Function nsDialogsPageLeave
FunctionEnd
Function UpdateText
${NSD_GetText} $TextBox $Text
FunctionEnd
Section
DetailPrint "Contents: $Text"
SectionEnd
You are correct, the control does not exist in the Section so you have to get the contents while you are on the custom page.
Your second example should work correctly if the user changes the text but not if they don't because the change event would not fire.
You normally just read the content in the page leave callback:
Var Dialog
Var TextBox
Var Text
!include LogicLib.nsh
!include nsDialogs.nsh
Page custom nsDialogsPage nsDialogsPageLeave
Page instfiles
Function nsDialogsPage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateText} 0 12u 93% 12u "correct"
Pop $TextBox
nsDialogs::Show
FunctionEnd
Function nsDialogsPageLeave
${NSD_GetText} $TextBox $Text
FunctionEnd
Section
DetailPrint "Contents: $Text"
SectionEnd

NSIS: How do I allow multiple user choice screens that select the sections to install?

I'm attempting to create an installer that asks the user a series of questions to decide which components to install. Each choice (potentially) influences the available options in later choices (else I would just do a normal components page—I don't want to give the user invalid options).
How do I accomplish such a thing? If I just use the components page, all the options are shown, some combinations of which are completely invalid. I don't want to let the user select those. Is it possible to leave out the components page?
Here's a minimal working example of what I'm trying. (Sorry it's long, I couldn't really simplify the dialog code.)
!include nsDialogs.nsh
!include Sections.nsh
Name "mwe"
OutFile "mwe.exe"
InstallDir C:\mwe
Var hwnd
Var Level1Opt
Page custom SelectLevel1Opt ProcessLevel1
Function SelectLevel1Opt
nsDialogs::Create 1018
pop $hwnd
${NSD_CreateLabel} 0 0 100% 12u "Please select level 1 option"
Pop $hwnd
${NSD_CreateRadioButton} 10% 12u 100% 12u "Level 1 A"
Pop $hwnd
nsDialogs::SetUserData $hwnd "Level 1 A"
${NSD_OnClick} $hwnd SetLevel1
${NSD_CreateRadioButton} 10% 24u 100% 12u "Level 1 B"
Pop $hwnd
nsDialogs::SetUserData $hwnd "Level 1 B"
${NSD_OnClick} $hwnd SetLevel1
nsDialogs::Show
FunctionEnd
Function SetLevel1
Pop $hwnd
nsDialogs::GetUserData $hwnd
Pop $Level1Opt
MessageBox MB_OK "Selected: $Level1Opt"
FunctionEnd
Function ProcessLevel1
${If} $Level1Opt == "Level 1 A"
!insertmacro SelectSection Level1A
${ElseIf} $Level1Opt == "Level 1 B"
!insertmacro SelectSection Level1B
${EndIf}
FunctionEnd
Page directory
Page instfiles
Section ""
MessageBox MB_OK "Common Install"
SectionEnd
Section /o "" Level1A
MessageBox MB_OK "Level 1 A"
SectionEnd
Section /o "" Level1B
MessageBox MB_OK "Level 1 B"
SectionEnd
No matter what I choose, neither Level1A nor Level1B sections get run. The selection from the dialog is correctly detected in the handler and the post function. However, selecting the sections isn't causing them to run. Even if I add a components page, neither of them is selected.
I looked in Selection.nsh, and the example it refers to (one-section.nsi) doesn't really do what I want, because it uses the components page. (I also don't understand quite how it works.)
What am I doing wrong? In what way am I misunderstanding the way NSIS is supposed to work?
As idleberg says, the correct syntax is !insertmacro SelectSection ${Level1A} but you get a warning because the section id is not defined until after its Section instruction in your .nsi. You need to move the functions that use ${Level1A} below the sections in your source code:
!include nsDialogs.nsh
!include Sections.nsh
Page custom SelectLevel1Opt ProcessLevel1
Page instfiles
Section ""
MessageBox MB_OK "Common Install"
SectionEnd
Section /o "a" Level1A
MessageBox MB_OK "Level 1 A"
SectionEnd
Section /o "b" Level1B
MessageBox MB_OK "Level 1 B"
SectionEnd
Var hInnerDialog
Var hL1A
Var hL1B
Function SelectLevel1Opt
nsDialogs::Create 1018
pop $hInnerDialog
${NSD_CreateLabel} 0 0 100% 12u "Please select level 1 option"
Pop $0
${NSD_CreateRadioButton} 10% 12u 100% 12u "Level 1 A"
Pop $hL1A
nsDialogs::SetUserData $hL1A ${Level1A} ; Only used by the generic function
${NSD_CreateRadioButton} 10% 24u 100% 12u "Level 1 B"
Pop $hL1B
nsDialogs::SetUserData $hL1B ${Level1B} ; Only used by the generic function
nsDialogs::Show
FunctionEnd
Function ProcessLevel1
${NSD_GetState} $hL1A $1
${If} $1 <> ${BST_UNCHECKED}
!insertmacro SelectSection ${Level1A}
!insertmacro UnselectSection ${Level1B}
${Else}
!insertmacro SelectSection ${Level1B}
!insertmacro UnselectSection ${Level1A}
${EndIf}
FunctionEnd
The ProcessLevel1 function can also be implemented as a loop if there are many radio buttons:
Function ProcessLevel1
StrCpy $0 ""
loop:
FindWindow $0 "${__NSD_RadioButton_CLASS}" "" $hInnerDialog $0
System::Call "USER32::GetWindowLong(p$0,i${GWL_STYLE})i.r1"
IntOp $1 $1 & ${BS_AUTORADIOBUTTON}
${If} $1 = ${BS_AUTORADIOBUTTON} ; Is it a auto radio button?
nsDialogs::GetUserData $0 ; Get the section id
Pop $2
${NSD_GetState} $0 $1
${If} $1 <> ${BST_UNCHECKED}
!insertmacro SelectSection $2
${Else}
!insertmacro UnselectSection $2
${EndIf}
${EndIf}
IntCmp $0 0 "" loop loop
FunctionEnd

Making a password work

So what you gave me works great.
Now the question is how do I set it so the password page has a message on it asking for you to inter your password This is what I have so far.
Function leave
${NSD_GetText} $hwnd $0
${If} $0 != "web9^root"
MessageBox MB_OK "Please enter a valid security code"
Abort
${EndIf}
FunctionEnd
Function pre
nsDialogs::Create 1018
Pop $dialog
${NSD_CreatePassword} 0 0 50% 8% ""
Pop $hwnd
nsDialogs::show
FunctionEnd
======================
so i have the following script set up to run,
!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
Page Custom pre
var dialog
var hwnd
Function pre
nsDialogs::Create 1018
Pop $dialog
${NSD_CreatePassword} 0 0 50% 8% "web9^root"
Pop $hwnd
SendMessage $hwnd ${EM_SETPASSWORDCHAR} 149 0 # 149 = medium dot
nsDialogs::Show
FunctionEnd
Section ""
SectionEnd
How do I make it so that if you do not know the password it will not let you pass. I also need to know who to set it so that the password is not already in the text feild. Can you all help me with this
you will have to add a leave function for that nsdialogs page...
Page custom pre leave
...and there you verify the password...
Function leave
${NSD_GetText} $hwnd $0
${If} $0 != "yourPassword"
MessageBox MB_OK "Wrong password"
Abort
${EndIf}
FunctionEnd
if you don't want the password in that field, just leave it empty when creating the dialog!

Retain data on custom pages when back button is pressed

Incase of custom pages in NSIS script is there any way to retain the data entered by user when back button is pressed (when the installer is running)?
There are a couple of ways to do this. Either way you need to store your data in globals.
1) Use a "Leave" function.
Page custom EnterCustom LeaveCustom
; Create two variables for each value/widget pair
Var Text
Var TextWidget
Var Check
Var CheckWidget
Function EnterCustom
nsDialogs::Create 1018
Pop $0
${NSD_CreateText} 0 0 80u 12u $Text
Pop $TextWidget
${NSD_CreateCheckBox} 0 26u 80u 12u "Check this box"
Pop $CheckWidget
${NSD_SetState} $CheckWidget $Check
nsDialogs::Show
FunctionEnd
Function LeaveCustom
${NSD_GetText} $TextWidget $Text
${NSD_GetState} $CheckWidget $Check
FunctionEnd
The only problem with this method is that LeaveCustom only gets called if you hit the next button. So if you edit the fields then click the Back button your changes are lost. The changes are however saved if you go forward then come back.
2) Use the OnChange callback.
This is a little more complicated but solves the problem with the previous method.
Page custom EnterCustom
Var Initialized
; Create two variables for each value/widget pair
Var Text
Var TextWidget
Var Check
Var CheckWidget
Function EnterCustom
nsDialogs::Create 1018
Pop $0
${If} $Initialized != "True"
; Set defaults for all your values here
StrCpy $Text "Initial Value"
StrCpy $Check ${BST_UNCHECKED}
StrCpy $Initialized "True"
${EndIf}
; Create and configure all of your widgets
${NSD_CreateText} 0 0 80u 12u $Text
Pop $TextWidget
${NSD_OnChange} $TextWidget OnTextChange
${NSD_CreateCheckBox} 0 26u 80u 12u "Check this box"
Pop $CheckWidget
${NSD_SetState} $CheckWidget $Check
${NSD_OnClick} $CheckWidget OnCheckClick
nsDialogs::Show
FunctionEnd
; Create a callback function for each Widget
Function OnTextChange
Pop $0 ; Widget handle is on stack
${NSD_GetText} $TextWidget $Text
FunctionEnd
Function OnCheckClick
Pop $0 ; Widget handle is on stack
${NSD_GetState} $CheckWidget $Check
FunctionEnd
Some widgets, e.g. RadioButtons and CheckBoxes, use the OnClick function instead. Also the ComboBox does not work well with this method. However, a DropList, which does not seem to be documented, can usually replace it and works fine.
Radio buttons are also a little tricky because only the click callback for the selected button is called. I solved this by updating all of the radio button values in each radio button click callback.
Messy/tedious but it works.
I know it's an old question, but I've landed here from my googling.
You can use NSD_OnBack (or call nsDialogs::OnBack directly) to set OnBack callback.
Here is the code snipet:
Function portsSelectionPage
nsDialogs::Create 1018
Pop $0
${NSD_CreateNumber} 70u 0 40u 12u $TomcatPort
Pop $TomcatPortHWND
${NSD_CreateNumber} 70u 14u 40u 12u $PostgresPort
Pop $PostgresPortHWND
nsDialogs::Show
${NSD_OnBack} "portsSelectionPageLeave"
FunctionEnd
Function portsSelectionPageLeave
${NSD_GetText} $TomcatPortHWND $TomcatPort
${NSD_GetText} $PostgresPortHWND $PostgresPort
FunctionEnd
You can store data in a global variable, or in a .ini in $pluginsdir
!include nsDialogs.nsh
!include LogicLib.nsh
Name nsDialogs
OutFile nsDialogs.exe
XPStyle on
Var Dialog
Var Label
Var Text
Var Text_State
Var Checkbox
Var Checkbox_State
Page custom nsDialogsPage nsDialogsPageLeave
Page license
Page instfiles
Function .onInit
StrCpy $Text_State "Type something here..."
FunctionEnd
Function nsDialogsPage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateLabel} 0 0 100% 12u "Hello, welcome to nsDialogs!"
Pop $Label
${NSD_CreateText} 0 13u 100% 12u $Text_State
Pop $Text
${NSD_CreateCheckbox} 0 30u 100% 10u "&Something"
Pop $Checkbox
${If} $Checkbox_State == ${BST_CHECKED}
${NSD_Check} $Checkbox
${EndIf}
# alternative for the above ${If}:
#${NSD_SetState} $Checkbox_State
nsDialogs::Show
FunctionEnd
Function nsDialogsPageLeave
${NSD_GetText} $Text $Text_State
${NSD_GetState} $Checkbox $Checkbox_State
FunctionEnd
Section
DetailPrint "hello world"
SectionEnd
more information http://nsis.sourceforge.net/Docs/nsDialogs/Readme.html#step-memory

Resources