NSIS Install like in TeamViewer (Portable mode option) - installation

What I basically want is TeamViewer-like first page for my NSIS installer with the following options:
3 radio buttons for: Run Only; Install for current user; Install for all users (requires restart with admin rights).
A label for license like in TeamViewer (i.e. no actual EULA page, only a link to it in the footer).
A button that can change text, i.e. Accept and Run or Accept and Install.
I cannot figure out how to do it easily in terms of UI and in terms of control flow.
Also I need the ability to restart the installer if user decides to install program for all users (i.e. I guess there should be a detectable command line switch, so that if present installer will automatically assume 3rd install type).
A screenshot of a sample UI as requested:
A sample NSIS template would be greatly appreciated.
Thanks.

...
RequestExecutionLevel user
!include LogicLib.nsh
!include nsDialogs.nsh
!include FileFunc.nsh
!include MUI2.nsh
Var mode
Var modeRadioRun
Var modeRadioInstCU
Var modeRadioInstLM
Function OnRadioChange
GetDlgItem $1 $hwndparent 1 ; Find Install/Next button
${NSD_GetState} $modeRadioRun $0
${If} $0 = ${BST_CHECKED}
${NSD_SetText} $1 "Accept && Run"
${Else}
${NSD_SetText} $1 "Accept && Install"
${EndIf}
FunctionEnd
Function ModePageCreate
!insertmacro MUI_HEADER_TEXT "Welcome to blah" "blah blah"
${GetParameters} $0
ClearErrors
${GetOptions} "$0" "/ELEVATEDINSTALL" $0
${IfNot} ${Errors}
UserInfo::GetAccountType
Pop $0
${If} $0 == "Admin"
StrCpy $mode 1
Abort ; Skip page and start installing
${Else}
MessageBox mb_iconstop "Admin rights required!"
${EndIf}
${EndIf}
nsDialogs::Create 1018
Pop $0
${NSD_CreateRadioButton} 30u 20u 50% 12u "Run"
Pop $modeRadioRun
${NSD_OnClick} $modeRadioRun OnRadioChange
${NSD_CreateRadioButton} 30u 40u 50% 12u "Install for current user"
Pop $modeRadioInstCU
${NSD_OnClick} $modeRadioInstCU OnRadioChange
${NSD_CreateRadioButton} 30u 60u 50% 12u "Install for all users"
Pop $modeRadioInstLM
${NSD_OnClick} $modeRadioInstLM OnRadioChange
${NSD_CreateLink} 20u -14u 50% 12u "License"
Pop $0
${NSD_OnClick} $0 ShowLicense
${NSD_Check} $modeRadioRun
call OnRadioChange ; Trigger button change
nsDialogs::Show
FunctionEnd
Function ModePageLeave
${NSD_GetState} $modeRadioRun $0
${NSD_GetState} $modeRadioInstCU $1
${If} $0 = ${BST_CHECKED}
InitPluginsDir
SetOutPath $pluginsdir
File "myapp.exe"
ExecWait '"$pluginsdir\myapp.exe"'
SetOutPath $temp ; Don't lock $pluginsdir
Quit
${ElseIf} $1 = ${BST_CHECKED}
StrCpy $mode 0
${Else}
StrCpy $mode 1
UserInfo::GetAccountType
Pop $0
${If} $0 != "Admin"
ExecShell "runas" '"$exepath"' "/ELEVATEDINSTALL"
Quit
${EndIf}
${EndIf}
FunctionEnd
Function ShowLicense
ExecShell "" "http://example.com/license"
FunctionEnd
Page Custom ModePageCreate ModePageLeave
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Section
${If} $mode > 0
SetShellVarContext all
StrCpy $InstDir "$ProgramFiles\MyApp"
${Else}
SetShellVarContext current
StrCpy $InstDir "$LocalAppData\Programs\MyApp"
${EndIf}
SetOutPath $InstDir
File myapp.exe
CreateShortcut "$SMPrograms\MyApp.lnk" "$InstDir\myapp.exe"
WriteUninstaller "$InstDir\Uninst.exe"
WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\MyAppGuid" "UninstallString" '"$InstDir\Uninst.exe"'
WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\MyAppGuid" "DisplayName" "MyApp blah blah"
SectionEnd
Section Uninstall
; Todo: Remove files and registry entries (You should write to a .ini in $InstDir so you know if it was a per user or machine install)
RMDir "$InstDir"
SectionEnd
You might want to edit the base UI to make the install button larger with Resource Hacker (on one of the files in NSIS\Contrib\UIs) and in the script use ChangeUI to apply.

