Disable empty SectionGroup (NSIS) - nsis

In my script I have the conditional header inside a SectionGroup where sections are created at compile time.
If sections are not created SectionGroup exists and not checkable. How can I disable it if sections are not exist?

Set the name to empty to hide a section and/or group.
!include Sections.nsh
!include LogicLib.nsh
Page Components
Page InstFiles
Function HideEmptyGroup
Exch $3
Push $0
Push $1
Push $2
StrCpy $0 $3
StrCpy $2 ""
ClearErrors
loop:
SectionGetFlags $0 $1
IfErrors done
${If} $1 & ${SF_SECGRPEND}
${If} $2 = 1
SectionSetText $3 ""
${EndIf}
IntOp $2 $2 - 1
${ElseIf} $1 & ${SF_SECGRP}
IntOp $2 $2 + 1
${Else}
Goto done
${EndIf}
IntOp $0 $0 + 1
Goto loop
done:
Pop $2
Pop $1
Pop $0
Pop $3
FunctionEnd
SectionGroup /e "My Group" SID_MYGROUP
!if 0 ; Set to 1 to show section and group
Section "Thing"
SectionEnd
!endif
SectionGroupEnd
Function .onInit ; Note: This must come after the sections!
Push ${SID_MYGROUP}
Call HideEmptyGroup
FunctionEnd

Related

NSIS install path validation

I want to validate the installation path selected by the user. I can't figure out how to check that so it will look like this:
You can't select a path with spaces (except Program Files)
When you click "Install" then it will prompt the error saying that you have to change the installation directory
For now I have this:
Function StrStr
Exch $1 ; st=haystack,old$1, $1=needle
Exch ; st=old$1,haystack
Exch $2 ; st=old$1,old$2, $2=haystack
Push $3
Push $4
Push $5
StrLen $3 $1
StrCpy $4 0
; $1=needle
; $2=haystack
; $3=len(needle)
; $4=cnt
; $5=tmp
loop:
StrCpy $5 $2 $3 $4
StrCmp $5 $1 done
StrCmp $5 "" done
IntOp $4 $4 + 1
Goto loop
done:
StrCpy $1 $2 "" $4
Pop $5
Pop $4
Pop $3
Pop $2
Exch $1
FunctionEnd
Function .onVerifyInstDir
Push "$INSTDIR"
Push " "
Call StrStr
Pop $0
StrCpy $0 $0 1
StrCmp $0 " " 0 +2
Abort
FunctionEnd
It refuses to install while there is any space in the path. I need to modify this so Program Files will be the only exception for that rule. Also, printing error message would be helpful
This restriction makes no sense to me. Some legacy applications can't handle spaces in the path but that of course also includes the Program Files folder (although the progra~1 hack can be used as a workaround if short name generation is active).
NSIS does not have a specific way to display a error/warning message directly on the page but you can change existing text in the UI and/or display a balloon.
!include WinMessages.nsh
!define /IfNDef EM_SHOWBALLOONTIP 0x1503
!define /IfNDef EM_HIDEBALLOONTIP 0x1504
!define DIRPAGE_CHANGETEXT ; Remove this line to disable the text change
!define DIRPAGE_BALLOON ; Remove this line to disable the balloon
Function .onVerifyInstDir
FindWindow $9 "#32770" "" $HWNDPARENT
!ifdef DIRPAGE_CHANGETEXT
GetDlgItem $3 $9 1006 ; IDC_INTROTEXT
LockWindow on
!endif
StrCpy $1 0
loop:
StrCpy $2 $InstDir 1 $1
StrCmp $2 '' valid ; End of string
StrCmp $2 ' ' found_space
IntOp $1 $1 + 1
Goto loop
valid:
!ifdef DIRPAGE_CHANGETEXT
SetCtlColors $3 SYSCLR:18 SYSCLR:15
SendMessage $3 ${WM_SETTEXT} "" "STR:$(^DirText)"
LockWindow off
!endif
!ifdef DIRPAGE_BALLOON
GetDlgItem $3 $9 1019
SendMessage $3 ${EM_HIDEBALLOONTIP} "" "" ; Not required?
!endif
Return
found_space:
StrLen $1 "$ProgramFiles\"
StrCpy $2 "$InstDir\" $1
StrCmp $2 "$ProgramFiles\" valid
!ifdef DIRPAGE_CHANGETEXT
SetCtlColors $3 ff0000 transparent
SendMessage $3 ${WM_SETTEXT} "" "STR:Paths with spaces are not allowed, except for $ProgramFiles for some reason!"
LockWindow off
!endif
!ifdef DIRPAGE_BALLOON
GetDlgItem $3 $9 1019
System::Call '*(&l${NSIS_PTR_SIZE},w "Bad path!", w "Spaced not allowed in path!",p 3)p.r2'
SendMessage $3 ${EM_SHOWBALLOONTIP} "" $2 ; This will only work on XP and later (and you must use "XPStyle on")
System::Free $2
!endif
Abort
FunctionEnd
XPStyle on
Page Directory
The Next button is disabled when Abort is called inside .onVerifyInstDir. If you want to display a MessageBox when the user clicks next then you can't call Abort in .onVerifyInstDir, you will have to use the page leave function callback (where you have to verify the path again and maybe call MessageBox+Abort).

