How to Enable the disabled Next button in NSIS? - nsis

In My custom page, I have disabled the next button while the page loading... Because there is some background process need to complete, until we cant go to the next page. So I have disable the next button using following code.
GetDlgItem $1 $HWNDPARENT 1
EnableWindow $1 0
But I want to enable the next button once background process completed. I have used the follwoing code to enable
GetDlgItem $1 $HWNDPARENT 1
EnableWindow $1 1
But it was not worked... cld u please suggest the mistake done by me or how achieve this in NSIS?
Following is My Code
Function StartProgressPage
; Set dialog text:
!insertmacro MUI_HEADER_TEXT "$(STARTPRGDIA_TITLE)" "$(STARTPRGDIA_SUBTITLE)"
; Display the page:
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "StartPrgDia"
;Disable Next Button
GetDlgItem $0 $HWNDPARENT 1
EnableWindow $0 0
nsDialogs::Create 1018
Pop $dialog
nsDialogs::CreateControl "msctls_progress32" \
${DEFAULT_STYLES}|${PBS_SMOOTH} \
${PB_EXSTYLE} \
0 30 100% 10% \
"Test" \
Pop $hwnd
${NSD_CreateTimer} NSD_Timer.CallStop 10
nsDialogs::Show
FunctionEnd
Function NSD_Timer.CallStop
${NSD_KillTimer} NSD_Timer.CallStop ; Kill the timer
SendMessage $hwnd ${PBM_SETRANGE32} 0 100
SendMessage $hwnd ${PBM_SETPOS} 25 0
Call <Some Process>
SendMessage $hwnd ${PBM_SETPOS} 50 0
Call ConfPropertyChanges
SendMessage $hwnd ${PBM_SETPOS} 100 0
${NSD_CreateLabel} 0 10 20% 10u Completed
;Enable Next Button
GetDlgItem $0 $HWNDPARENT 1
EnableWindow $0 1
FunctionEnd

As far as I know installer will wait inside this code:
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "StartPrgDia"
until user goes to the next page.
Are you sure that the code bellow this line is executed when custom page is displayed?
You should call
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "StartPrgDia"
after all your code or use this(MUI2 version)
Function CustomPage
!insertmacro INSTALLOPTIONS_INITDIALOG "page.ini"
# your code here
!insertmacro INSTALLOPTIONS_SHOW
FunctionEnd

Related

Downloading files in Custom NSIS Page

I want to create an installer that downloads a file without freezing the UI using the inetc plugin. My question is that possible without using the ExecDos::Exec with the /ASYNC flag and then ExecDos::Wait.
In addition , i do not want installation page in my installer.
Here is my code:
!macro DOWNLOAD_PAGE
Function myTimer
${If} $0 == "OK"
${NSD_KillTimer} myTimer
SendMessage $hPBar ${PBM_SETRANGE32} 0 100
SendMessage $hPBar ${PBM_SETPOS} 100 0
Return
${EndIf}
SendMessage $hPBar ${PBM_SETRANGE32} 0 100
SendMessage $hPBar ${PBM_SETPOS} 50 0
FunctionEnd
function Page1
nsDialogs::Create 1018
Pop $Dialog
${NSD_CreateLabel} 0 0 100% 50% "Starting download..."
Pop $hInfo
${NSD_CreateProgressBar} 0 55% 100% 10u ""
Pop $hPBar
${NSD_CreateTimer} myTimer 1000
inetc::get /silent "$URL\skype.exe" "$EXEDIR\skype.exe" /end
Pop $0
nsDialogs::Show
functionEnd
function Page2
functionEnd
Page Custom Page1 Page2
!macroend
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "${NSISDIR}\Docs\Modern UI\License.txt"
!insertmacro DOWNLOAD_PAGE
!insertmacro MUI_PAGE_FINISH
Or the only option i have is using the ExecDos::Exec with the /ASYNC flag?
Perhaps, a solution it is to create another nsis installer that does the download?
Performing a long operation between nsDialogs::Create and nsDialogs::Show will hang the UI.
The easy solution is to download in a Section because those are executed in a different thread. You are supposed to perform most of your tasks in a Section.
If you insist on downloading something on a custom page then you can use the InetBgDL plug-in:
!include nsDialogs.nsh
!include LogicLib.nsh
Page Custom Page1
Var hInfo
Function Page1
nsDialogs::Create 1018
Pop $0
${NSD_CreateLabel} 0 0 100% 50% "Starting download..."
Pop $hInfo
InitPluginsDir
INetBgDl::Get "https://example.com/file.exe" "$PluginsDir\file.exe" /end
${NSD_CreateTimer} myTimer 1000
nsDialogs::Show
${NSD_KillTimer} myTimer
FunctionEnd
Function myTimer
InetBgDL::GetStats
${NSD_SetText} $hInfo "Status=$0$\nCompleted files=$1$\nDownloaded $3 of $4 bytes"
${If} $0 = 0 ; HTTP status code or 0 if completed without errors
${NSD_KillTimer} myTimer
MessageBox mb_ok Done
${EndIf}
FunctionEnd

