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

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)

Related

Disable empty SectionGroup (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

NSIS script doesn't display service status

I created section at the end of script which suppose to display myservice status, but no messagebox displays after exec.
What might be the reason?
Section "Create Service"
ExecShellWait '' 'sc.exe' 'create myservice error= "severe" displayname= "myservice" type= "own" start= "auto" binpath= "$INSTDIR\MyService.exe"' SW_HIDE
SectionEnd
Section "Start Service"
ExecShellWait '' 'sc.exe' 'start myservice' SW_HIDE
SectionEnd
Section "Ensure Running"
StrCpy $R0 '"$SYSDIR\cmd.exe" /c "sc QUERY myservice | FIND /C "RUNNING""'
nsExec::ExecToStack '$R0'
Pop $R1 # contains return code
Pop $R2 # contains output
${If} $R1 == "0"
DetailPrint "checking if command is success"
${If} $R2 == "1"
MessageBox mb_ok "myservice is Running" # it's running
${Else}
MessageBox mb_ok "Not Running" # it's not running
${EndIf}
${Else}
DetailPrint "command failed"
${EndIf}
SectionEnd ```
nsExec::ExecToStack /OEM '$R0' didn't change anything. During installation it rushes to finish page after sc exec ..Do I need to add additional page page here ?
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE ..\license.rtf
; Page custom pageRegistration
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Without seeing the output I can only guess that there is some sort of Unicode issue.
Change nsExec::ExecToStack '$R0' to nsExec::ExecToStack /OEM '$R0'.
That being said, I don't know if the RUNNING string is always in English. There are several service plug-ins and macros on the NSIS wiki you can use instead.
Or you can use this:
!include "LogicLib.nsh"
!include "Win\COM.nsh"
!include "Win\Propkey.nsh"
Function IsServiceRunning
Exch $0
Push $1
StrCpy $1 $0
!insertmacro ComHlpr_CreateInProcInstance {13709620-C279-11ce-A49E-444553540000} {A4C6892C-3BA9-11D2-9DEA-00C04FB16162} r0 "" ; IE5+?
${If} $0 P<> 0
System::Call "$0->36(wr1,#r1)i.s"
${IUnknown::Release} $0 ""
Pop $0
${If} $0 >= 0
StrCpy $0 0
Push $2
${V_GetVT} $1 r2
${V_GetBOOL} $1 r1
${If} $2 = ${VT_BOOL}
${AndIf} $1 <> ${VARIANT_FALSE}
StrCpy $0 1
${EndIf}
Pop $2
${Else}
StrCpy $0 0
${EndIf}
${EndIf}
Pop $1
Exch $0
FunctionEnd
Section "Example"
Push "RemoteRegistry"
Call IsServiceRunning
Pop $0
DetailPrint "RemoteRegistry running=$0"
Push "Themes"
Call IsServiceRunning
Pop $0
DetailPrint "Themes running=$0"
SectionEnd

How to get the return value of INSTALLOPTIONS_DISPLAY_RETURN?

I'm try to get if the back bottom is clicked and avoid displaying the error message when one of input is not filled in.
Rigth now the $R9 variable is not filed is as I expect it.
Function SetCustom
;SectionGetFlags ${SEC04} $R0
SectionGetFlags ${SEC05} $R5
SectionGetFlags ${SEC06} $R6
SectionGetFlags ${SEC07} $R7
IntOp $R0 $R0 & ${SF_SELECTED}
IntOp $R5 $R5 & ${SF_SELECTED}
IntOp $R6 $R6 & ${SF_SELECTED}
IntOp $R7 $R7 & ${SF_SELECTED}
IntCmp $R0 ${SF_SELECTED} show
IntCmp $R5 ${SF_SELECTED} show
IntCmp $R6 ${SF_SELECTED} show
IntCmp $R7 ${SF_SELECTED} show
Abort
show:
Input:
!insertmacro MUI_HEADER_TEXT "Customization" "Please input the Info1 and Info2 of the Customization user."
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "File.ini"
!insertmacro INSTALLOPTIONS_READ $R1 "File.ini" "Field 2" "State"
!insertmacro INSTALLOPTIONS_READ $R2 "File.ini" "Field 3" "State"
;!insertmacro INSTALLOPTIONS_SHOW_RETURN
!insertmacro INSTALLOPTIONS_DISPLAY_RETURN $R9
MessageBox MB_OK "$R9"
${If} "$R9" == "back"
Abort
${Else}
StrCmp $R1 "" 0 +3
MessageBox MB_ICONEXCLAMATION|MB_OK "Please enter a Info1."
Goto Input
StrCmp $R2 "" 0 +3
MessageBox MB_ICONEXCLAMATION|MB_OK "Please enter a Info2"
Goto Input
${EndIf}
FunctionEnd
The documentation tells you to Pop:
If you need the return value, use the INSTALLOPTIONS_DISPLAY_RETURN or INSTALLOPTIONS_SHOW_RETURN macro. The return value will be added to the stack, so you can use the Pop command to get it.
!insertmacro INSTALLOPTIONS_DISPLAY_RETURN "File.ini"
Pop $5
${If} $5 == "back"
...
${EndIf}
From my reading there's one post from pengyou that can be found here:
https://nsis-dev.github.io/NSIS-Forums/html/t-182393.html
The post ask for changing the function used to call the display of the page.
I changed all my call to be using the MUI version of it and I used MUI_INSTALLOPTIONS_DISPLAY_RETURN instead of MUI_INSTALLOPTIONS_DISPLAY.
Also I change the order of the calls to the loading of the ini file is done prior to the configuration.
Function SetCustom
SectionGetFlags ${SEC01} $R0
SectionGetFlags ${SEC02} $R4
SectionGetFlags ${SEC03} $R5
IntOp $R0 $R0 & ${SF_SELECTED}
IntOp $R4 $R4 & ${SF_SELECTED}
IntOp $R5 $R5 & ${SF_SELECTED}
IntCmp $R0 ${SF_SELECTED} show
IntCmp $R4 ${SF_SELECTED} show
IntCmp $R5 ${SF_SELECTED} show
Abort
show:
InputMySQL:
StrCpy $UserAborted "0"
!insertmacro MUI_INSTALLOPTIONS_DISPLAY_RETURN "file.ini"
!insertmacro MUI_HEADER_TEXT "MySQL customization" "Please input the username and password of the MySQL user."
!insertmacro MUI_INSTALLOPTIONS_READ $R1 "file.ini" "Field 2" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $R2 "file.ini" "Field 3" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $R3 "file.ini" "Field 4" "State"
Pop $R6
MessageBox MB_OK "$R6" ;This will pop a message box comment it out for debugging
${If} $R6 <> "back"
${If} $R6 <> "cancel"
;Do validation code here
${EndIf}
${EndIf}
FunctionEnd

NSIS script + Skip button alignment issue

I am trying NSIS script to add "SKIP" button.
I have tested below code and it works well but when I actually integrate with the main code which is having a screen of option "back" "Next" and "Cancel" button. this skip button is not visible at all, it is in extreme right of cancel button and hardly visible.
How can move this "Skip" button towards left side.
Name BtnTest
Outfile test.exe
Installdir "$temp"
RequestExecutionLevel user
BrandingText " " ;Button covers this text
!include nsDialogs.nsh ;For WS_*
!define MUI_CUSTOMFUNCTION_GUIINIT SKIP
Function SKIP
; You are supposed to use ChangeUI (or MUI_UI) and a modified ui file to add new buttons but this example adds the button at run-time...
GetDlgItem $0 $hwndparent 2 ; Find cancel button
System::Call *(i,i,i,i)i.r1
System::Call 'USER32::GetWindowRect(ir0,ir1)'
System::Call *$1(i.r2,i.r3,i.r4,i.r5)
IntOp $5 $5 - $3 ;height
IntOp $4 $4 - $2 ;width
System::Call 'USER32::ScreenToClient(i$hwndparent,ir1)'
System::Call *$1(i.r2,i.r3)
System::Free $1
IntOp $2 $2 + $4 ;x
IntOp $2 $2 + 8 ;x+padding
System::Call 'USER32::CreateWindowEx(i0,t "Button",t "Click Me",i${WS_CHILD}|${WS_VISIBLE}|${WS_TABSTOP},ir2,ir3,ir4,ir5,i $hwndparent,i 0x666,i0,i0)i.r0'
SendMessage $hwndparent ${WM_GETFONT} 0 0 $1
SendMessage $0 ${WM_SETFONT} $1 1
GetFunctionAddress $0 onmybtnclick
ButtonEvent::AddEventHandler 0x666 $0
FunctionEnd
Function onmybtnclick
MessageBox mb_ok "You clicked me!"
FunctionEnd
Page Directory
Page Instfiles
Section
SectionEnd
And also this button appears on all screen How can I display only in one screen.
Change $2 and $3 before the call to CreateWindowEx to change the button position. You can save the handle to the button in another variable after CreateWindowEx but I just used the Id in this example (0x666).
Name BtnTest
Outfile test.exe
Installdir "$temp"
RequestExecutionLevel user
BrandingText " " ;Button covers this text
!include nsDialogs.nsh ;For WS_*
!define MUI_CUSTOMFUNCTION_GUIINIT SKIP
/**/ !include MUI2.nsh #*/
!ifndef MUI_INCLUDED
Function .onGuiInit
Call SKIP
FunctionEnd
!endif
Function SKIP
; You are supposed to use ChangeUI (or MUI_UI) and a modified ui file to add new buttons but this example adds the button at run-time...
GetDlgItem $0 $hwndparent 2 ; Find cancel button
System::Call *(i,i,i,i)i.r1
System::Call 'USER32::GetWindowRect(ir0,ir1)'
System::Call *$1(i.r2,i.r3,i.r4,i.r5)
IntOp $5 $5 - $3 ;height
IntOp $4 $4 - $2 ;width
System::Call 'USER32::ScreenToClient(i$hwndparent,ir1)'
System::Call *$1(i.r2,i.r3)
System::Free $1
!ifdef MUI_INCLUDED
StrCpy $2 15 ; Whatever you feel looks good
!else
IntOp $2 $2 + $4 ;x
IntOp $2 $2 + 8 ;x+padding
!endif
System::Call 'USER32::CreateWindowEx(i0,t "Button",t "Click Me",i${WS_CHILD}|${WS_VISIBLE}|${WS_TABSTOP},ir2,ir3,ir4,ir5,i $hwndparent,i 0x666,i0,i0)i.r0'
SendMessage $hwndparent ${WM_GETFONT} 0 0 $1
SendMessage $0 ${WM_SETFONT} $1 1
GetFunctionAddress $0 onmybtnclick
ButtonEvent::AddEventHandler 0x666 $0
FunctionEnd
Function onmybtnclick
MessageBox mb_ok "You clicked me!"
FunctionEnd
Function HideIt
GetDlgItem $0 $hwndparent 0x666
ShowWindow $0 0
FunctionEnd
Function ShowIt
GetDlgItem $0 $hwndparent 0x666
ShowWindow $0 1
FunctionEnd
!ifdef MUI_INCLUDED
!define MUI_PAGE_CUSTOMFUNCTION_SHOW HideIt
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_SHOW ShowIt
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_SHOW HideIt
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE English
!else
Page Components "" HideIt
Page Components "" ShowIt
Page Components "" HideIt
Page Instfiles
!endif
Section
SectionEnd

NSIS script that installs Chrome extension kills Outlook

This is an incredibly bizarre bug.
Here is an NSIS script that installs an extension into Google Chrome. Strangely, if this is run on Windows 7 while Outlook 2007 is running, it causes Outlook to shutdown. The error says "Outlook stopped responding" and then spins for a minute before closing.
Can anyone shed any light on what the cause is and how to fix it?
!define VERSION "1.0.0"
!define EXT_ID "kmffervcdaycdjlksmflkjghksdf"
!define INSTALL_DIR "$LOCALAPPDATA\Google\Chrome\User Data\Default\Extensions\${EXT_ID}\${VERSION}_0"
!define P_FILE "$LOCALAPPDATA\Google\Chrome\User Data\Default\Preferences"
!include "ZipDLL.nsh"
!include "TextFunc.nsh"
!insertmacro LineFind
!include "WordFunc.nsh"
!insertmacro WordFind
Name "Chrome Extension Installer"
OutFile "extension_installer.exe"
RequestExecutionLevel admin
Var PMEMORY
Var SIZE
Function .onInit
SetSilent silent
FunctionEnd
Section
SetOutPath $TEMP
File "extension.crx"
File "chrome_preferences.txt"
CreateDirectory "${INSTALL_DIR}"
ZipDLL::extractall "$TEMP\extension.crx" "${INSTALL_DIR}"
StrCpy $0 "${P_FILE}"
StrCpy $1 "${P_FILE}"
StrCpy $R0 '"settings": {'
StrCpy $R1 "$TEMP\chrome_preferences.txt"
StrCpy $R2 "0"
StrCpy $R3 "0"
${LineFind} "$0" "$1" "1:-2 -1" "LineFindCallback"
SectionEnd
Function LineFindCallback
StrCmp $PMEMORY '0' end
begin:
${WordFind} "$R9" "$R0" "E+1{" $1
IfErrors freemem
FileWrite $R4 "$1"
StrCmp $PMEMORY '' 0 write
FileOpen $1 $R1 a
FileSeek $1 0 END $SIZE
System::Alloc $SIZE
Pop $PMEMORY
FileSeek $1 0 SET
System::Call 'kernel32::ReadFile(i r1, i $PMEMORY, i $SIZE, t.,)'
FileClose $1
write:
IntOp $R3 $R3 + 1
System::Call 'kernel32::WriteFile(i R4, i $PMEMORY, i $SIZE, t.,)'
${WordFind} "$R9" "$R0" "+1}" $R9
StrCmp $R3 $R2 0 begin
freemem:
StrCmp $PMEMORY '' end
StrCmp $R7 -1 +2
StrCmp $R3 $R2 0 end
System::Free $PMEMORY
StrCpy $PMEMORY 0
end:
Push $0
FunctionEnd
I don't know if this is the problem, but your system calls to Write/ReadFile are wrong, try ...(i r?, i $PMEMORY, i $SIZE, *i,i 0)'
The problem turned out to be ZipDLL. Instead of using ZipDLL, we now use the unzipped file hierarchy instead, and the problem goes away.
(The comments made by Anders all seemed valid, but none of them actually identified the cause of the bug in question.)

Resources