How to use nsdialogs REMOVE or REPAIR feature in NSIS script?

My installer working fine without nsdialogs, If i add nsdialogs its not working good.I can't call nsdialogs in correct place.where i need to call nsdialogs?
Var hwnd
Var Dialog
Page custom checkinstall
Page custom nsDialogsPage
.
.
.
Function .onInit
!define MUI_LANGDLL_ALWAYSSHOW
!insertmacro MUI_LANGDLL_DISPLAY
SetShellVarContext all
!insertmacro VerifyUserIsAdmin
FunctionEnd
Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
SetShellVarContext all
SetRebootFlag true
MessageBox MB_OKCANCEL "Are you sure you want to uninstall
${APPNAME}?" IDOK next
Abort
next:
!insertmacro VerifyUserIsAdmin
FunctionEnd
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed
from your computer."
FunctionEnd
Function Repair
Call .onInit
FunctionEnd
Function Remove
ExecWait "$INSTDIR\Uninstall.exe"
FunctionEnd
Function checkinstall
ReadRegStr $R0 HKLM \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"UninstallString"
IfFileExists $R0 +1 NotInstalled
Call nsDialogsPage
NotInstalled:
FunctionEnd
Function nsDialogsPage
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 Repair
${NSD_CreateRadioButton} 0 25u 100% 56u "Remove"
Pop $hwnd
${NSD_OnClick} $hwnd Remove
nsDialogs::Show
FunctionEnd
I can not call nsdialogs correctly. checkinstall function not working at all.I need a correct solution for that.
You are writing the uninstall command to the registry with quotes (correct) but you are not removing the quotes when reading the value so the file exists test is always going to be false.
You should remove the quotes from commands you read from the registry:
Function GetAppFromCommand
Exch $1
Push $2
Push $3
StrCpy $3 ""
StrCpy $2 $1 1
StrCmp $2 '"' qloop sloop
sloop:
StrCpy $2 $1 1 $3
IntOp $3 $3 + 1
StrCmp $2 "" +2
StrCmp $2 ' ' 0 sloop
IntOp $3 $3 - 1
Goto done
qloop:
StrCmp $3 "" 0 +2
StrCpy $1 $1 "" 1 ; Remove initial quote
IntOp $3 $3 + 1
StrCpy $2 $1 1 $3
StrCmp $2 "" +2
StrCmp $2 '"' 0 qloop
done:
StrCpy $1 $1 $3
Pop $3
Pop $2
Exch $1
FunctionEnd
!macro GetAppFromCommand cmd outvar
Push `${cmd}`
Call GetAppFromCommand
Pop ${outvar}
!macroend
Function checkinstall
ReadRegStr $R0 HKLM \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"UninstallString"
!insertmacro GetAppFromCommand $R0 $R0
IfFileExists $R0 +1 NotInstalled
Call nsDialogsPage
NotInstalled:
FunctionEnd

NSIS window disappear

I have tried a lot of possibility with this code but it doesn't work. I need to check a VPN connection. If I run the code firs time it works, but if I don't restart the program and push the button secondly then the program just disappear. Any idea how to fix it? thanks.
Function VpnLoginWindow
${Do}
!insertmacro VpnLoginWindow
StrCpy $4 0
${If} $3 == 1
${Do}
!insertmacro VpnLoginWindow
StrCpy $4 1
StrCmp $3 "0" 0 +2
${ExitDo}
${Loop}
${EndIf}
${If} $4 == 1
${ExitDo}
${EndIf}
${Loop}
FunctionEnd
!macro VpnLoginWindow
StrCpy $5 $1
System::Get "(i.r1) iss"
Pop $R0
System::Call "user32::EnumWindows(k R0, i) i.s"
${Do}
Pop $0
StrCmp $0 "1" 0 +2
${ExitDo}
System::Call "user32::GetWindowText(ir1,t.r2,i ${NSIS_MAX_STRLEN})"
StrCpy $2 $2 7
StrCpy $3 0
${If} $2 == "SSL VPN"
StrCpy $3 1
${ExitDo}
${EndIf}
Push 1 # callback's return value
System::Call "$R0"
${Loop}
System::Free $R0
StrCpy $1 $5
!macroend
Callbacks are tricky to get right and the plug-in is a little buggy. Also, you should not use relative jumps to jump over macros like ${ExitDo}!
!include LogicLib.nsh
!macro FindVpnLoginWindow
Push "" ; Result: Window not found
System::Store S
System::Get '(p.r1, p)ir0r0'
Pop $9
System::Call 'USER32::EnumWindows(k r9, p 0)'
${Do}
${IfThen} $0 != "callback1" ${|} ${ExitDo} ${|} ; <-- adjust the callback# if required
StrCpy $0 1 ; Set callback return value, continue search
System::Call "USER32::GetWindowText(pr1, t.r2, i ${NSIS_MAX_STRLEN})"
StrCpy $2 $2 7
${If} $2 == "SSL VPN"
Pop $2 ; Throw away old result
Push $1 ; Result: HWND
StrCpy $0 0 ; Set callback return value, stop enum with 0
${EndIf}
System::Call $9 ; Return from callback function
${Loop}
System::Free $9
System::Store L
!macroend
Function BackgroundFindWindow
!insertmacro FindVpnLoginWindow
Pop $0
DetailPrint "Result:$0"
FunctionEnd
...
GetFunctionAddress $0 BackgroundFindWindow
BgWorker::CallAndWait
GetFunctionAddress $0 BackgroundFindWindow
BgWorker::CallAndWait

