UpgradeDLL Func function

From NSIS Wiki
Jump to navigationJump to search
Author: opher (talk, contrib)


Description

In my setup package I had to install 64 system files, which meant 64 !insertmacro UpgradeDLL ... lines :'( So I wrote this function. It is capable of accepting a source directory (e.g. "$PLUGINSDIR\SysFiles") and a destination directory (e.g. $SYSDIR) and installing/upgrading all files in source with *one* function call. :)

Requires Get extention of file

The Function

!ifndef UPGRADEDLL_FUNC_INCLUDED
!define UPGRADEDLL_FUNC_INCLUDED
 
; Function - Upgrade DLL File
; Written by Opher Shachar on 2004/01/14
; Revised on 2004/01/15: Supports registering *.TLB, *.OLB files with 'regtlib.exe'
;     Needs 'GetFileExt' (http://nsis.sourceforge.net/wiki/Get_extention_of_file)
; Revised 2007/02/06: fix directory as source, skip '.' and '..' entries
;     $R6 now is VOLATILE
; Based on macro by Joost Verburg
; --------------------------------------
;
; Parameters:
; $R4 = DESTFILE    - Location of the DLL file that should be upgraded (on user's system)
;    OR DESTDIR     - Target directory for upgraded DLLs
; $R5 = TEMPBASEDIR - Directory on the user's system to store a temporary file when
;               the system has to be rebooted.
;               For Win9x support, this should be on the same volume as the DESTFILE!
;               The Windows temp directory could be located on any volume, so you cannot
;               use this directory.
; $R6 = REGISTER    - if you want to upgrade a DLL that has to be registered.
;       0 = Don't register, anything else = Register  -- note: VOLATILE
; $R7 = LOCALFILE   - Location of the new DLL file (on the user's system)
;    OR LOCALDIR    - Source directory of newer DLLs
; Note: If you want to support Win9x, you can only use short filenames (8.3).
;
; Example of usage for single file:
; $R4 = "$SYSDIR\dllname.dll"
; $R5 = "$SYSDIR"
; $R6 = "1" (= Register DLL)
; $R7 = "Source_dir\dllname.dll"
; Call UpgradeDLL_Func
;
; Example of usage with File command:
; SetOutPath "$PLUGINSDIR\SysFiles"
; File /r "sys_files_to_be_installed\*.*" ...
; ...
; File "some_other_sys_file.dll"
; $R4 = "$SYSDIR"               -- note no '\', MUST be an existing directory
; $R5 = "$SYSDIR"
; $R6 = "1" (= Register DLL)
; $R7 = "$PLUGINSDIR\SysFiles"  -- note no '\'
; Call UpgradeDLL_Func
; RMDir /r "$PLUGINSDIR\SysFiles"
;
; Example of usage for whole bunch of external files:
; $R4 = "$SYSDIR"       -- note no '\', MUST be an existing directory
; $R5 = "$SYSDIR"
; $R6 = "1" (= Register DLL)
; $R7 = "Source_dir"    -- note no '\'
; Call UpgradeDLL_Func
 
Function UpgradeDLL_Func
 
  Push $R0
  Push $R1
  Push $R2
  Push $R3
 
  SetOverwrite try
 
  ;------------------------
  ;Check single file
 
  IfFileExists "$R7\*.*" +3
    Call :upgradedll.doit
    Goto upgradedll.end
 
  ;------------------------
  ;Enumerate directory
 
  Push $0
  Push $1
  Push $2
  StrCpy $1 $R7
  StrCpy $2 $R4
  FindFirst $0 $R0 "$1\*.*"
 
  loop:
  IfErrors upgradedll.nomore
    StrCpy $R7 "$1\$R0"
    StrCpy $R4 "$2\$R0"
    StrCmp $R0 "." +3
    StrCmp $R0 ".." +2
      Call :upgradedll.doit
    FindNext $0 $R0
    Goto loop
 
  upgradedll.nomore:
  FindClose $0
  StrCpy $R7 $1
  StrCpy $R4 $2
  Pop $2
  Pop $1
  Pop $0
  Goto upgradedll.end
 
  ;------------------------
  ;Do we want to register file?
 
  upgradedll.doit:
  IntCmp $R6 0 upgradedll.moveon
 
  ;------------------------
  ;Is file a .TLB? == Use 'regtlib.exe' instead of 'RegDLL'
 
  Push $R4
  Call GetFileExt
  Pop $R0
  StrCmp $R0 "TLB" +2
  StrCmp $R0 "OLB"  0 +3
    StrCpy $R6 -1
    Goto upgradedll.moveon
    StrCpy $R6 1
 
  ;------------------------
  ;File already installed? No. Just copy
 
  upgradedll.moveon:
  IfFileExists $R4 0 upgradedll.copy
 
  ;------------------------
  ;Check file and version
 
  ClearErrors
    GetDLLVersion $R7 $R0 $R1
    GetDLLVersion $R4 $R2 $R3
  IfErrors upgradedll.upgrade
 
  IntCmpU $R0 $R2 0 upgradedll.done upgradedll.upgrade
  IntCmpU $R1 $R3 upgradedll.noreboot upgradedll.done upgradedll.upgrade
 
  ;------------------------
  ;Let's upgrade the DLL!
 
  upgradedll.upgrade:
  IntCmp $R6 1 0 +2 +2
    UnRegDLL $R4   ;Unregister the DLL
 
  ;------------------------
  ;Try to copy the DLL directly
 
  upgradedll.copy:
  ClearErrors
    StrCpy $R0 $R4
    Call :upgradedll.file
  IfErrors 0 upgradedll.noreboot
 
  ;------------------------
  ;DLL is in use. Copy it to a temp file and Rename it on reboot.
 
  GetTempFileName $R0 $R5
    Call :upgradedll.file
  Rename /REBOOTOK $R0 $R4
 
  ;------------------------
  ;Register the DLL on reboot
 
  IntCmp $R6 0 upgradedll.done
  IntCmp $R6 1 +3
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\RunOnce" \
      "Register $R4" '"$SYSDIR\regtlib.exe" "$R4"'
    Goto upgradedll.done
 
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\RunOnce" \
      "Register $R4" '"$SYSDIR\rundll32.exe" "$R4",DllRegisterServer'
    Goto upgradedll.done
 
  ;------------------------
  ;Register the DLL
 
  upgradedll.noreboot:
  IntCmp $R6 0 upgradedll.done
  IntCmp $R6 1 +3
    ExecWait '"$SYSDIR\regtlib.exe" "$R4"'
    Goto upgradedll.done
 
    RegDLL $R4
 
  ;------------------------
  ;Done
 
  upgradedll.done:
  Return
 
  ;------------------------
  ;Called to copy the DLL
 
  upgradedll.file:
    CopyFiles /silent $R7 $R0
    Return
 
  ;------------------------
  ;End
 
  upgradedll.end:
  Pop $R3
  Pop $R2
  Pop $R1
  Pop $R0
 
  ;------------------------
  ;Restore settings
 
  SetOverwrite lastused
 
FunctionEnd
 
!endif