NSIS - onClick not working at all - nsis

I have an issue that is driving me crazy: I cannot get the the onClick to work, not even with a simple example:
I've already read the documentation of NSIS, once and again before asking, but it seems something is stuck (yes I'm new in NSIS).
Function button_click
MessageBox MB_OK "Hi there!"
FunctionEnd
Function fnc_USB_drive_Create
...
${NSD_CreateButton} 12 74 121 30 "Button1"
Pop $hCtl_USB_drive_Button1
${NSD_OnClick} $hCtl_USB_drive_Button1 button_click
...
FunctionEnd
The button appears on screen, but clicking it does nothing. It's days that I have been working on this.
I have Win7 SP1 64b, and the NSIS + NSIS dialog designer (latest version of all). Anyone have any idea?

page custom fnc_USB_drive_Create
var hCtl_USB_drive_Button1
!include nsDialogs.nsh
Function fnc_USB_drive_Create
nsDialogs::Create 1018
pop $0
${NSD_CreateButton} 12 74 121 30 "Button1"
Pop $hCtl_USB_drive_Button1
${NSD_OnClick} $hCtl_USB_drive_Button1 button_click
nsDialogs::Show
FunctionEnd
Function button_click
MessageBox MB_OK "Hi there!"
FunctionEnd
Works fine for me, do you have any other code that might be messing up the dialog?

Related

How to fix "CreateDirectory: Relative paths not supported" when using a variable?

As a newbiew, I am still in the stage of experimenting and building little prototypes. The idea is to build a silent installer that has all settings in multiple sections of a .INI and the users calls the setup with parameter /config={NameOfSection}.
My current situation:
FooBar-install.ini
[PROD]
FOOHOME=c:\FooBar
FooBar.nsi
!include FileFunc.nsh
!include LogicLib.nsh
!insertmacro GetParameters
!insertmacro GetOptions
var /GLOBAL config
var /GLOBAL cmdLineParams
var /global REGAPPKEY
var /global FOOHOME
!define TheName "FooBar"
!define OutFileSuffix "-Install."
!define IniFile "$EXEDIR\${TheName}${OutFileSuffix}ini"
Name "${TheName} ${PRODUCT_VERSION}" ; bei 2 Kunden geht's auch kd-spezifisch ;)
OutFile ${TheName}${OutFileSuffix}exe
RequestExecutionLevel admin
Icon "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
UninstallIcon "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
Section "-aInstaller Section"
ReadINIStr $FOOHOME ${IniFile} $config "FOOHOME"
MessageBox MB_OK "ini=${IniFile} , config=$config, FOOHOME=$FOOHOME"
CreateDirectory "SFOOHOME"
SectionEnd
function .onInit
UserInfo::GetAccountType
pop $0
${If} $0 != "admin" ;Require admin rights on NT4+
MessageBox mb_iconstop "Administrator rights required!"
SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
${Else}
MessageBox MB_OK "onInit"
${EndIf}
; Get parameters
${GetParameters} $cmdLineParams
; /? param (help)
ClearErrors
${GetOptions} $cmdLineParams '/?' $R0
IfErrors +3 0
MessageBox MB_OK "Befehlszeilenparameter /config={{name}} verweist auf einen Abschnitt aus ${TheName}${OutFileSuffix}ini mit div. Parametern zur Steuerung des Setup"
Abort
Call parseParameters
Pop $R0
FunctionEnd
Function parseParameters
; /config
${GetOptions} $cmdLineParams '/config=' $R0
${If} ${Errors}
StrCpy $config "errPROD"
${Else}
StrCpy $config $R0
${Endif}
FunctionEnd
Problem
If I try to compile this, I get the msg
CreateDirectory: Relative paths not supported
Usage: CreateDirectory directory_name
Questions
I do not understand why this error comes up at compile time. When using a variable (especially in this situation where the variable depends on user-input), it does not seem to make sense to complain about the argument when it is not known.
How can I avoid this probolem?
A little puzzle that messes me up is the syntax to refer to variables.The statement MessageBox MB_OK "ini=${IniFile} , config=$config, FOOHOME=$FOOHOME" shows that. I found that I needed to enclose IniFile in {} in order to display its value (I commented out the CreateDir-line to compile the installer and check my assumptions). When do I have to use {}?
If you see any other "unusual" things in my little script, I'd be happy to know ;)
You have a typo, change CreateDirectory "SFOOHOME" to CreateDirectory "$FOOHOME"
You might want to read the documentation again to learn the basics; ${define}, $(langstring) and $variable.