Related

Cant understand why ${NSD_GetState} $Checkbox is not working

Can anyone explain to me, why ${NSD_GetState} $Checkbox is not working ?
I lost about 4 hours trying to find out what is wrong. I tried diffrent variants but in THIS script they are not working.
Actualy it is my first attempt to make a nsis installer, so I even dont know where should I look for mistake or I just dont understand the logic of this language.
From Russia with love :)
And sorry for my bad english
!define NAME "Simple LiveUSB installer"
!define FILENAME "USB"
!define VERSION "v0.1"
Name "${NAME} ${VERSION}"
OutFile "${FILENAME}.exe"
SetCompressor LZMA
ShowInstDetails hide
XPStyle on
!include MUI2.nsh
!include FileFunc.nsh
!include nsDialogs.nsh
;!include LogicLib.nsh
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\HEADER2.bmp"
!define MUI_HEADERIMAGE_BITMAP_NOSTRETCH
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\media-floppy.ico"
Page custom drivePage
Page custom Var123
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "Russian"
LangString DrivePage_Title ${LANG_RUSSIAN} "TEXT"
LangString DrivePage_Title2 ${LANG_RUSSIAN} "TEXT"
LangString DrivePage_Text ${LANG_RUSSIAN} "TEXT"
LangString DrivePage_Text2 ${LANG_RUSSIAN} "Format"
LangString DrivePage_Input ${LANG_RUSSIAN} "Choose"
Var Label
Var Label2
Var Checkbox
;Var Checkbox_State
Var DestDriveHW
Var DestDrive
# Functions #######################################
Function drivePage
!insertmacro MUI_HEADER_TEXT $(DrivePage_Title) $(DrivePage_Title2)
nsDialogs::Create 1018
${If} $DestDrive == ""
GetDlgItem $6 $HWNDPARENT 1
EnableWindow $6 0
${EndIf}
${NSD_CreateLabel} 0 0 100% 160 $(DrivePage_Text)
Pop $Label
${NSD_CreateLabel} 10 182 100% 15 $(DrivePage_Input)
Pop $Label2
${NSD_CreateDroplist} 10 200 13% 20 ""
Pop $DestDriveHw
${NSD_OnChange} $DestDriveHw db_select.onchange
${GetDrives} "FDD" driveListFiller
${If} $DestDrive != ""
${NSD_CB_SelectString} $DestDriveHw $DestDrive
${EndIf}
${NSD_CreateCheckbox} 80 203 100% 10u $(DrivePage_Text2)
Pop $Checkbox
${If} $Checkbox_State == ${BST_CHECKED}
${NSD_Check} $Checkbox_State
${EndIf}
nsDialogs::Show
FunctionEnd
Function db_select.onchange
Pop $DestDriveHw
${NSD_GetText} $DestDriveHw $0
StrCpy $DestDrive "$0"
GetDlgItem $6 $HWNDPARENT 1
EnableWindow $6 1
FunctionEnd
Function driveListFiller
SendMessage $DestDriveHw ${CB_ADDSTRING} 0 "STR:$9"
Push 1
FunctionEnd
Function Var123
Pop $Checkbox
MessageBox mb_ok "FIN Checkbox_State=$Checkbox_State Checkbox=$Checkbox"
${NSD_GetState} $Checkbox $0
${If} $0 <> 0
MessageBox mb_ok "Custom checkbox was checked... N=$0"
${EndIf}
${If} $0 == 0
MessageBox mb_ok "Custom checkbox ZERO... N=$0"
${EndIf}
Functionend
# Section #######################################
Section "" main
InitPluginsDir
File /oname=$PLUGINSDIR\syslinux.cfg "${NSISDIR}\plugins\syslinux.cfg"
File /oname=$PLUGINSDIR\syslinux.exe "${NSISDIR}\plugins\syslinux.exe"
File /oname=$PLUGINSDIR\nsExec.dll "${NSISDIR}\plugins\nsExec.dll"
StrCpy $R0 $DestDrive -1
;ExpandEnvStrings $0 %COMSPEC%
;nsExec::Exec '"$0" /c echo. | format $R0 /q /x /v:LiveUSB /fs:fat32'
nsExec::Exec '$PLUGINSDIR\syslinux.exe -maf -d boot\syslinux $R0'
;SendMessage $Checkbox ${BM_GETSTATE} 0 0 $0
;${If} $0 != 0
; MessageBox MB_OK checked!
;${EndIf}
;Pop $Checkbox
;MessageBox mb_ok "FIN Checkbox_State=$Checkbox_State Checkbox=$Checkbox"
;${NSD_GetState} $Checkbox $0
;${If} $0 <> 0
; MessageBox mb_ok "Custom checkbox was checked... N=$0"
;${EndIf}
CopyFiles $PLUGINSDIR\syslinux.cfg "$R0\syslinux.cfg"
SectionEnd
There is so much unrelated code in your example that it is a bit hard to understand what you really want to do.
If you want to retrieve the state of a checkbox on another page then you must save the state because the checkbox control is destroyed when you leave the page.
!include nsDialogs.nsh
Var hwndCheckbox
Var CheckboxState
Page Custom myPageWithCombobox myPageWithComboboxLeaveCallback
Page Custom myPageWithComboState
Function .onInit
StrCpy $CheckboxState ${BST_CHECKED} ; Set the initial state. This is optional but useful if you use $CheckboxState in a section when the installer is silent
FunctionEnd
Function myPageWithCombobox
nsDialogs::Create 1018
Pop $0
${NSD_CreateCheckbox} 80 203 100% 10u $(DrivePage_Text2)
Pop $hwndCheckbox
${NSD_SetState} $hwndCheckbox $CheckboxState ; Reuse the existing state so it is correct when the back button is used
nsDialogs::Show
FunctionEnd
Function myPageWithComboboxLeaveCallback
${NSD_GetState} $hwndCheckbox $CheckboxState ; Save the state so we can retrieve it on the next page
FunctionEnd
Function myPageWithComboState
nsDialogs::Create 1018
Pop $0
${NSD_CreateLabel} 0 0 100% 160 CheckboxState=$CheckboxState
Pop $0
nsDialogs::Show
FunctionEnd

