How to control progress bar from two different tasks - nsis

I have a custom page, and and added a progress bar. Now I have two functions to control that progress bar. One function is to stop few services and other is to back up some files from install location.
Function myCustomPage
Var /GLOBAL progressBar
Var /GLOBAL label
!insertmacro MUI_HEADER_TEXT "Setup is preaparing to install updates" "Please wait"
nsDialogs::Create 1018
${NSD_CreateLabel} 10 15 80% 10% ""
Pop $label
${NSD_CreateProgressBar} 10 30 80% 8% ""
Pop $progressBar
${NSD_CreateTimer} BackUp_Files.Callback 10
${NSD_CreateTimer} Stop_Services.Callback 10
nsDialogs::Show
FunctionEnd
Stop_Services call back function
Function Stop_Services.Callback
${NSD_SetText} $label "Stopping services"
${NSD_KillTimer} Stop_Services.Callback
SendMessage $progressBar ${PBM_SETRANGE32} 0 100
SendMessage $progressBar ${PBM_SETPOS} 25 0
Sleep 100
SendMessage $progressBar ${PBM_SETPOS} 25 0
Sleep 100
SendMessage $progressBar ${PBM_SETPOS} 50 0
Sleep 100
SendMessage $progressBar ${PBM_SETPOS} 75 0
Sleep 100
SendMessage $progressBar ${PBM_SETPOS} 100 0
FuncionEnd
Same kind of structure for Backup_files.callback has.
Result:
When I look at the label its kind of switching between "stopping services" and "backing up files".
Can any one tell me how to handle this. I want to stop the services first then I want to copy the backup files. Progress bar needs to be set to 0 after stopping the services and start again for backing up files. I need to do it on single custom page.

Just use one timer and at the end of the first task call the second task function directly...

Related

NSIS Welcome Image stretching

I have a bitmap 164x314 with my logo which is showing just fine.
When I use header to resize my installer
!include "nsResize.nsh"
and add width and height to it
!define AddWidth 100u
!define AddHeight 75u
I also change dimensions of my bitmap to 164x436 and my logo is looking ugly and stretchy.
Even if I use
!define AddHeight 122
or
!define MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH
it don't do much.
What am I missing?
Left — original, right — NSIS
Solved my problem with using 0px intro file
File "/oname=$PluginsDir\modern-wizard.bmp" "${}\Intro\intro0.bmp"
${NSD_SetImage} "$mui.WelcomePage.Image" "$PLUGINSDIR\modern-wizard.bmp" "$mui.WelcomePage.Image.Bitmap"
${NSD_SetImage} "$mui.FinishPage.Image" "$PLUGINSDIR\modern-wizard.bmp" "$mui.FinishPage.Image.Bitmap"
and setting image later:
${NSD_CreateBitmap} 0 0 164 436 ""
Pop $9
File "/oname=$pluginsdir\welcomeimg.bmp" "${}\Intro\intro96.bmp"
${NSD_SetImage} $9 "$pluginsdir\welcomeimg.bmp" $1

NSIS not displaying new control after change ui