NSIS - How to implement three Mutually Exclusive Sections within a SubSection

I have a simple NSIS script based on the following source code I found on: NSIS Mutually Exclusive Sections. The problem I'm facing is that I'm not able to implement three mutually exclusive sections instead of one.
Section 0 is independent.
For Section 1, 2 and 3 I need to have them mutually exclusive. Being able to select none of them or just one selected at same time.
If you test the example it seems it works well when you select:
section 1 (#2 and #3 unselected)
section 2 (#1 and #3 unselected)
section 3 (#1 and #2 unselected)
The problem starts when you continue selecting options after having section 3 selected and then you go to select section 2 and section 1. Or if you try to doing random selections the selections are messed up.
I'm not able to implement this mutually exclusive between three sections within a SubSection. Can anyone give me some light on this?
Much appreciated.
Thanks,
Here's the script that I have modified so far:
# Example to control section selection.
# It allows only one of three sections to be selected at any
# given time, but unlike one-section, it allows non of them
# to be selected as well.
#
!include Sections.nsh
!include LogicLib.nsh
Name example
OutFile "mutually exclusive.exe"
ComponentText "please choose zero or one but the default"
SubSection /e "Test" SSEC00
Section /o "Test Sec 0" sec0
SectionEnd
Section /o "#2 and #3 unselected" sec1
SectionEnd
Section /o "#1 and #3 unselected" sec2
SectionEnd
Section /o "#1 and #2 unselected" sec3
SectionEnd
SubSectionEnd
Function .onInit
Push $0
SectionGetFlags ${sec1} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec1} $0
SectionGetFlags ${sec2} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec2} $0
SectionGetFlags ${sec3} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec3} $0
Pop $0
FunctionEnd
Function .onSelChange
Push $0
${If} ${SectionIsSelected} ${sec1}
${OrIf} ${SectionIsSelected} ${sec2}
${OrIf} ${SectionIsSelected} ${sec3}
StrCmp $R9 ${sec1} check_sec2 # If last selection was sec1 goto check_sec2
StrCmp $R9 ${sec2} check_sec3 # If last selection was sec2 goto check_sec3
StrCmp $R9 ${sec3} check_sec1
check_sec1:
SectionGetFlags ${sec1} $0
IntOp $0 $0 & ${SF_SELECTED}
IntCmp $0 ${SF_SELECTED} 0 Seldone Seldone
StrCpy $R9 ${sec1}
SectionGetFlags ${sec2} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec2} $0
SectionGetFlags ${sec3} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec3} $0
Goto Seldone
check_sec2:
SectionGetFlags ${sec2} $0
IntOp $0 $0 & ${SF_SELECTED}
IntCmp $0 ${SF_SELECTED} 0 Seldone Seldone
StrCpy $R9 ${sec2}
SectionGetFlags ${sec1} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec1} $0
SectionGetFlags ${sec3} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec3} $0
check_sec3:
SectionGetFlags ${sec3} $0
IntOp $0 $0 & ${SF_SELECTED}
IntCmp $0 ${SF_SELECTED} 0 Seldone Seldone
StrCpy $R9 ${sec3}
SectionGetFlags ${sec1} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec2} $0
SectionGetFlags ${sec2} $0
IntOp $0 $0 & ${SECTION_OFF}
SectionSetFlags ${sec2} $0
Goto check_sec1
Seldone:
${EndIf}
Pop $0
FunctionEnd
In NSIS v3+ the section id of the changed section is stored in $0 when .onSelChange is called, this makes it easier to get the logic correct:
!include Sections.nsh
!include LogicLib.nsh
ComponentText "please choose zero or one but the default"
SubSection /e "Test" SSEC00
Section /o "Test Sec 0" sec0
SectionEnd
Section /o "#2 and #3 unselected" sec1
SectionEnd
Section /o "#1 and #3 unselected" sec2
SectionEnd
Section /o "#1 and #2 unselected" sec3
SectionEnd
SubSectionEnd
Page Components
Page InstFiles
Function .onSelChange
${IfThen} $0 = -1 ${|} Return ${|} ; I don't care about InstType changes
${If} $0 < ${sec1}
${OrIf} $0 > ${sec3}
Return ; I don't care about other sections
${EndIf}
!macro SelectOnlyMe sid
${IfThen} $0 <> ${sid} ${|} !insertmacro UnselectSection ${sid} ${|}
!macroend
!insertmacro SelectOnlyMe ${sec1}
!insertmacro SelectOnlyMe ${sec2}
!insertmacro SelectOnlyMe ${sec3}
FunctionEnd
(If you need multiple groups of mutually exclusive sections you could make the "if/orif" range check part of the helper macro instead)

