开发者

NSIS silent install ( started via code )

I'm trying to build an NSIS installer that will do the following:

1 When run normaly it will install the application with a installer with the usual choices.

2 When the installer is run with /S switch it silent installs, it's ok if it shows UI. But it should be automatic.

It seems to me that the installer "works", as it runs and then correctly starts the application again. But it has not updated anything, it's almost as if it runs, but does not copy any files.

When it's a silent install, it's started by the following code ( Application updates itself)

    ProcessStartInfo Pro = new ProcessStartInfo();
    Pro.Verb = "runas";
    Pro.UseShellExecute = true;
    Pro.FileName = gDownloadedFileName;
    Pro.Arguments = "/S";
    Pro.WindowStyle = ProcessWindowStyle.Normal;
    Pro.CreateNoWindow = true;

NSIS script main ( I can post support scripts for NSIS that are custom if ppl desire )

; example2.nsi
;
; This script is based on example1.nsi, but it remember the directory, 
; has uninstall support and (optionally) installs start menu shortcuts.
;
; It will install example2.nsi into a directory that the user selects,
!include MUI.nsh
!include fileassoc.nsh
!include Silent.nsh


!define _AppName "My application"
!define _AppExe "My application.exe"
!define _AppVersion "1.0.0.0"

;--------------------------------------------------------------------- Dont edit beloow

; The name of the installer
Name "${_AppName}"

; The file to write
OutFile "DFOInfo_Setup_beta.exe"

; The default installation directory
InstallDir "$PROGRAMFILES\${_AppName}"

; Registry key to check for directory (so if you install again, it will 
; overwrite the old one automatically)
InstallDirRegKey HKLM "Software\TheXSoft.com\${_AppName}" "Install_Dir"

RequestExecutionLevel admin

Function .onInit
  Call GetDotNet

  Call IsSilent
  Pop $0
  StrCmp $0 1 0 +3
    Call SilentInstall

FunctionEnd

Function GetDotNet
  IfFileExists "$WINDIR\Microsoft.NET\Framework\v4.0.30319\installUtil.exe" NextStep
  MessageBox MB_OK|MB_ICONEXCLAMATION "You must have the Microsoft .NET Framework 4.0 Installed to use this application. $\n$\n The installer will now open the Microsft .NET Framework 4.0 webpage$\n$\n$\n$\nRemember this program will not function until you have installed the .NET Framework 4 ( You will get a error message if you try to start it)"
  ExecShell Open "http://www.microsoft.com/downloads/en/details.aspx?FamilyID=9cfb2d51-5ff4-4491-b0e5-b386f32c0992&displaylang=en" SW_SHOWNORMAL
  Quit
NextStep:
FunctionEnd

Section
SectionEnd
;--------------------------------

; Pages shown on none silent installer

;!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES

# These indented statements modify settings for MUI_PAGE_FINISH

;If we want to display a run app function
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_FINISHPAGE_RUN_TEXT "Run ${_AppName}"
!define MUI_FINISHPAGE_RUN_CHECKED
!define MUI_FINISHPAGE_RUN "$INSTDIR\${_AppExe}"
!insertmacro MUI_PAGE_FINISH

!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "German"
!insertmacro MUI_LANGUAGE "French"


UninstPage uninstConfirm
UninstPage instfiles

;--------------------------------

; The stuff to install
Section "${_AppName} (required)"
  SectionIn RO

  ; Set output path to the installation directory.
  SetOutPath $INSTDIR

  ; Put file there
  File /R "Current\*.*"

  ; Write the installation path into the registry
  WriteRegStr HKLM "Software\TheXSoft.com\${_AppName}" "Install_Dir" "$INSTDIR"

  ; Write the uninstall keys for Windows
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}" "DisplayName" "${_AppName} ( Remove only)"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}" "UninstallString" '"$INSTDIR\uninstall.exe"'
  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}" "NoModify" 1
  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}" "NoRepair" 1
  WriteUninstaller "uninstall.exe"

SectionEnd

; Optional section (can be disabled by the user)
Section "Start Menu Shortcuts"

  SetShellVarContext all

  CreateDirectory "$SMPROGRAMS\${_AppName}"
  CreateShortCut "$SMPROGRAMS\${_AppName}\TheXSoft.com - Software page.url" "$INSTDIR\TheXSoft.com - Software page.url"
  CreateShortCut "$SMPROGRAMS\${_AppName}\GuildStats.NET - Get the stats for your MMO.url" "$INSTDIR\GuildStats.NET - Get the stats for your MMO.url"
  CreateShortCut "$SMPROGRAMS\${_AppName}\${_AppName}.lnk" "$INSTDIR\${_AppExe}" "" "$INSTDIR\${_AppExe}" 0