nsDialogs::Show accepts ENTER key even if all buttons from UI are destroyed

I have a small bootstrapper with custom UI, which contains two pages, one to configure the install, and the other showing a progressbar for the download and install of the payload.
My problem:
If the user presses Enter key when in the second page, the installer exits, even if it didn't finish installing.
I removed all the controls I could from the UI with:
GetDlgItem $1 $HWNDPARENT 1 //(2, and 3)
System::Call `User32::DestroyWindow(i $1)`
in the onGuiInit function, and the first page ignores enter and space keys now, but the second page still exits on Enter key.
I have no other components on that page except some labels, a progressbar, and a slideshow (nsisSlideshow plugin).
In the background I have a thread which downloads and installs the payload.
The pages are declared like this:
Page Custom Options_Show Options_Leave
Page Custom Progress_Show Progress_Leave
So, long story short, when I press enter, the nsDialogs::Show function returns, killing the installer.
Any way I can stop it from doing this?
You really should try to stop forward page changes by calling Abort in the page leave callback function.
NSIS only blocks clicks from buttons that exist and are disabled.
!include nsDialogs.nsh
Page Custom Options_Show #Options_Leave
Page Custom Progress_Show #Progress_Leave
Function DisableBottomButtons
GetDlgItem $0 $hwndparent 1
ShowWindow $0 0
EnableWindow $0 0
GetDlgItem $0 $hwndparent 2
ShowWindow $0 0
EnableWindow $0 0
GetDlgItem $0 $hwndparent 3
ShowWindow $0 0
EnableWindow $0 0
FunctionEnd
Function Options_Show
nsDialogs::Create 1018
Pop $0
${NSD_CreateButton} 0 20 100% 12u "Go to next page"
Pop $0
${NSD_OnClick} $0 GoToNextPage
Call DisableBottomButtons
nsDialogs::Show
FunctionEnd
Function GoToNextPage
GetDlgItem $0 $hwndparent 1
EnableWindow $0 1
SendMessage $hwndparent ${WM_COMMAND} 1 0
FunctionEnd
Function Progress_Show
nsDialogs::Create 1018
Pop $0
${NSD_CreateButton} 0 20 100% 12u "Exit"
Pop $0
${NSD_OnClick} $0 ExitApp
Call DisableBottomButtons
nsDialogs::Show
FunctionEnd
Function ExitApp
MessageBox mb_ok "Bye"
; Faking a click on Next/Close will not work when the button is disabled,
; this uses a slightly ugly trick to bypass that. GoToNextPage does it properly.
!define /math WM_NOTIFY_OUTER_NEXT ${WM_USER} + 0x8
SendMessage $hwndparent ${WM_NOTIFY_OUTER_NEXT} 1 0
FunctionEnd
First, small code snippet:
!macro EnableNextButtonM IsEnable
Push $1
GetDlgItem $1 $HWNDPARENT 1 ;next button
EnableWindow $1 ${IsEnable}
Pop $1
!macroend
!define EnableNextButton "!insertmacro EnableNextButtonM"
Then, in function Progress_Show you should call
${EnableNextButton} 0
before
nsDialogs::Show
to disable Next button before it will be shown.
After download completion (at the download callback) call
${EnableNextButton} 1
to enable it again.
You variant with .onGuiInit does not work because it calls once - before first page will be shown. But every next page will be created dynamically again, thus, all UI page must be implemented into page custom -Pre function.

