views:

672

answers:

3

In silent install mode the user is not asked about the installation target with the PageEx directory, and therefore the functions DirVerify and GetInstDirError are never called.

This is also applicable to installs that hardcode the installation target (a bad idea) for the same reason as above: the PageEx directory is never invoked.

+1  A: 

I wrote a function called CheckFreeSpace in NSIS to do this. Unfortunately it has the following limitations:

  • To compute the size of all the sections in your installation, you have to modify CheckFreeSpace to add each section, by knowing each variable that each section id was written into. I can't find a way of iterating over all sections that will be installed using NSIS.
  • Installation drive must be computed because ${DriveSpace} requires a drive letter, not a path to an arbitrary directory. The drive letter string is computed with StrCpy $instdrive $INSTDIR 3. If the $INSTDIR variable is a relative path or does not begin with a string such as C:\, this will fail.
  • If the installation cannot continue it produces a MessageBox. You can suppress the MessageBox by adding /SD IDOK at the end of the statement, but then the user is not informed of the installation failure: I can't find a way to emit to stdout from NSIS. Perhaps the return code from the installer is enough?
  • If the disk free space is really low (like 10kb), the installer won't run at all; it doesn't have space to unpack its temporary DLL's into the \tmp directory.

Also, in my implementation below, CheckFreeSpace has a hardcoded value for the space free after installation. Obviously that can be parameterized.

Here it is within a sample installer:

!include FileFunc.nsh
!insertmacro DriveSpace

Name "CheckFreeSpace"
OutFile "C:\CheckFreeSpace.exe"

InstallDir C:\tmp\checkfreespace

Page instfiles

Section "install_section" install_section_id
    Call CheckFreeSpace

    CreateDirectory $INSTDIR
    SetOutPath $INSTDIR
    File "C:\installme.bat"

    WriteUninstaller "$INSTDIR\Uninstall.exe"

    DetailPrint "Installation Successful."
SectionEnd

Section "Uninstall"

    RMDIR /r "$INSTDIR"

SectionEnd

Function CheckFreeSpace

    var /GLOBAL installsize
    var /GLOBAL adjustedinstallsize
    var /GLOBAL freespace
    var /GLOBAL instdrive

    ; Verify that we have sufficient space for the install

    ; SectionGetSize returns the size of each section in kilobyte.
    SectionGetSize ${install_section_id} $installsize

    ; Adjust the required install size by 10mb, as a minimum amount
    ; of free space left after installation.
    IntOp $adjustedinstallsize $installsize + 10240

    ; Compute the drive that is the installation target; the
    ; ${DriveSpace} macro will not accept a path, it must be a drive.
    StrCpy $instdrive $INSTDIR 3

    ; Compute drive space free in kilobyte
    ${DriveSpace} $instdrive "/D=F /S=K" $freespace

    DetailPrint "Determined installer needs $adjustedinstallsize kb ($installsize kb) while $freespace kb is free"

    IntCmp $adjustedinstallsize $freespace spaceok spaceok

    MessageBox MB_OK|MB_ICONSTOP "Insufficient space for installation. Please free space for installation directory $INSTDIR and try again."
    DetailPrint "Insufficient space for installation. Installer needs $adjustedinstallsize kb, but freespace is only $freespace kb."
    Abort "Insufficient space for installation."

  spaceok:
    DetailPrint "Installation target space is sufficient"

FunctionEnd
Jared Oberhaus
If the assumptions in my question or answer are wrong, please correct them. At this time, this code seems to work with low free space conditions.
Jared Oberhaus
the section id is just a number starting from 0, so you could just make a loop to check all sections, call SectionGetFlags to see if the section is selected/checked, if it is, call SectionGetSize, if the error flag is set after calling SectionGetFlags, you have reached the end
Anders
As far as output to stdout goes, NSIS is a GUI app so there is not really any console handling. On XP and later its possible if you use the system plugin, see http://forums.winamp.com/showthread.php?postid=2303779#post2303779
Anders
+1  A: 

Your sample code is ok, but calling ${DriveSpace} on Win9x could fail. I also removed the need to specify the section id's

!define APPNAME "CalcEnoughSpace"
name "${APPNAME}"
outfile "$%temp%\${APPNAME}.exe"
ShowInstDetails show
RequestExecutionLevel user
installdir "$Temp"
AllowRootDirInstall true

!include Sections.nsh
!include LogicLib.nsh

Function .onInit
push $instdir
call VerifyFreeSpace
pop $0
${If} $0 < 1
    MessageBox mb_iconstop "Not enough free space!"
${EndIf}
FunctionEnd

page instfiles

Section !a
AddSize 10000
SectionEnd
Section /o b
AddSize 10000
SectionEnd

SectionGroup grp
Section c
AddSize 10000
SectionEnd
SectionGroupEnd



Function VerifyFreeSpace
System::Store s
pop $0 ;path to check
Push 0 ;default to no
System::Call 'kernel32::GetDiskFreeSpaceEx(tr0,*l.r1,*l,*l)i.r2'
${If} $2 < 1 
    StrCpy $0 $0 3
    System::Call 'kernel32::GetDiskFreeSpace(tr0,*i.r1,*i.r2,*i.r3,*i)i.r4'
    IntCmpU $4 0 ret 
    IntOp $1 $1 * $2
    System::Int64Op $1 * $3
    pop $1 
${EndIf}
System::Int64Op $1 / 1024 ;to kb
pop $1
StrCpy $4 0 ;size
StrCpy $2 0 ;section idx
loop:
    ClearErrors
    SectionGetFlags $2 $3
    IfErrors testspace
    IntOp $3 $3 & ${SF_SELECTED}
    ${If} $3 <> 0
     SectionGetSize $2 $3
     IntOp $4 $4 + $3
     ${EndIf}
    IntOp $2 $2 + 1
    goto loop
testspace:
pop $2 ;throw away default return value
System::Int64Op $1 > $4
ret:
System::Store l
FunctionEnd

I only did limited testing, hopefully there are no bugs :)

Anders
Thanks @Anders, I'll try this. I never write anything for Win95, but better to be prepared...
Jared Oberhaus
A: 

Do you have a sample script for silent install?