Getting a path for each component in NSIS

I'm trying to create an installer with NSIS that installs 3 different components to three different paths. I want the users to select/confirm each of them, but they should only be asked if they've selected the relevant component.
Since pages can't appear in sections, I'm at a loss how to do this
Any suggestions?
Thanks in advance :-)
You can use multiple directory pages:
!include LogicLib.nsh
InstallDir $ProgramFiles32\Foo\Bar
Var Comp1Path
Var Comp2Path
Page Components
PageEx Directory
DirText "Blah blah 1"
DirVar $Comp1Path
PageCallbacks Comp1Pre
PageExEnd
PageEx Directory
DirText "Blah blah 2"
DirVar $Comp2Path
PageCallbacks Comp2Pre
PageExEnd
Page InstFiles
Section /o Comp1 SID_C1
DetailPrint "Installing Comp1 to $Comp1Path"
SectionEnd
Section Comp2 SID_C2
DetailPrint "Installing Comp2 to $Comp2Path"
SectionEnd
Function Comp1Pre
StrCpy $Comp1Path $InstDir\Comp1
${IfNot} ${SectionIsSelected} ${SID_C1}
Abort ; Skipping this page
${EndIf}
FunctionEnd
Function Comp2Pre
StrCpy $Comp2Path $InstDir\Comp2
${IfNot} ${SectionIsSelected} ${SID_C2}
Abort
${EndIf}
FunctionEnd
; In this example the next button on the components page might be the last page before InstFiles so we have to update the button text
!include WinMessages.nsh
Function .onSelChange
GetDlgItem $1 $hwndParent 1
${If} ${SectionIsSelected} ${SID_C1}
${OrIf} ${SectionIsSelected} ${SID_C2}
SendMessage $1 ${WM_SETTEXT} 0 "STR:$(^NextBtn)"
${Else}
SendMessage $1 ${WM_SETTEXT} 0 "STR:$(^InstallBtn)"
${EndIf}
FunctionEnd
Another alternative would be to create a custom page with nsDialogs and just disable or hide the text fields the user does not need to confirm...

Disable two mutually exclusive Sections when SectionGroup unchecked?