SectionEnd


;--------------------------------

; Uninstaller

Section "Uninstall"

  ; Remove registry keys
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}"
  DeleteRegKey HKLM "SOFTWARE\${_AppName}"

  ; Remove files and uninstaller
  Delete "$INSTDIR\*.exe"
  Delete $INSTDIR\uninstall.exe

  ; Remove shortcuts, if any
  Delete "$SMPROGRAMS\${_AppName}\*.*"

  ; Remove directories used
  RMDir "$INSTDIR"

SectionEnd


;--------------------------------
; Silent install logic

Function SilentInstall
  ; Set output path to the installation directory.
  SetOutPath $INSTDIR

  ; Put file there
  File /R "Current\*.*"

  ; Write the installation path into the registry
  WriteRegStr HKLM "Software\TheXSoft.com\${_AppName}" "Install_Dir" "$INSTDIR"

  ; Write the uninstall keys for Windows
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}" "DisplayName" "${_AppName} ( Remove only)"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}" "UninstallString" '"$INSTDIR\uninstall.exe"'
  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}" "NoModify" 1
  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${_AppName}" "NoRepair" 1
  WriteUninstaller "uninstall.exe"


  SetShellVarContext all

  CreateDirectory "$SMPROGRAMS\${_AppName}"
  CreateShortCut "$SMPROGRAMS\${_AppName}\TheXSoft.com - Software page.url" "$INSTDIR\TheXSoft.com - Software page.url"
  CreateShortCut "$SMP开发者_StackOverflow中文版ROGRAMS\${_AppName}\GuildStats.NET - Get the stats for your MMO.url" "$INSTDIR\GuildStats.NET - Get the stats for your MMO.url"
  CreateShortCut "$SMPROGRAMS\${_AppName}\${_AppName}.lnk" "$INSTDIR\${_AppExe}" "" "$INSTDIR\${_AppExe}" 0

  Exec ${_AppExe}
  Quit
FunctionEnd


StrCmp $0 1 0 +3 is wrong, it should be +2 (+3 in this example is probably undefined behavior since you are skipping the hidden return instruction).

Using relative jumps is error prone, you should use a label or rewrite it using the logic lib:

!include LogicLib.nsh
...
Function .onInit
  Call GetDotNet

  ${If} ${Silent}
    call SilentInstall
  ${EndIf}
FunctionEnd

But putting the silent install logic in a separate function doubles your work when the normal install logic is almost equal. You should be able to remove the SilentInstall function and just use a hidden section to execute during silent installs:

Section "${_AppName} (required)"
#shared install code
SectionEnd

Section "Start Menu Shortcuts"
#shared startmenu code
SectionEnd

Section
${If} ${Silent}
    Exec ${_AppExe}
${EndIf}
Section

It is hard to say why your files are not updated, but if you use shared code you can run it without /S and check the detail log. My only guess is that InstallDirRegKey is picking up a non default installdir and you are looking at the wrong files. You can Process Monitor to monitor the install.


Your code has some other unrelated issues:

  • RequestExecutionLevel admin is not enough, you need to deal with NT5 and NT6 with UAC off (UserInfo::GetAccountType)
  • You don't have to specify the shortcut icon when the icon is the same as the target program
  • You should quote the path when calling Exec: Exec '"$instdir\${_AppExe}"'
  • Mixing UAC/runas/RequestExecutionLevel admin with Exec is problematic since you can end up running the program as the wrong user.


This is a tricky one. I am using the same approach to update an application of mine, calling an NSIS installer silently. In my case, the problem did not lie in the NSIS script, it was that I was using the WebClient class to download a zip file containing my installer to the temp directory, and subsequently extracting it there, and running it from there. The problem is that the ZipFile class generates an exception if the extracted setup file exists, and hence the old installer persists, and nothing except the uninstall executable is updated in the program directory.

For me, the solution was to clean up the old zip and exe files before downloading the new ones (in VB.NET):

    If File.Exists(Path.Combine(Path.GetTempPath(), "Setup.zip")) Then
        File.Delete(Path.Combine(Path.GetTempPath(), "Setup.zip"))
    End If
    If File.Exists(Path.Combine(Path.GetTempPath(), "Setup.exe")) Then
        File.Delete(Path.Combine(Path.GetTempPath(), "Setup.exe"))
    End If
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