I took ui.cpp and default.rc from the svn, compiled it, used changeui ---> works!
I can't display new controls. This is a section from the new default.rc
IDD_LICENSE DIALOGEX 0, 0, 266, 130 STYLE DS_FIXEDSYS | DS_CONTROL | WS_CHILD
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
ICON IDR_MAINFRAME,IDR_MAINFRAME,0,0,22,20
LTEXT "pppppppppppppppppppppp",507,25,0,241,23
LTEXT "email user",508, 172, 106, 233, 31
CONTROL "",IDC_EDIT1,RICHEDIT_CLASS,WS_BORDER | WS_VSCROLL |
WS_TABSTOP | 0x804,0,24,266,105
END
In the nsh script, on create function of a page I tried:
GetDlgItem $MyHandle $HWNDPARENT507
MessageBox MB_OK $MyHandle //this is always 0 no matter what I tried in the previous line!!!
EnableWindow $MyHandle 1
I also tried editing the original defalt.exe with resource hacker but nsis wouldn't display new text/control.
How can I display the new text and control???
The NSIS GUI is actually one dialog window with another dialog window inside it.
You first need to find the inner dialog before you can find the controls in it:
FindWindow $0 "#32770" "" $HWNDPARENT ; Find the inner dialog (See attached picture)
GetDlgItem $MyHandle $0 507 ; Find the control
Complete example:
!define ResHacker "$%localappdata%\Programs\ResHacker.exe" ; TODO: Adjust this path
!define myui "${__FILE__}\..\mytempui.exe"
!tempfile res
!tempfile rc
!delfile "${rc}"
!delfile "${res}"
!define /redef rc "${res}.rc"
!define /redef res "${res}.res"
!appendfile "${rc}" '#define IDD_LICENSE 102$\r$\n'
!appendfile "${rc}" '#define IDC_EDIT1 1000$\r$\n'
!appendfile "${rc}" '#define RICHEDIT_CLASS "RichEdit20W"$\r$\n'
!appendfile "${rc}" 'LANGUAGE 0x09, 0x01$\r$\n'
!appendfile "${rc}" 'IDD_LICENSE DIALOGEX 0, 0, 266, 130$\r$\n STYLE DS_FIXEDSYS | DS_CONTROL | WS_CHILD$\r$\n'
!appendfile "${rc}" 'FONT 8, "MS Shell Dlg", 0, 0, 0x1$\r$\n'
!appendfile "${rc}" 'BEGIN$\r$\n'
!appendfile "${rc}" ' LTEXT "pppppppppppppppppppppp",507,25,0,241,23,SS_LEFT|WS_VISIBLE$\r$\n'
!appendfile "${rc}" ' LTEXT "email user",508, 172, 106, 233, 31,SS_LEFT|WS_VISIBLE$\r$\n'
!appendfile "${rc}" ' CONTROL "",IDC_EDIT1,RICHEDIT_CLASS,WS_BORDER | WS_VSCROLL | WS_TABSTOP|WS_VISIBLE | 0x804,0,24,266,55$\r$\n'
!appendfile "${rc}" 'END$\r$\n'
!system '"${ResHacker}" -open "${rc}" -save "${res}" -action compile -log CON' = 0
!delfile "${rc}"
!system '"${ResHacker}" -open "${NSISDIR}\Contrib\UIs\default.exe" -save "${myui}" -action addoverwrite -res "${res}" -mask DIALOG,102,1033 -log CON' = 0
!delfile "${res}"
ChangeUI IDD_LICENSE "${myui}"
Page License "" MyLicShow
Page InstFiles
!include WinMessages.nsh
Var MyHandle
Function MyLicShow
FindWindow $0 "#32770" "" $HWNDPARENT ; Find the inner dialog (See attached picture)
GetDlgItem $MyHandle $0 507 ; Find the control
SendMessage $MyHandle ${WM_SETTEXT} "" "STR:New control text"
SetCtlColors $MyHandle ff0000 transparent
FunctionEnd

how to create a modeless dialog in tcl/tk

I'm trying to make a modeless dialog, since whenever a dialog appears stops the real time process that is running in the main gui, after some reasearch i realize that the problem that is causing the real time part to stop is the "vwait" command in the dialog procedure. How could I make the my current dialog modeless so it doesn't affect the real time process in the back? Should I put the msgDialog in a different thread everytime i call the proc ? or what other way can I do it?
TCL CODE:
proc MsgDialog {w message type icon} \
{
if {![winfo exists $w]} {
set dialColor white
image create photo .alert -format PNG -file alertIcon.png -width 40
image create photo .question -format PNG -file questionicon.png
toplevel $w -borderwidth 2 -relief raised -background $dialColor
wm overrideredirect $w 1
set x [expr { ( [winfo vrootwidth $w] - 350 ) / 2 }]
set y [expr { ( [winfo vrootheight $w] - 190 ) / 2 }]
wm geometry $w 350x190+${x}+${y}
frame $w.msgPnl -relief flat -borderwidth 1 -background $dialColor -width 280 -height 140
place $w.msgPnl -x 0 -y 0
frame $w.imgPnl -relief flat -borderwidth 1 -background $dialColor -width 50 -height 140
place $w.imgPnl -x 285 -y 0
frame $w.btnPnl -relief flat -borderwidth 1 -background $dialColor -width 300 -height 50
place $w.btnPnl -x 0 -y 130
label $w.msgPnl.message -text $message -background $dialColor -justify center -wraplength 270 -font dialogFont
pack $w.msgPnl.message -anchor center -pady 20 -padx 10 -expand 1 -fill both
if {$type == "ok"} {
button $w.btnPnl.okbut -text "OK" -background black -foreground white -relief flat -command {set _res "ok"} -width 8 -height 2 -highlightthickness 2 -font boldFont
grid $w.btnPnl.okbut -row 1 -column 1 -padx 125
} elseif {$type == "yesno"} {
button $w.btnPnl.yes -text "Yes" -background black -foreground white -relief flat -command {set _res "yes"} -width 8 -height 2 -highlightthickness 2 -font boldFont
button $w.btnPnl.no -text "No" -background black -foreground white -relief flat -command {set _res "no"} -width 8 -height 2 -highlightthickness 2 -font boldFont
grid $w.btnPnl.yes -row 1 -column 1 -padx 50
grid $w.btnPnl.no -row 1 -column 2
} else {
button $w.btnPnl.okbut -text "OK" -background $btnColor -relief flat -command {set _res "ok"} -width 8 -height 2
pack $w.btnPnl.okbut -side top -anchor center
}
if {$icon == "alert"} {
label $w.imgPnl.alertI -image .alert -compound top -background $dialColor
pack $w.imgPnl.alertI -fill both -expand 1 -pady 20
} elseif {$icon == "question"} {
label $w.imgPnl.quest -image .question -compound top -background $dialColor
pack $w.imgPnl.quest -fill both -expand 1 -pady 20
} else {
label $w.imgPnl.alertI -image .alert -compound top -background $dialColor
pack $w.imgPnl.alertI -fill both -expand 1 -pady 20
}
raise $w
vwait _res
destroy $w
return $::_res
}
}
I was trying something like this, but when i get invalid command name MsgDialog
set tid [thread::create {thread::wait}]
::thread::send -async $tid {MsgDialog .dialog "Are you ready for measurement ?" yesno question} answer
vwait answer
if {$answer == yes} {
#do something
}
Your dialog proc is fundamentally modal, since it returns a value. It therefore blocks until the user responds, because it can't return its value until the user gives it one.
To make it modeless, build it to just create itself and return. The buttons all then need to call procs (either global or with some fully qualified name) that will set the user value in some place you're waiting for it, then destroy the dialog properly.
This means that your "return" value must be global, the window ID variable must be global, the handler proc/procs must be global, and you'll need to trigger whatever processing you want the value for in some way that's too application specific for me to guess. It's a fair bit of work, but it's easier than trying to incorporate the threading library.
Have you tried simply removing the vwait? The difference between a modal dialog and a non-modal one is really nothing more than the modal dialog calling vwait and then doing a grab on the keyboard and mouse.

