Search code examples
nsiselectron-builder

NSIS install path validation


I want to validate the installation path selected by the user. I can't figure out how to check that so it will look like this:

  • You can't select a path with spaces (except Program Files)
  • When you click "Install" then it will prompt the error saying that you have to change the installation directory

For now I have this:

  Function StrStr
  Exch $1 ; st=haystack,old$1, $1=needle
  Exch    ; st=old$1,haystack
  Exch $2 ; st=old$1,old$2, $2=haystack
  Push $3
  Push $4
  Push $5
  StrLen $3 $1
  StrCpy $4 0
  ; $1=needle
  ; $2=haystack
  ; $3=len(needle)
  ; $4=cnt
  ; $5=tmp
  loop:
    StrCpy $5 $2 $3 $4
    StrCmp $5 $1 done
    StrCmp $5 "" done
    IntOp $4 $4 + 1
    Goto loop
  done:
  StrCpy $1 $2 "" $4
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Exch $1
FunctionEnd

Function .onVerifyInstDir
Push "$INSTDIR"
Push " "
Call StrStr
Pop $0
StrCpy $0 $0 1
StrCmp $0 " " 0 +2
  Abort
FunctionEnd

It refuses to install while there is any space in the path. I need to modify this so Program Files will be the only exception for that rule. Also, printing error message would be helpful


Solution

  • This restriction makes no sense to me. Some legacy applications can't handle spaces in the path but that of course also includes the Program Files folder (although the progra~1 hack can be used as a workaround if short name generation is active).

    NSIS does not have a specific way to display a error/warning message directly on the page but you can change existing text in the UI and/or display a balloon.

    !include WinMessages.nsh
    !define /IfNDef EM_SHOWBALLOONTIP 0x1503
    !define /IfNDef EM_HIDEBALLOONTIP 0x1504
    
    !define DIRPAGE_CHANGETEXT ; Remove this line to disable the text change
    !define DIRPAGE_BALLOON    ; Remove this line to disable the balloon
    Function .onVerifyInstDir
        FindWindow $9 "#32770" "" $HWNDPARENT
    !ifdef DIRPAGE_CHANGETEXT
        GetDlgItem $3 $9 1006 ; IDC_INTROTEXT
        LockWindow on
    !endif
        StrCpy $1 0
        loop:
            StrCpy $2 $InstDir 1 $1
            StrCmp $2 '' valid ; End of string
            StrCmp $2 ' ' found_space
            IntOp $1 $1 + 1
            Goto loop
    valid:
    !ifdef DIRPAGE_CHANGETEXT
        SetCtlColors $3 SYSCLR:18 SYSCLR:15
        SendMessage $3 ${WM_SETTEXT} "" "STR:$(^DirText)"
        LockWindow off
    !endif
    !ifdef DIRPAGE_BALLOON
        GetDlgItem $3 $9 1019
        SendMessage $3 ${EM_HIDEBALLOONTIP} "" "" ; Not required?
    !endif
        Return
    found_space:
        StrLen $1 "$ProgramFiles\"
        StrCpy $2 "$InstDir\" $1
        StrCmp $2 "$ProgramFiles\" valid
    !ifdef DIRPAGE_CHANGETEXT
        SetCtlColors $3 ff0000 transparent
        SendMessage $3 ${WM_SETTEXT} "" "STR:Paths with spaces are not allowed, except for $ProgramFiles for some reason!"
        LockWindow off
    !endif
    !ifdef DIRPAGE_BALLOON
        GetDlgItem $3 $9 1019
        System::Call '*(&l${NSIS_PTR_SIZE},w "Bad path!", w "Spaced not allowed in path!",p 3)p.r2'
        SendMessage $3 ${EM_SHOWBALLOONTIP} "" $2 ; This will only work on XP and later (and you must use "XPStyle on")
        System::Free $2
    !endif
        Abort
    FunctionEnd
    
    XPStyle on
    Page Directory
    

    The Next button is disabled when Abort is called inside .onVerifyInstDir. If you want to display a MessageBox when the user clicks next then you can't call Abort in .onVerifyInstDir, you will have to use the page leave function callback (where you have to verify the path again and maybe call MessageBox+Abort).