Three choices in shortcut options

we have our installer where are some choices to create shortcuts
1) create shortcut for current user
2) create shortcut for all users
3) do not create any shortcut
The first option is checked as default, if I check any other it work well..the problem is that if I check for example 3rd option, move to next page and come back there are 2 choices checked - 1st (default) and 3rd (selected).
I am very new to NSIS and can't find better way than I did it as the code is not very friendly for me.
I would appreciate advice.
Thanks,
This is what I have in my installation options .ini :
[Field 5]
Type=GroupBox
Left=1
Right=-1
Top=80
Bottom=144
Text="Choice of shortcuts:"
State=""
[Field 6]
Type=RadioButton
Left=10
Right=-10
Top=96
Bottom=108
Text="Create shortcuts for all users"
State=1
Flags=GROUP|NOTIFY
[Field 7]
Type=RadioButton
Left=10
Right=-10
Top=112
Bottom=124
Text="Create shortcuts only for a current user"
State=0
Flags=NOTIFY
[Field 8]
Type=RadioButton
Text="Do not create shortcut"
Flags=NOTIFY
State=0
Left=10
Right=-10
Top=128
Bottom=140
Then later in nsis script:
IntCmp $ShortcutsForAllUsers 1 ShortcutsForAll ShortcutsForCurrentUser ShortcutsForCurrentUser
ShortcutsForAll:
SetShellVarContext all
goto done
ShortcutsForCurrentUser:
SetShellVarContext current
goto done
NoShortcuts:
goto done
done:
FunctionEnd
The InstallOptions page should remember its state as long as you don't extract it every time in the page create callback function.
!include LogicLib.nsh
!include InstallOptions.nsh
ChangeUI All "${NSISDIR}\Contrib\UIs\modern.exe"
Function .onInit
!insertmacro INSTALLOPTIONS_EXTRACT_AS "...\test.ini" "iosp.ini"
FunctionEnd
Var ShortcutMode
Page Custom MyShortcutPageCreate MyShortcutPageLeave
Page Components
Page InstFiles
Function MyShortcutPageCreate
!insertmacro INSTALLOPTIONS_DISPLAY "iosp.ini"
FunctionEnd
Function MyShortcutPageLeave
!insertmacro INSTALLOPTIONS_READ $0 "iosp.ini" "Settings" "State"
${If} $0 > 0
IntOp $ShortcutMode $0 - 6 ; Map .ini Field Id to 0 (or empty), 1 or 2
Abort
${EndIf}
FunctionEnd
Section
${If} $ShortcutMode = 0
SetShellVarContext all
${ElseIf} $ShortcutMode = 1
SetShellVarContext current
${EndIf}
...
SectionEnd
There is a problem with this design, SetShellVarContext current might not map $SMPrograms to the correct startmenu folder because UAC can elevate a process with a different Administrator user...

Find a string pattern in a file from an NSIS script