Is there anyway to call function after nsDialog show?

I wonder if there is any way which I can call function after nsDialog::show.
My case is a custom page that need to run http get request after the nsDialog show.
Function customPage
nsDialogs::Create 1018
Pop $0
${NSD_CreateButton} 0 0 100% 12u Test
Pop $BUTTON
${NSD_CreateText} 0 35 100% 12u hello
Pop $EDIT
${NSD_CreateCheckbox} 0 -20 100% 8u Test
Pop $CHECKBOX
${NSD_CreateLabel} 0 40u 75% 40u "* Type `hello there` above.$\n* Click the button.$\n* Check the checkbox.$\n* Hit the Back button."
Pop $0
nsDialogs::Show
Call MyFunc
FunctionEnd
Actually, the call for MyFunc is occuring only when im closing the nsDialog..
Use nsDialogs::CreateTimer with a short timeout and kill the timer in the function callback, it is a bit of a hack but seems to work fine.
nsDialog running by solo thread, but actually you can use nsDialogs::CreateTimer to callback a function without specify time which.
You can read more about that on:
http://nsis.sourceforge.net/Docs/nsDialogs/Readme.html#ref-createtimer

Ways to update progress bar

I want to control progressbar in my nsis installer. Actually I'm using ThreadTimer plugin but I've got big problem with it. ThreadTimer runs function every 10 seconds to update progress bar (increace progress bar value by 1%). The problem is that it crashes stack (as I can see ThreadTimer is using the same stack as NSIS). Crashes stack means that when I want to get value from the stack the value is wrong because of ThreadTimer function action. Any ideas? Maybe there are some other ways to update progress bar?
Those are functions/macros which should update progress bar. Macro StartProgressBarIntervalUpdate starts updating progress bar, macro StopProgressBarIntervalUpdate stops doping it.
Var /GLOBAL ProgressBarPosition
Var /GLOBAL ProgressBarParentWindow
Var /GLOBAL ProgressBarItem
Function InitProgressBar
StrCpy $ProgressBarPosition "0"
FindWindow $ProgressBarParentWindow "#32770" "" $HWNDPARENT
GetDlgItem $ProgressBarItem $ProgressBarParentWindow 1004
FunctionEnd
Function UpdateProgressBarTimer
${If} $ProgressBarPosition >= 30000 ; 100% * 300
StrCpy $ProgressBarPosition "0"
${Endif}
IntOp $ProgressBarPosition $ProgressBarPosition + 300
SendMessage $ProgressBarItem ${PBM_SETPOS} $ProgressBarPosition 0
FunctionEnd
!define StartProgressBarIntervalUpdate "!insertmacro StartProgressBarIntervalUpdate"
!macro StartProgressBarIntervalUpdate
Call InitProgressBar
GetFunctionAddress $UpdateProgressBarTimerFunctionAddress UpdateProgressBarTimer
ThreadTimer::Start /NOUNLOAD 20 -1 $UpdateProgressBarTimerFunctionAddress
Sleep 1000
!macroend
!define StopProgressBarIntervalUpdate "!insertmacro StopProgressBarIntervalUpdate"
!macro StopProgressBarIntervalUpdate
ThreadTimer::Stop $UpdateProgressBarTimerFunctionAddress
Sleep 15000
!macroend
Here are sections which uses progressbar
Var /GLOBAL UpdateProgressBarTimerFunctionAddress
Section BeforeMoveData SEC01
${StartProgressBarIntervalUpdate}
Call core.UnpackExeData
SectionEnd
Section OnMoveData SEC02
Call InstallFiles
Call InstallRegistry
Call InstallShortcuts
${StopProgressBarIntervalUpdate}
...
SectionEnd

Resources