Archive: Calculating Space required


Calculating Space required
Is there any way to calculate the Space Required for installation?


This is done automatically and displayed one the components page or directory page (can't remember which!)

Stu


I have created a custom page, so how to calculate the Space required in that page


Enumerate all of the sections, use SectionGetSize for each one and some it up with IntOp.


Note that on 32bits Windows, IntOp will not allow numbers larger than 4294967296 (4GB). Use the Math plugin for larger installations.

If your problem is more related to customizing the space required depending on what files are already present on the target system, here's my code. I cloned the DIRECTORY page using nsDialogs and made it update the Space Required field realtime depending on what directory name is currently in the Install To: field.

Function DIRECTORYpage
nsDialogs::Create /NOUNLOAD 1018
Pop $2
GetDlgItem $1 $HWNDPARENT 1
EnableWindow $1 0
${NSD_CreateLabel} 0u 0u 300u 18u "Setup will install $(^Name) in the following folder. \
To install in a different folder, click Browse and select \
another folder. Click Next to continue."
Pop $2
${NSD_CreateGroupBox} 0u 70u 300u 35u "Destination Folder"
Pop $2
${NSD_CreateDirRequest} 10u 85u 210u 12u "$INSTDIR"
Pop $NSDFIELD1
${NSD_OnChange} $NSDFIELD1 DIRECTORYField1
${NSD_CreateBrowseButton} 229u 83u 59u 15u "Br&owse..."
pop $NSDBUTTON1
${NSD_OnClick} $NSDBUTTON1 DIRECTORYButton1
${NSD_CreateLabel} 0u 115u 100u 10u "Space required: 0MB"
pop $NSDFIELD2
${NSD_CreateLabel} 0u 125u 100u 10u "Space available: 0MB"
pop $NSDFIELD3
call DIRECTORYField1
SendMessage $NSDFIELD1 ${EM_SETSEL} 0 -1
${NSD_SetFocus} $NSDFIELD1
!insertmacro MUI_HEADER_TEXT "Choose Install Location" "Choose the folder in which to install $(^Name)."
nsDialogs::Show
FunctionEnd


Function DIRECTORYField1
System::Call 'user32::GetWindowText(i $NSDFIELD1, t.r0, i${NSIS_MAX_STRLEN})'
StrCpy $1 $0 2 1
${If} $1 == ":\"
StrCpy $1 $0 2
System::Call 'kernel32::GetDiskFreeSpaceExA(t, *l, *l, *l)i(r1,.r2,.,.) .r3'
${If} $3 == 0
;could not detect free space, drive probably doesn't exist. Disable Next button.
SendMessage $NSDFIELD2 ${WM_SETTEXT} 0 "STR:Space required: 0MB"
GetDlgItem $1 $HWNDPARENT 1
EnableWindow $1 0
goto DIRECTORYField1end
${EndIf}
StrCpy $INSTDIR $0

#(call some function that computes how much space is
# needed, depending on what files were found in $INSTDIR)

Math::Script 'r1 = ff(f(r2)/1048576,17); #[r1 > 999, r1 = ff(r1/1024,17) + "GB", r1 = r1 + "MB"]'
SendMessage $NSDFIELD3 ${WM_SETTEXT} 0 "STR:Space available: $1"
Math::Script 'r1 = ff(f($SPACENEEDED)/1048576,17); #[r1 > 999, r1 = ff(r1/1024,17) + "GB", r1 = r1 + "MB"]'
SendMessage $NSDFIELD2 ${WM_SETTEXT} 0 "STR:Space required: $1"

;Prevent installing to root of $WINDIR, $WINDIR itself,
;$PROGRAMFILES or $SYSDIR (put down here because the
;Space fields should still update)
StrCpy $0 $INSTDIR 3
StrCpy $3 $WINDIR 3
${If} $0 == $3
${OrIf} $INSTDIR == "$PROGRAMFILES"
${OrIf} $INSTDIR == "$WINDIR"
${OrIf} $INSTDIR == "$SYSDIR"
GetDlgItem $1 $HWNDPARENT 1
EnableWindow $1 0
goto GetINSTDIRField1end
${EndIf}

Math::Script '#[$2 > $SPACENEEDED, r1 = "EnoughSpace"]'
${If} $1 == "EnoughSpace"
GetDlgItem $1 $HWNDPARENT 1
EnableWindow $1 1
${Else}
GetDlgItem $1 $HWNDPARENT 1
EnableWindow $1 0
${EndIf}
${Else}
SendMessage $NSDFIELD2 ${WM_SETTEXT} 0 "STR:Space required: 0MB"
GetDlgItem $1 $HWNDPARENT 1
EnableWindow $1 0
${EndIf}
DIRECTORYField1end:
FunctionEnd


The function for the Browse button is as follows, in case you can use it:
Function DIRECTORYButton1
;remove all trailing backslashes and spaces from $INSTDIR
StrCpy $2 $INSTDIR
StrCpy $1 $2 1 -1
StrCmp $1 '\' +2
StrCmp $1 ' ' 0 +3
StrCpy $2 $2 -1
goto -3
;Get the deepest existing subdirectory in $INSTDIR
${Do}
IfFileExists "$2\*.*" 0 +2
${ExitDo}
${CutBeforeLastBackslash} 3 $2
StrLen $1 $3
${If} $1 > 2
StrCpy $2 $3
${Else}
${ExitDo}
${EndIf}
${Loop}
nsDialogs::SelectFolderDialog "Select folder" $2
pop $3
StrCmp $3 "error" +2
StrCpy $INSTDIR $3
SendMessage $NSDFIELD1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
FunctionEnd


And this uses the following macro:
;  Cut off a string just before its last backslash.
; _outvar is the number/name of the variable to write to (0 for $0, 2 for $2,
; NEVER USE $R# REGISTERS), _input is any string.
; Example:
;
; StrCpy $2 "c:\program files\longpath\subdir"
; ${CutBeforeLastBackslash} 3 $2
; # $2 is now: c:\program files\longpath\subdir
; # $3 is now: c:\program files\longpath
!macro _CutBeforeLastBackslash _outvar _input
Push $R0
Push $R1
Push $R2
!define Lprefix L${__LINE__}
StrCpy $R0 ${_input}
${Do}
StrLen $R2 $R0
${If} $R2 == "0"
${ExitDo}
${EndIf}
StrCpy $R1 $R0 "" -1
StrCpy $R0 $R0 -1
${If} $R1 == "\"
${ExitDo}
${EndIf}
${Loop}
StrCpy $${_outvar} $R0
!undef Lprefix
Pop $R2
Pop $R1
Pop $R0
!macroend
!define CutBeforeLastBackslash `!insertmacro _CutBeforeLastBackslash`