In an NSIS installer script, I'm trying to check if a given httpd.conf file contains the following line :
Include "c:\xxx\yyy.conf"
If so, then my installer script would not append it to the file, otherwise, it would append it.
I've come through {LineFind} but not sure this really makes what i'm trying to achieve.
What could be the simplest way to do a kind of "grep" on a text file from an NSIS script ?
Thank you !
Here is a sample for searching for a given line into a file, using the LogicLib for ease of syntax. The search is stopped as soon as the line is found. This sample works on the sample script itself:
# find.nsi : sample for LineFind from TextFunc.nsh
!include "textfunc.nsh"
!include "logiclib.nsh"
OutFile "find.exe"
!define lookfor `Section` ;will find
;!define lookfor `Sectionn` ;will not find
Var found
Section
StrCpy $found 0
${LineFind} "find.nsi" "/NUL" "1:-1" "GrepFunc"
${if} $found = 1
MessageBox MB_OK "string found"
${else}
MessageBox MB_OK "string NOT found"
${endIf}
SectionEnd
Function GrepFunc
${TrimNewLines} '$R9' $R9
DetailPrint "test for line $R8 `$R9`"
${if} $R9 == "${lookfor}"
StrCpy $found 1 ;set flag
Push "StopLineFind" ;stop find
${else}
Push 0 ;ignore -> continue
${endIf}
FunctionEnd

NSIS: How to add custom button to left bottom corner and handle it's click?

I tried the ButtonEvent plugin, but when I run compiled example, it fails with memory access error. Maybe it is able to do with System plugin via Windows API or something else? Can anyone show how it can be done?
UPD: Error was appeared because I tried to use non-unicode ButtonEvent on Unicode NSIS. Now example compiles and executes OK, but when I click on TryMe button, callback function is not called and nothing happens. How to determine what is the problem? Can anyone compile ButtonEventMUI.nsi and click on TryMe button? I downloaded latest ButtonEvent version. Using NSIS 2.46 Unicode
The system plugin cannot do this because it cannot subclass windows.
The ButtonEvent plugin works fine for me (NSIS 2.46):
Name BtnTest
Outfile test.exe
Installdir "$temp"
RequestExecutionLevel user
BrandingText " " ;Button covers this text
!include nsDialogs.nsh ;For WS_*
Function .onGuiInit
; 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

NSIS - Conditionally Display Components

I want the installer/uninstaller to check if any of the possible components are installed, and display only relevan components (installer should display only components not already installed and uninstaller should display only already-installed components)
I'm using MUI.
The component section of my .nsi looks something like this:
; Section descriptions
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1 Description"
!insertmacro MUI_DESCRIPTION_TEXT ${Component2} "Component2 Description."
!insertmacro MUI_DESCRIPTION_TEXT ${Component3} "Component3 Description."
!insertmacro MUI_FUNCTION_DESCRIPTION_END
I've tried a couple of different ways:
First, I tried using this macro for checking if a registry exists:
!insertmacro IfKeyExists HKEY_LOCAL_MACHINE SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall "Component1"
Pop $R0
${If} $R0 == 0 #Not installed yet. Display.
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1"
${EndIf}
This didn't work.
Then I tried using the Registry Plug-In:
${registry::KeyExists} "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Component1" $R0
${If} $R0 == 0 #Not installed yet. Display.
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1"
${EndIf}
This didn't work either.
So then I thought maybe there was some other more basic problem, and I tried this:
StrCpy $0 "0"
${If} $0 == "1"
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1 Description."
${EndIf}
But even then I still saw "Component1" as a possible component during installation!
What am I doing wrong and how can I go about achieving my goal?
Thanks in advance!
To select a section (= a component) at runtime you can use SectionGetFlag / SectionSetFlag and some bit manipulation :
SectionGetFlags ${test_section_id} $0
IntOp $0 $0 | ${SF_SELECTED}
SectionSetFlags ${test_section_id} $0
To deselect it, just toogle its selected bit, with
SectionGetFlags ${test_section_id} $0
IntOp $0 $0 ^ ${SF_SELECTED}
SectionSetFlags ${test_section_id} $0
These are simpler if you include the Sections.nsh header and use the SelectSection and UnselectSection macros.
If you want to hide a section, you just need to set its text to "" (empty string)
SectionSetText ${test_section_id} ""
You are modifying the wrong text. MUI_DESCRIPTION_TEXT is the long description that is typically displayed when hovering the mouse over a component. But the visibility of a section depends on its name to be empty or not empty.
Therefore you have to use this instead to hide your component1:
SectionSetText ${Component1} ""

Resources