I have an NSIS installer script with two SectionGroups. Each SectionGroup has two Sections, which are mutually exclusive.
I need to have the user able to uncheck the SectionGroup, which unchecks and disables ("grays-out") the Sections. Basically the user can choose to install one of the Sections, or none. On top of that, the second SectionGroup must be disabled if the first SectionGroup is unchecked.
I'm using this script as reference to control the mutually exclusive Sections: http://nsis.sourceforge.net/Mutually_Exclusive_Sections
Thanks in advance.
Your disable/gray requirement does not make sense. If you click the section group again to enable the "child sections", which of the two gets checked? And you would only be able to get out of the disabled state by clicking the group itself so I did not implement it.
In general, anything beyond simple radio button behavior gets complicated quickly. If you are using the 3.0 alpha version of NSIS then .onSelChange will actually tell you which item that changed which would make this less complicated...
Page Components "" SaveState
Page Instfiles
!include sections.nsh
!include logiclib.nsh
SectionGroup /e "Grp1" GRP_1
Section "A" SEC_A
SectionEnd
Section /o "B" SEC_B
SectionEnd
SectionGroupEnd
SectionGroup /e "Grp2" GRP_2
Section /o "C" SEC_C
SectionEnd
Section "D" SEC_D
SectionEnd
SectionGroupEnd
; Remove comments for a different behavior
Function SaveState
!macro SaveSel id var
SectionGetFlags ${id} ${var}
IntOp ${var} ${var} & ${SF_SELECTED}
!macroend
#SectionGetFlags ${GRP_1} $R0
!insertmacro SaveSel ${SEC_A} $R1
!insertmacro SaveSel ${SEC_B} $R2
#SectionGetFlags ${GRP_2} $R3
!insertmacro SaveSel ${SEC_C} $R4
!insertmacro SaveSel ${SEC_D} $R5
FunctionEnd
Function .onSelChange
!macro OneOfTwoItemsInAGroup gid gv i1 v1 i2 v2
#SectionGetFlags ${gid} $0
!insertmacro SaveSel ${i1} $1
!insertmacro SaveSel ${i2} $2
${If} $1 <> 0
${AndIf} $2 <> 0
StrCpy $1 ${i1}
${IfThen} ${v1} = 0 ${|} StrCpy $1 ${i2} ${|}
!insertmacro UnselectSection $1
/*${If} ${gv} = $0
!insertmacro UnselectSection ${gid}
${EndIf}*/
${EndIf}
!macroend
!insertmacro OneOfTwoItemsInAGroup ${GRP_1} $R0 ${SEC_A} $R1 ${SEC_B} $R2
!insertmacro OneOfTwoItemsInAGroup ${GRP_2} $R3 ${SEC_C} $R4 ${SEC_D} $R5
Call SaveState
FunctionEnd

Implement dependency check with command line params in NSIS scripts

My goal is to know if you use any check or code snipp to determine if ther is some dependecy when passing NSIS command line params to copiled setup using Silent installation ( /S param ).
The NSIS sample: http://nsis.sourceforge.net/Get_command_line_parameter_by_name
For example, If I have three params: Setup.exe /S param1="" param2="" param3=""
How to check the following secanrio:
${if} <Param1 is passed to Setup.exe>
<Param2 must ALSO be passed to Setup.exe>
${else}
<Error message notifiing that Param1 is present, but dependent Param2 param is missing in CMD parameters>
Thank you!
I really hope you will share at least code snipp ... if not whole functional code.
outfile test.exe
requestexecutionlevel user
silentinstall silent ;always force silent in this sample
!include LogicLib.nsh
!include FileFunc.nsh
Function StripOptPrefix
Exch $0
Push $1
StrCpy $1 $0 1
${If} $1 == "="
${OrIf} $1 == ":"
StrCpy $0 $0 "" 1
${EndIf}
Pop $1
Exch $0
FunctionEnd
!macro StripOptPrefix var
Push ${var}
call StripOptPrefix
Pop ${var}
!macroend
Section
${GetParameters} $0
${If} $0 == ""
;No parameters, lets run the tests
ExecWait '"$exepath" /param1=foo'
ExecWait '"$exepath" /param1=foo /param2=bar'
${Else}
${GetOptions} $0 "/param1" $1
${If} ${Errors}
# /param 1 not used, do nothing?
${Else}
${GetOptions} $0 "/param2" $2
${If} ${Errors}
MessageBox mb_iconstop "Missing /param2, required by /param1"
Quit
${Else}
!insertmacro StripOptPrefix $1
!insertmacro StripOptPrefix $2
MessageBox mb_ok "1=$1$\n2=$2"
${EndIf}
${EndIf}
${EndIf}
SectionEnd

How to show some text on mouse move in NSIS installer