nsis refresh environment during setup

During my setup i install redistributables. After installing my program i want to execute the program during the setup. For that the redistributables are needed. But the environment is not refreshed during setup, so i can't start the program. Can i refresh the environtment during the setup, or reread the environment? Or read the system environment which has changed after the redistributable installation?
I need the environment changes made during the installation of the redistributables during the setup.
Thanks for your help.
I had your same issue, that I solved with a workaround, but I came across this solution and I would like to share with you.
This should reload environment variables during setup:
!include LogicLib.nsh
!include WinCore.nsh
!ifndef NSIS_CHAR_SIZE
!define NSIS_CHAR_SIZE 1
!define SYSTYP_PTR i
!else
!define SYSTYP_PTR p
!endif
!ifndef ERROR_MORE_DATA
!define ERROR_MORE_DATA 234
!endif
/*!ifndef KEY_READ
!define KEY_READ 0x20019
!endif*/
Function RegReadExpandStringAlloc
System::Store S
Pop $R2 ; reg value
Pop $R3 ; reg path
Pop $R4 ; reg hkey
System::Alloc 1 ; mem
StrCpy $3 0 ; size
loop:
System::Call 'SHLWAPI::SHGetValue(${SYSTYP_PTR}R4,tR3,tR2,i0,${SYSTYP_PTR}sr2,*ir3r3)i.r0' ; NOTE: Requires SHLWAPI 4.70 (IE 3.01+ / Win95OSR2+)
${If} $0 = 0
Push $2
Push $0
${Else}
System::Free $2
${If} $0 = ${ERROR_MORE_DATA}
IntOp $3 $3 + ${NSIS_CHAR_SIZE} ; Make sure there is room for SHGetValue to \0 terminate
System::Alloc $3
Goto loop
${Else}
Push $0
${EndIf}
${EndIf}
System::Store L
FunctionEnd
Function RefreshProcessEnvironmentPath
System::Store S
Push ${HKEY_CURRENT_USER}
Push "Environment"
Push "Path"
Call RegReadExpandStringAlloc
Pop $0
${IfThen} $0 <> 0 ${|} System::Call *(i0)${SYSTYP_PTR}.s ${|}
Pop $1
Push ${HKEY_LOCAL_MACHINE}
Push "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Push "Path"
Call RegReadExpandStringAlloc
Pop $0
${IfThen} $0 <> 0 ${|} System::Call *(i0)${SYSTYP_PTR}.s ${|}
Pop $2
System::Call 'KERNEL32::lstrlen(t)(${SYSTYP_PTR}r1)i.R1'
System::Call 'KERNEL32::lstrlen(t)(${SYSTYP_PTR}r2)i.R2'
System::Call '*(&t$R2 "",&t$R1 "",i)${SYSTYP_PTR}.r0' ; The i is 4 bytes, enough for a ';' separator and a '\0' terminator (Unicode)
StrCpy $3 ""
${If} $R1 <> 0
${AndIf} $R2 <> 0
StrCpy $3 ";"
${EndIf}
System::Call 'USER32::wsprintf(${SYSTYP_PTR}r0,t"%s%s%s",${SYSTYP_PTR}r2,tr3,${SYSTYP_PTR}r1)?c'
System::Free $1
System::Free $2
System::Call 'KERNEL32::SetEnvironmentVariable(t"PATH",${SYSTYP_PTR}r0)'
System::Free $0
System::Store L
FunctionEnd
Section
Call RefreshProcessEnvironmentPath
SectionEnd
Source: http://forums.winamp.com/showpost.php?p=3028153&postcount=12

Resources