design-patternsfreezensisnsdialogs

A way to warn the user about a little freeze by a function doing calculations in a NSIS installer


I'm doing my first NSIS script but i found a bump in the road. In fact i think is hard to explain only in the title, (maybe someone can help with that to) so let me explain fully:

I'm creating a installer that uses a few custom pages because i want the user select some options first (it uses nsDialogs), and depending of that do some tweaks in the with installation path (mostly autodetect it because it could depend to other things). All of this is working fine.

In some cases of that options, between checking if some files exists, it hash a file to look if the file is the one it expects (because it's going to patch it with a delta later). I used Crypto plugin or MD5 plugin, both are fine and both do what i want, but they hang the installer for a while (a second or so), i suppose because the file is a little big (it's about more than 100MB) and near that is the issue.

Normally in these cases, you select the option, goes to the next (custom) page, and in the creator function of the custom page autodetects the folder, and it directly do the file checks and when is checking the file hash, it hangs for a second and continues, but all this time hanged it only shows a blank page, because it didn't reach yet in the creator function the nsDialogs::Show instruction to show the window content. In that page you can change the folder, and if it's the case, once is changed it runs the checks again (it's a dedicated function that was called in both cases) and hangs again for a bit, but then the window shows everything and i can set a text to say something (in fact, it is what i did first), but with that automatic first time i can't do this.

That's the point: how to show something to the user to aware them about the installer is doing the hash calculations, instead showing only a blank window.

What I have tried or thought to do:

if it is not well understood the topic, i could add a little example tomorrow created from scratch to show this, because my main test is so big that is not point to paste all of that here.

Thanks!

EDIT:

This is the original example with the issue (Don't forget to add a path to a big file as marked):

Name "Example HASH Freeze"
Outfile "ExampleHASHFreeze.exe"

RequestExecutionLevel user
Unicode True
XPStyle on

!include nsDialogs.nsh
!include LogicLib.nsh

Page Custom FirstCreate
Page Custom SecondCreate
Page instfiles

var file
var hash
var info

Function FirstCreate

    StrCpy $file "" ; Add a path to a big file to do the hash. 150 MB or more.
    
    nsDialogs::Create 1018
    ${NSD_CreateLabel} 0u 64u 100% 12u "Hashing: $file"
    nsDialogs::Show
FunctionEnd

Function SecondCreate
    
    StrCpy $hash "hashing..."

    nsDialogs::Create 1018
    ${NSD_CreateLabel} 0u 58u 100% 12u "Hashing: $file"
    ${NSD_CreateLabel} 0u 70u 100% 12u "Hash: $hash"
    Pop $info

    call hashFile
    
    nsDialogs::Show
FunctionEnd

Function hashFile
    ${If} ${FileExists} "$file"
        md5dll::GetMD5File "$file"  ; Using MD5 Plugin
        ; Crypto::HashFile "MD5" "$file" ; Using Crypto Plugin
        Pop $0
        ${NSD_SetText} $info "Hash: $0" 
    ${Else}
        ${NSD_SetText} $info "Hash: FILE NOT FOUND" 
    ${EndIf}
FunctionEnd

Section
    MessageBox MB_OK "Hello world!"
SectionEnd

However, with Anders tips to use Banner plugin (that is what i was searching!), and BgWorker plugin with a nsDialogs Timer, nsDialogs render the window and at the same time it does the hash showing a banner, so now looks perfect! (Don't forget to add a path to a big file as marked).

Name "Example HASH Freeze Fix"
Outfile "ExampleHASHFreezeFix.exe"

RequestExecutionLevel user
Unicode True
XPStyle on

!include nsDialogs.nsh
!include LogicLib.nsh

Page Custom FirstCreate
Page Custom SecondCreate
Page instfiles

var file
var hash
var info

Function FirstCreate

    StrCpy $file "" ; Add a path to a big file to do the hash. 150 MB or more.
    
    nsDialogs::Create 1018
    ${NSD_CreateLabel} 0u 64u 100% 12u "Hashing: $file"
    nsDialogs::Show
FunctionEnd

Function SecondCreate
    
    StrCpy $hash "hashing..."

    nsDialogs::Create 1018
    ${NSD_CreateLabel} 0u 58u 100% 12u "Hashing: $file"
    ${NSD_CreateLabel} 0u 70u 100% 12u "Hash: $hash"
    Pop $info
    
    GetFunctionAddress $0 onShow_hack
    nsDialogs::CreateTimer $0 1
    
    nsDialogs::Show
FunctionEnd

Function hashFile
    ${If} ${FileExists} "$file"
        Banner::show "Calculating Hash..."
        md5dll::GetMD5File "$file"  ; Using MD5 Plugin
        ; Crypto::HashFile "MD5" "$file" ; Using Crypto Plugin
        Pop $0
        Banner::destroy
        ${NSD_SetText} $info "Hash: $0" 
    ${Else}
        ${NSD_SetText} $info "Hash: FILE NOT FOUND" 
    ${EndIf}
FunctionEnd

Function onShow_hack
    GetFunctionAddress $0 ${__FUNCTION__}
    nsDialogs::KillTimer $0
    GetFunctionAddress $0 hashFile
    BgWorker::CallAndWait
FunctionEnd

Section
    MessageBox MB_OK "Hello world!"
SectionEnd

Maybe it should disable the buttons meanwhile it's doing the calculations are not done but those things are easy. Thanks!


Solution

  • You are not really supposed to do heavy work on the custom pages. You can use the BgWorker plug-in to do background work. Combine that with a timer hack and you get this:

    !include nsDialogs.nsh
    
    Page Custom mypage
    
    Function hashfile
    Crypto::HashFile "MD5" "$somefilepath"
    Pop $0
    MessageBox "" $0
    FunctionEnd
    
    Function onShow_hack
    GetFunctionAddress $0 ${__FUNCTION__}
    nsDialogs::KillTimer $0
    GetFunctionAddress $0 hashfile
    BgWorker::CallAndWait
    FunctionEnd
    
    Function mypage
    nsDialogs::Create 1018
    Pop $0
    ${NSD_CreateButton} 0 13u 100% 12u "I do nothing"
    Pop $0
    GetFunctionAddress $0 onShow_hack
    nsDialogs::CreateTimer $0 1
    nsDialogs::Show
    FunctionEnd
    
    Section
    SectionEnd
    

    The Banner plug-in allows you to display a overlayed message...