is there any possibility to show some descriptive text on NSIS installer custom page, but only on mouse hover?
I have the prerequisites check at the beginning of the installer and when one (or more) of the tests fail, appropriate warning message is displayed. It is custom page displayed before whole installation. The problem is, that there are too many messages (in the worst case) and installer page small -- it isn't possible to show all of them without overlaying... So I would like to display only some title (briefly describing the problem) and more detailed information somewhere below in the dedicated area, but only when mouse moved over the brief text. Or, other solution is to create some scrollable area...
But I don't know how to do it in NSIS. I know .onMouseOverSection callback, but AFAIK it can be used only in section selection page.
Is it possible to do it in NSIS page?
Thanks in advance.
I don't think doing this on the instfiles page is a good idea. I would go for a custom page. If you decide to go with the custom page idea, you probably have 3 options:
Create a custom nsis plugin that shows a page in any way you want.
Create a custom page with nsDialogs and handle the mouse messages with the WndSubclass plugin.
Use two component pages and tweak one of them.
The following example uses the 3rd option since it uses only official nsis plugins.
OutFile "$%temp%\test.exe"
Name "Prereq desc"
RequestExecutionLevel user
!include MUI2.nsh
!define MUI_PAGE_HEADER_TEXT "Header blablah"
!define MUI_PAGE_HEADER_SUBTEXT "subheader blablah"
!define MUI_COMPONENTSPAGE_TEXT_TOP "blablah 1"
!define MUI_COMPONENTSPAGE_TEXT_INSTTYPE " "
!define MUI_COMPONENTSPAGE_TEXT_COMPLIST " "
!define MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_TITLE "blablah 4"
!define MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_INFO "blablah 5"
!define MUI_PAGE_CUSTOMFUNCTION_PRE prereqcreate
!define MUI_PAGE_CUSTOMFUNCTION_SHOW prereqshow
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_PRE componentscreate
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Function .onInit
InitPluginsDir
StrCpy $0 0
loop:
ClearErrors
SectionGetText $0 $1
IfErrors done
WriteIniStr "$Pluginsdir\sec.ini" S $0 $1 ;Save section names...
IntOp $0 $0 + 1
Goto loop
done:
FunctionEnd
!macro ShowSections initial flip
StrCpy $0 0
StrCpy $2 ${initial}
loop:
ClearErrors
SectionGetText $0 $1
IfErrors done
ReadIniStr $1 "$Pluginsdir\sec.ini" S $0
IntCmpU 0 $2 "" +2 +2
StrCpy $1 ""
SectionSetText $0 $1
IntOp $0 $0 + 1
IntCmpU $0 ${flip} "" +2 +2
IntOp $2 $2 !
Goto loop
done:
!macroend
!macro CreatePreReq text name
Section /o "${text}" ${name}
SectionIn RO
SectionEnd
!macroend
!insertmacro CreatePreReq "Windows Installer v4.5" SEC_PRMSI
!insertmacro CreatePreReq ".NET v666" SEC_PRDOTNET
Section "Program files" SEC_PROG
;...
SectionEnd
Section "Desktop shortcut" SEC_DESKLNK
;...
SectionEnd
!define FIRSTREALSECTION ${SEC_PROG}
Function prereqcreate
!insertmacro ShowSections 1 ${FIRSTREALSECTION}
FunctionEnd
Function prereqshow
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $1 $0 0x3FD
ShowWindow $1 0
GetDlgItem $1 $0 0x3FE
ShowWindow $1 0
GetDlgItem $1 $0 0x3FF
ShowWindow $1 0 ;hide space texts
GetDlgItem $1 $0 0x408
!define TVM_SETIMAGELIST 0x1109
SendMessage $1 ${TVM_SETIMAGELIST} 2 0 ;Remove images (leaking the imagelist in the process, oh well)
System::Call '*(&i24)i.r2'
System::Call 'user32::GetWindowRect(ir1,ir2)'
System::Call 'user32::MapWindowPoints(i0,ir0,ir2,i2)'
System::Call '*$2(i,i.r0,i.r3,i.r4)'
System::Free $2
IntOp $4 $4 - $0
System::Call 'user32::SetWindowPos(ir1,i0,i0,ir0,ir3,ir4,i0)'
FunctionEnd
Function componentscreate
!insertmacro ShowSections 0 ${FIRSTREALSECTION}
FunctionEnd
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_PRMSI} "You need MSI in a NSIS installer? ;)"
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_PRDOTNET} "You need moar bloat!"
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_PROG} "Required program files..."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_DESKLNK} "Stupid desktop shortcut"
!insertmacro MUI_FUNCTION_DESCRIPTION_END

Resources