Custom font on finish page not working

I have code like this
!define MUI_FINISHPAGE_TITLE_3LINES
!define MUI_PAGE_CUSTOMFUNCTION_SHOW WelcomeAndFinishPageShow
!insertmacro MUI_PAGE_FINISH
Function WelcomeAndFinishPageShow
FindWindow $1 "#32770" "" $HWNDPARENT
GetDlgItem $R0 $1 1201
CreateFont $R1 $(^Font) 10 600
SendMessage $R0 ${WM_SETFONT} $R1 0
FunctionEnd
But some how on finish page I am not seeing the changed font size.
Any ideas where I am doing wrong?
First thing you should do is check $R0, if it is 0 then GetDlgItem did not find the label (Those IDs might not be totally stable, different MUI settings might change the order in which controls are created)
If you are using MUI2 there are some (under-documented) variables you can use:
Function WelcomeAndFinishPageShow
CreateFont $R1 "Comic Sans MS" 10 600
SendMessage $mui.WelcomePage.Title ${WM_SETFONT} $R1 0
SendMessage $mui.FinishPage.Title ${WM_SETFONT} $R1 0
FunctionEnd

Radio button with Immeditate action using NSIS?

I have created customize page with two radio button for repair and remove.If the user select the repair button it goes for repair fun.but again come to old page that is repair and remove page.so the user selects both option.My requirement is user selects only one option and do corresponding actions only.No need to give the option to select both the buttons.
Function nsDialogpage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateRadioButton} 0 5u 100% 10u "Repair"
Pop $Repair
${NSD_AddStyle} $Repair ${WS_GROUP}
${NSD_OnClick} $Repair Repair
${NSD_CreateRadioButton} 0 25u 100% 56u "Remove"
Pop $Remove
${NSD_OnClick} $Remove Remove
nsDialogs::Show
FunctionEnd
Function Repair
-- Do repair function.
FunctionEnd
Function Remove
ExecWait "$INSTDIR\uninstall.exe"
Quit
FunctionEnd
also if remove fun have remove the installer and quit the installation.
uninstaller has been done successfully.but installer again go main page after that user click any button then only quit from installation steps.
1.User select any one option and leave this repair and remove page.
2.If user click remove option remove the installer and immediately quit the process.
Quit does not quit right away when used in a page action callback, it will quit when the page changes. Performing some action when a radio button is clicked is not normal Windows behavior (Try pressing TAB) so I added a normal example aswell.
!include nsDialogs.nsh
Page Custom RepairOrRemovePageInsane_Create
Page Custom RepairOrRemovePageNormal_Create RepairOrRemovePageNormal_Leave
Page InstFiles
Function Remove
ExecWait '"cmd" /C echo This is a dummy uninstall command...&pause' ; Call real uninstaller here
FunctionEnd
Function Repair
# Do repair....
FunctionEnd
Function RepairOrRemovePageInsane_Create
nsDialogs::Create 1018
Pop $0
${NSD_CreateRadioButton} 0 5u 100% 10u "Repair"
Pop $0
SendMessage $HWNDPARENT ${WM_NEXTDLGCTL} $0 1
${NSD_OnClick} $0 RepairInsane
${NSD_CreateRadioButton} 0 25u 100% 56u "Remove"
Pop $0
${NSD_OnClick} $0 RemoveInsane
GetDlgItem $0 $hwndparent 1 ; Get Next button handle and
EnableWindow $0 0 ; ...disable it because it does not make sense with this insane radio button handling
nsDialogs::Show
FunctionEnd
Function RemoveInsane
Call Remove
SendMessage $HWNDPARENT ${WM_CLOSE} 0 0
FunctionEnd
Function RepairInsane
Call Repair
SendMessage $HWNDPARENT 0x408 1 0 ; Go to next page
FunctionEnd
Function RepairOrRemovePageNormal_Create
nsDialogs::Create 1018
Pop $1
${NSD_CreateRadioButton} 0 5u 100% 10u "Repair"
Pop $1
${NSD_CreateRadioButton} 0 25u 100% 56u "Remove"
Pop $2
SendMessage $1 ${BM_CLICK} 0 0 ; Select one of them by default
nsDialogs::Show
FunctionEnd
Function RepairOrRemovePageNormal_Leave
${NSD_GetState} $1 $0
${If} $0 <> 0
Call Repair
${Else}
Call Remove
Quit
${EndIf}
FunctionEnd

How can I get a finish button after a nsDialogs page

I'm trying to create a post install configuration page in my nsis script using nsDialogs. My script for gathering the input and executing the configuration works however I'm never presented with a finish/close/exit button after I'm done. Currently my pages declaration looks like:
Page directory
Page instfiles
Page custom nsDialogsPage nsDialogsPageLeave
How can I get a finish/exit/done button to show after nsDialogsPageLeave executes?
The classic NSIS UI does not have a finish page, the instfiles page is usually the last page and it will show a "finish button" after all the sections have executed. You can set the text of any button to the same string with SendMessage $hwndButton ${WM_SETTEXT} 0 "STR:$(^CloseBtn)" if you want to provide your own finish page.
Most installers request the required information before the instfiles page, if you cannot do this then you might want to use the Modern UI, it will provide a finish page for you:
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
Page custom nsDialogsPage nsDialogsPageLeave
!insertmacro MUI_PAGE_FINISH
It was a little unclear to me if you wanted two pages; a input page and then a finish page or a combined input/finish page. A combined page is a little weird but it is possible:
!define AppName "Test"
Name "${AppName}"
Outfile "${AppName} setup.exe"
InstallDir $temp
!include LogicLib.nsh
!include WinMessages.nsh
!include nsDialogs.nsh
Var MyEndConfigPageStage
Page Directory
Page InstFiles
Page Custom MyEndConfigPageCreate MyEndConfigPageLeave /EnableCancel
Function MyEndConfigPageCreate
StrCpy $MyEndConfigPageStage 0
GetDlgItem $0 $hwndparent 1
SendMessage $0 ${WM_SETTEXT} 0 "STR:&Apply"
nsDialogs::Create 1018
Pop $0
${NSD_CreateCheckBox} 0 13u 100% -13u "FooBar"
Pop $1
nsDialogs::Show
FunctionEnd
Function MyEndConfigPageLeave
${If} $MyEndConfigPageStage > 0
Return
${EndIf}
${NSD_GetState} $1 $2
ClearErrors
WriteIniStr "$instdir\settings.ini" Dummy "FooBar" $2
${If} ${Errors}
MessageBox mb_iconstop "Unable to apply settings!"
Abort
${EndIf}
IntOp $MyEndConfigPageStage $MyEndConfigPageStage + 1
GetDlgItem $0 $hwndparent 1
SendMessage $0 ${WM_SETTEXT} 0 "STR:$(^CloseBtn)"
GetDlgItem $0 $hwndparent 2
EnableWindow $0 0 ;Disable cancel
EnableWindow $1 0 ;Disable the checkbox
Abort
FunctionEnd
Section
SectionEnd

Resources