DotNET: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
(I re-wrote DotNet.nsh to actually check version numbers. The install is also silent now, so no need for the dll)
(false positive virus warning)
 
(16 intermediate revisions by 14 users not shown)
Line 8: Line 8:


=== Limitations ===
=== Limitations ===
* Only works for .NET Framework 2.0, not 1.0 or 1.1.
* Only works for .NET Framework 3.5, not 1.0 or 1.1 or 2.5 or 3.0 ...
* See talk page for 3.5 hack up!
* Won't work on XP 64 (as downloads the 32 bit installer by default)
* High risk of causing a false positive for "generic downloader trojan" in Kasperky antivirus software. No matter if you use inetc or NSISDL (verified in production with 1000+ downloads 2011-09-09).


== Usage ==
== Usage ==
Line 32: Line 35:
SectionIn RO
SectionIn RO


!insertmacro CheckDotNET
!insertmacro CheckDotNET ${DOTNET_VERSION}


#Files here
#Files here
Line 38: Line 41:
</highlight-nsis>
</highlight-nsis>


== Script ==
== The Script ==
<div style="overflow: auto; width=100%;">
<div style="overflow: auto; width=100%;">
<highlight-nsis>
<highlight-nsis>
# DotNET version checking macro.
# DotNET and MSI version checking macro.
# Written by AnarkiNet(AnarkiNet@gmail.com) originally, modified by eyal0
# Written by AnarkiNet(AnarkiNet@gmail.com) originally
# Downloads and runs the Microsoft .NET Framework version 2.0 Redistributable and runs it if the user does not have the correct version.
# modified by eyal0 in 2007 and sevenalive in 2010
;modifies the input to a version that can be compared like a string or num
# Installs the Microsoft .NET Framework version 3.5 SP1 if the required .NET runtime is not installed
!macro VerToNum VTN_output VTN_version
# To use, call the macro with a string:
   ;MessageBox MB_OK "***VTN_version is ${VTN_version}"
# Example: non real version numbers
#  !insertmacro CheckDotNET 3.5
#  !insertmacro CheckDotNET 3.5sp1
# (Version 2.0.9 is less than version 2.0.10.)
# Example: latest real version number at time of writing
# !insertmacro CheckDotNET "2.0.50727"
# All register variables are saved and restored by CheckDotNet
# No output
Var DN3Dir
Var dotnet3 ; is .NET 3 installed?
Var dotnet35 ; is .NET 3.5 installed?
Var dotnet35ver ; 3.5 version
Var dotnet35sp1 ; sp1
!macro CheckDotNET DotNetReqVer
   !define DOTNET_URL "http://download.microsoft.com/download/2/0/e/20e90413-712f-438c-988e-fdaa79a8ac3d/dotnetfx35.exe"
  DetailPrint "Checking your .NET Framework version..."
   Push $0
   Push $0
   Push $1
   Push $1
Line 52: Line 70:
   Push $3
   Push $3
   Push $4
   Push $4
    
   Push $5
   StrCpy $4 ${VTN_version}
   Push $6 ;backup of intsalled ver
   StrCpy $0 ""
  Push $7 ;backup of DoNetReqVer
   StrCpy $1 0
   ;MessageBox MB_OK "3**VTN_version is $4"
# NSIS
 
   ${DoUntil} $1 == 4
   ReadRegStr $dotnet3 HKEY_LOCAL_MACHINE "Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "InstallSuccess"
    ;MessageBox MB_OK "4**VTN_version is $4"
  ReadRegStr $dotnet35 HKEY_LOCAL_MACHINE "Software\Microsoft\NET Framework Setup\NDP\v3.5" "Install"
     ${StrTok} $2 $4 "." $1 "0"
  ReadRegStr $dotnet35ver HKEY_LOCAL_MACHINE "Software\Microsoft\NET Framework Setup\NDP\v3.5" "Version"
     ;MessageBox MB_OK "***VTN_version is now $4"
  ReadRegStr $dotnet35sp1 HKEY_LOCAL_MACHINE "Software\Microsoft\NET Framework Setup\NDP\v3.5" "SP"
    ;MessageBox MB_OK "***0 is $0"
   StrCpy $7 ${DotNetReqVer}
    ;MessageBox MB_OK "***1 is $1"
    StrLen $3 $2
   System::Call "mscoree::GetCORVersion(w .r0, i ${NSIS_MAX_STRLEN}, *i r2r2) i .r1 ?u"
    ${DoUntil} $3 >= 6 ;until the current part is 6 digits long
  DetailPrint ".NET Framework Version $0 found"
       ;MessageBox MB_OK "2 is $2"
   ${If} $0 == 0
       StrCpy $2 "0$2"
  DetailPrint ".NET Framework not found, download is required for program to run."
       StrLen $3 $2
     Goto NoDotNET
  ${EndIf}
  ${If} $0 == ""
  DetailPrint ".NET Framework not found, download is required for program to run."
    Goto NoDotNET
  ${EndIf}
  ;at this point, $0 has maybe v2.345.678.
  StrCpy $0 $0 $2 1 ;remove the starting "v", $0 has the installed version num as a string
  StrCpy $6 $0
  StrCpy $1 $7 ;$1 has the requested verison num as a string
  ;now let's compare the versions, installed against required <part0>.<part1>.<part2>.
  ${Do}
    StrCpy $2 "" ;clear out the installed part
     StrCpy $3 "" ;clear out the required part
    ${Do}
      ${If} $0 == "" ;if there are no more characters in the version
        StrCpy $4 "." ;fake the end of the version string
      ${Else}
        StrCpy $4 $0 1 0 ;$4 = character from the installed ver
        ${If} $4 != "."
          StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
        ${EndIf}
      ${EndIf}
      ${If} $1 == ""  ;if there are no more characters in the version
        StrCpy $5 "." ;fake the end of the version string
      ${Else}
        StrCpy $5 $1 1 0 ;$5 = character from the required ver
        ${If} $5 != "."
          StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
        ${EndIf}
      ${EndIf}
      ${If} $4 == "."
      ${AndIf} $5 == "."
        ${ExitDo} ;we're at the end of the part
      ${EndIf}
      ${If} $4 == "." ;if we're at the end of the current installed part
        StrCpy $2 "0$2" ;put a zero on the front
       ${Else} ;we have another character
        StrCpy $2 "$2$4" ;put the next character on the back
       ${EndIf}
      ${If} $5 == "." ;if we're at the end of the current required part
        StrCpy $3 "0$3" ;put a zero on the front
       ${Else} ;we have another character
        StrCpy $3 "$3$5" ;put the next character on the back
      ${EndIf}
     ${Loop}
     ${Loop}
     StrCpy $0 $0$2
     IntOp $1 $1 + 1
     ${If} $0 != "" ;let's remove the leading period on installed part if it exists
      StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1
    ${EndIf}
    ${If} $1 != "" ;let's remove the leading period on required part if it exists
      StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1
    ${EndIf}
    ;$2 has the installed part, $3 has the required part
     ${If} $2 S< $3
      IntOp $0 0 - 1 ;$0 = -1, installed less than required
      ${ExitDo}
    ${ElseIf} $2 S> $3
      IntOp $0 0 + 1 ;$0 = 1, installed greater than required
      ${ExitDo}
    ${ElseIf} $2 == ""
    ${AndIf} $3 == ""
      IntOp $0 0 + 0 ;$0 = 0, the versions are identical
      ${ExitDo}
    ${EndIf} ;otherwise we just keep looping through the parts
   ${Loop}
   ${Loop}
 
  ;check to see if v3 and/or v3.5 is installed
  ${If} $0 < 0
    ${If} $dotnet3 == "1"
          DetailPrint ".NET Framework Version 3 found"
          ${If} $dotnet35 == "1"
                DetailPrint ".NET Framework Version 3.5 found"
                            ${If} $dotnet35sp1 != "1"
                                  DetailPrint "SP1 needed. Installing..."
                                  Goto DownloadDotNET
                            ${EndIf}
                DetailPrint ".NET Framework Version 3.5 SP1 found"
                Goto NewDotNET
          ${EndIf}
    ${EndIf}
    DetailPrint ".NET Framework Version found: $6, but is older than the required version: $7"
    Goto OldDotNET
  ${Else}
    DetailPrint ".NET Framework Version found: $6, equal or newer to required version: $7."
    Goto NewDotNET
  ${EndIf}
NoDotNET:
    goto DownloadDotNET
OldDotNET:
    ReadRegStr $DN3Dir HKLM "SOFTWARE\Microsoft\.NETFramework" "InstallRoot"
    StrCpy $DN3Dir "$DN3Dir\v3.5\csc.exe"
    IfFileExists $DN3Dir NewDotNET
    goto DownloadDotNET
DownloadDotNET:
  DetailPrint "Beginning download of latest .NET Framework version."
  inetc::get /TIMEOUT=30000 ${DOTNET_URL} "$TEMP\dotnetfx35.exe" /END
  Pop $0
  DetailPrint "Result: $0"
  StrCmp $0 "OK" InstallDotNet
  StrCmp $0 "cancelled" GiveUpDotNET
  inetc::get /TIMEOUT=30000 /NOPROXY ${DOTNET_URL} "$TEMP\dotnetfx35.exe" /END
  Pop $0
  DetailPrint "Result: $0"
  StrCmp $0 "OK" InstallDotNet
  MessageBox MB_ICONSTOP "Download failed: $0"
  Abort
  InstallDotNet:
  DetailPrint "Completed download."
  Pop $0
  ${If} $0 == "cancel"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download cancelled.  Continue Installation?" \
    IDYES NewDotNET IDNO GiveUpDotNET
  ${EndIf}
;  TryFailedDownload:
  DetailPrint "Pausing installation while downloaded .NET Framework installer runs."
  ExecWait '$TEMP\dotnetfx35.exe /q /norestart /c:"install /q"'
  DetailPrint "Completed .NET Framework install/update. Removing .NET Framework installer."
  Delete "$TEMP\dotnetfx35.exe"
  DetailPrint ".NET Framework installer removed."
  goto NewDotNet
GiveUpDotNET:
  Abort "Installation cancelled by user."
NewDotNET:
  Pop $7
  Pop $6
  Pop $5
   Pop $4
   Pop $4
   Pop $3
   Pop $3
   Pop $2
   Pop $2
   Pop $1
   Pop $1
   Exch $0
   Pop $0
  Pop ${VTN_output}
!macroend
!macroend
</highlight-nsis></div>
[[Special:Contributions/97.117.134.8|97.117.134.8]] 22:20, 28 February 2010 (UTC)
== MSI Check Overview ==
Added check for MSI3.
MSI check code based on [http://www.codeproject.com/useritems/NSIS.asp]
=== What it does ===
# Checks the version of the MSI on the end-user's computer.
# Prompts them with a yes/no dialog if their version is lower than 3.
# If they press yes, the installer downloads the latest MSI3.1 installer and runs it.
# Checks the version of the .NET framework on the end-user's computer.
# Prompts them with a yes/no dialog if their version is different than the defined DOTNET_VERSION in the installer's main script.
# If they press yes, the installer downloads the latest .NET Framework 2.0 installer and runs it.


!macro CheckDotNET
== The Script With MSI Check ==
<div style="overflow: auto; width=100%;">
<highlight-nsis>
# DotNET and MSI version checking macro.
# Written by AnarkiNet(AnarkiNet@gmail.com) originally, modified by eyal0 (for use in http://www.sourceforge.net/projects/itwister)
# MSI check code based on http://www.codeproject.com/useritems/NSIS.asp
# Downloads the MSI version 3.1 and runs it if the user does not have the correct version.
# Downloads and runs the Microsoft .NET Framework version 2.0 Redistributable and runs it if the user does not have the correct version.
# To use, call the macro with a string:
# Example: non real version numbers
# !insertmacro CheckDotNET "2"
# !insertmacro CheckDotNET "2.0.9"
# (Version 2.0.9 is less than version 2.0.10.)
# Example: latest real version number at time of writing
# !insertmacro CheckDotNET "2.0.50727"
# All register variables are saved and restored by CheckDotNet
# No output
!macro CheckDotNET DotNetReqVer
   !define DOTNET_URL "http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=en&SrcCategoryId=&SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe"
   !define DOTNET_URL "http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=en&SrcCategoryId=&SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe"
  !define MSI31_URL "http://download.microsoft.com/download/1/4/7/147ded26-931c-4daf-9095-ec7baf996f46/WindowsInstaller-KB893803-v2-x86.exe"
 
   DetailPrint "Checking your .NET Framework version..."
   DetailPrint "Checking your .NET Framework version..."
  ;callee register save
   Push $0
   Push $0
   Push $1
   Push $1
  Push $2
  Push $3
  Push $4
  Push $5
  Push $6 ;backup of intsalled ver
  Push $7 ;backup of DoNetReqVer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                              MSI                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  GetDLLVersion "$SYSDIR\msi.dll" $R0 $R1
  IntOp $R2 $R0 / 0x00010000 ; $R2 now contains major version
  IntOp $R3 $R0 & 0x0000FFFF ; $R3 now contains minor version
  IntOp $R4 $R1 / 0x00010000 ; $R4 now contains release
  IntOp $R5 $R1 & 0x0000FFFF ; $R5 now contains build
  StrCpy $0 "$R2.$R3.$R4.$R5" ; $0 now contains string like "1.2.0.192"


   System::Call "mscoree::GetCORVersion(w .r0, i ${NSIS_MAX_STRLEN}, *i) i .r1 ?u"
   ${If} $R2 < '3'
   StrCmp $1 0 +2
    ;options
     StrCpy $0 "not found"
    SetOutPath "$TEMP"
 
    SetOverwrite on
   Pop $1
   
   Exch $0
    MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
    "Your MSI version: $0.$\nRequired Version: 3 or greater.$\nDownload MSI version from www.microsoft.com?" \
    /SD IDYES IDYES DownloadMSI IDNO NewMSI
    goto GiveUpDotNET ;IDCANCEL
   
   ${Else}
 
     DetailPrint "MSI3.1 already installed"
    goto NewMSI
   ${EndIf}
 
DownloadMSI:
  DetailPrint "Beginning download of MSI3.1."
   NSISDL::download ${MSI31_URL} "$TEMP\WindowsInstaller-KB893803-v2-x86.exe"
  DetailPrint "Completed download."
   Pop $0
   Pop $0
   ${If} $0 == "not found"
   ${If} $0 == "cancel"
  DetailPrint ".NET Framework not found, download is required for program to run!"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
     Goto InvalidDotNET
    "Download cancelled. Continue Installation?" \
    IDYES NewMSI IDNO GiveUpDotNET
  ${ElseIf} $0 != "success"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download failed:$\n$0$\n$\nContinue Installation?" \
     IDYES NewMSI IDNO GiveUpDotNET
   ${EndIf}
   ${EndIf}
  DetailPrint "Pausing installation while downloaded MSI3.1 installer runs."
  ExecWait '$TEMP\WindowsInstaller-KB893803-v2-x86.exe /quiet /norestart' $0
  DetailPrint "Completed MSI3.1 install/update. Exit code = '$0'. Removing MSI3.1 installer."
  Delete "$TEMP\WindowsInstaller-KB893803-v2-x86.exe"
  DetailPrint "MSI3.1 installer removed."
  goto NewMSI


NewMSI:
  DetailPrint "MSI3.1 installation done. Proceeding with remainder of installation."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  NetFX                                    ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  StrCpy $7 ${DotNetReqVer}
  System::Call "mscoree::GetCORVersion(w .r0, i ${NSIS_MAX_STRLEN}, *i r2r2) i .r1 ?u"
  ${If} $0 == 0
  DetailPrint ".NET Framework not found, download is required for program to run."
    Goto NoDotNET
  ${EndIf}
   ;at this point, $0 has maybe v2.345.678.
   ;at this point, $0 has maybe v2.345.678.
  Push $0
   StrCpy $0 $0 $2 1 ;remove the starting "v", $0 has the installed version num as a string
  Push $1
  StrCpy $6 $0
   StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1 ;remove the starting "v", $0 has the installed version num
   StrCpy $1 $7 ;$1 has the requested verison num as a string
   StrCpy $1 ${DOTNET_VERSION}      ;$1 has the requested verison num
   !insertmacro VerToNum $0 $0 ;convert $0 to a 16 digit num
   ;MessageBox MB_OKCANCEL "found $0" IDCANCEL GiveUpDotNET
  !insertmacro VerToNum $1 $1 ;convert $1 to a 16 digit num
   
 
  ;MessageBox MB_OKCANCEL "looking for $1" IDCANCEL GiveUpDotNET
  ${If} $0 S< $1
    Pop $1
  ;now let's compare the versions, installed against required <part0>.<part1>.<part2>.
    Pop $0
  ${Do}
    DetailPrint ".NET Framework Version found: $0, but does not match required version: ${DOTNET_VERSION}"
    StrCpy $2 "" ;clear out the installed part
    Goto InvalidDotNET
    StrCpy $3 "" ;clear out the required part
  ${EndIf}
  Pop $1
    ${Do}
  Pop $0
      ${If} $0 == "" ;if there are no more characters in the version
  DetailPrint ".NET Framework Version found: $0, matches required version: ${DOTNET_VERSION}."
        StrCpy $4 "." ;fake the end of the version string
  Goto ValidDotNET
      ${Else}
 
        StrCpy $4 $0 1 0 ;$4 = character from the installed ver
InvalidDotNET:
        ${If} $4 != "."
  ${If} $0 == "not found"
          StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
     MessageBox MB_YESNO|MB_ICONEXCLAMATION \
        ${EndIf}
     ".NET Framework not installed.$\nRequired Version: ${DOTNET_VERSION} or greater.$\nWould you like the installer to download the latest .NET Framework version from www.microsoft.com?" \
      ${EndIf}
     IDYES Download IDNO ValidDotNET 
     
      ${If} $1 == ""  ;if there are no more characters in the version
        StrCpy $5 "." ;fake the end of the version string
      ${Else}
        StrCpy $5 $1 1 0 ;$5 = character from the required ver
        ${If} $5 != "."
          StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
        ${EndIf}
      ${EndIf}
      ;MessageBox MB_OKCANCEL "installed $2,$4,$0 required $3,$5,$1" IDCANCEL GiveUpDotNET
      ${If} $4 == "."
      ${AndIf} $5 == "."
        ${ExitDo} ;we're at the end of the part
      ${EndIf}
      ${If} $4 == "." ;if we're at the end of the current installed part
        StrCpy $2 "0$2" ;put a zero on the front
      ${Else} ;we have another character
        StrCpy $2 "$2$4" ;put the next character on the back
      ${EndIf}
      ${If} $5 == "." ;if we're at the end of the current required part
        StrCpy $3 "0$3" ;put a zero on the front
      ${Else} ;we have another character
        StrCpy $3 "$3$5" ;put the next character on the back
      ${EndIf}
    ${Loop}
    ;MessageBox MB_OKCANCEL "finished parts: installed $2,$4,$0 required $3,$5,$1" IDCANCEL GiveUpDotNET
    ${If} $0 != "" ;let's remove the leading period on installed part if it exists
      StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1
    ${EndIf}
    ${If} $1 != "" ;let's remove the leading period on required part if it exists
      StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1
    ${EndIf}
     ;$2 has the installed part, $3 has the required part
     ${If} $2 S< $3
      IntOp $0 0 - 1 ;$0 = -1, installed less than required
      ${ExitDo}
    ${ElseIf} $2 S> $3
      IntOp $0 0 + 1 ;$0 = 1, installed greater than required
      ${ExitDo}
    ${ElseIf} $2 == ""
    ${AndIf} $3 == ""
      IntOp $0 0 + 0 ;$0 = 0, the versions are identical
      ${ExitDo}
    ${EndIf} ;otherwise we just keep looping through the parts
  ${Loop}
  ${If} $0 < 0
    DetailPrint ".NET Framework Version found: $6, but is older than the required version: $7"
     Goto OldDotNET
   ${Else}
   ${Else}
     MessageBox MB_YESNO|MB_ICONEXCLAMATION \
     DetailPrint ".NET Framework Version found: $6, equal or newer to required version: $7."
    "Your .NET Framework version: $0.$\nRequired Version: ${DOTNET_VERSION} or greater.$\nWould you like the installer to download the latest .NET Framework version from www.microsoft.com?" \
     Goto NewDotNET
     IDYES Download IDNO ValidDotNET
   ${EndIf}
   ${EndIf}
   
   
Download:
NoDotNET:
    MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
    ".NET Framework not installed.$\nRequired Version: $7 or greater.$\nDownload .NET Framework version from www.microsoft.com?" \
    /SD IDYES IDYES DownloadDotNET IDNO NewDotNET
    goto GiveUpDotNET ;IDCANCEL
OldDotNET:
    MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
    "Your .NET Framework version: $6.$\nRequired Version: $7 or greater.$\nDownload .NET Framework version from www.microsoft.com?" \
    /SD IDYES IDYES DownloadDotNET IDNO NewDotNET
    goto GiveUpDotNET ;IDCANCEL
DownloadDotNET:
   DetailPrint "Beginning download of latest .NET Framework version."
   DetailPrint "Beginning download of latest .NET Framework version."
   NSISDL::download ${DOTNET_URL} "$TEMP\dotnetfx.exe"
   NSISDL::download ${DOTNET_URL} "$TEMP\dotnetfx.exe"
   DetailPrint "Completed download."
   DetailPrint "Completed download."
   Pop $R0
   Pop $0
   StrCmp $R0 "success" +2
   ${If} $0 == "cancel"
     Abort "Download failed: $R0"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download cancelled.  Continue Installation?" \
    IDYES NewDotNET IDNO GiveUpDotNET
  ${ElseIf} $0 != "success"
     MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download failed:$\n$0$\n$\nContinue Installation?" \
    IDYES NewDotNET IDNO GiveUpDotNET
  ${EndIf}
   DetailPrint "Pausing installation while downloaded .NET Framework installer runs."
   DetailPrint "Pausing installation while downloaded .NET Framework installer runs."
   ExecWait 'dotnetfx.exe /q /c:"install /q"'
   ExecWait '$TEMP\dotnetfx.exe /q /c:"install /q"'
   DetailPrint "Completed .NET Framework install/update. Cleaning temporary files..."
   DetailPrint "Completed .NET Framework install/update. Removing .NET Framework installer."
   Delete "$TEMP\dotnetfx.exe"
   Delete "$TEMP\dotnetfx.exe"
   DetailPrint "Completed cleaning temporary files."
   DetailPrint ".NET Framework installer removed."
 
  goto NewDotNet
ValidDotNET:
   DetailPrint "Proceeding with remainder of installation..."
GiveUpDotNET:
  Abort "Installation cancelled by user."
NewDotNET:
   DetailPrint "Proceeding with remainder of installation."
  Pop $7
  Pop $6
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Pop $0
!macroend
!macroend
</highlight-nsis></div>
</highlight-nsis></div>


[[Category:Other Products Handling Functions]]
[[Category:Other Products Handling Functions]]
[[Category:Other Products Version Detection Functions]]
[[Category:Other Products Version Detection Functions]]
[[Category:Internet Functions]]
[[Category:Internet Functions]]

Latest revision as of 12:22, 9 September 2011

Overview

DotNET is a macro written to aid in the deployment of .NET Framework 2.0-based applications using NSIS.

What it does

  1. Checks the version of the .NET framework on the end-user's computer.
  2. Prompts them with a yes/no dialog if their version is different than the defined DOTNET_VERSION in the installer's main script.
  3. If they press yes, the installer downloads the latest .NET Framework 2.0 installer and runs it.

Limitations

  • Only works for .NET Framework 3.5, not 1.0 or 1.1 or 2.5 or 3.0 ...
  • See talk page for 3.5 hack up!
  • Won't work on XP 64 (as downloads the 32 bit installer by default)
  • High risk of causing a false positive for "generic downloader trojan" in Kasperky antivirus software. No matter if you use inetc or NSISDL (verified in production with 1000+ downloads 2011-09-09).

Usage

Including the Script

Save the Script contents as DotNET.nsh in the same folder as your main script, and put line in your main script like this:

!include "DotNET.nsh"

DotNet.nsh uses the ${if} function of LogicLib, so you also have to include it in your script:

!include LogicLib.nsh

Required Variables

Define the variable DOTNET_VERSION as the full or partial version of .NET Framework your app requires, like so:

!define DOTNET_VERSION "2.0.50727"

or

!define DOTNET_VERSION "2.0.5"

or

!define DOTNET_VERSION "2"

Using the Script

Insert the macro CheckDotNET into your script inside the first required section of the installer:

Section "Main Section (Required)"
	SectionIn RO
 
	!insertmacro CheckDotNET ${DOTNET_VERSION}
 
	#Files here
SectionEnd

The Script

# DotNET and MSI version checking macro.
# Written by AnarkiNet(AnarkiNet@gmail.com) originally
# modified by eyal0 in 2007 and sevenalive in 2010
# Installs the Microsoft .NET Framework version 3.5 SP1 if the required .NET runtime is not installed
# To use, call the macro with a string:
# Example: non real version numbers
#   !insertmacro CheckDotNET 3.5
#   !insertmacro CheckDotNET 3.5sp1
# (Version 2.0.9 is less than version 2.0.10.)
# Example: latest real version number at time of writing
# !insertmacro CheckDotNET "2.0.50727"
# All register variables are saved and restored by CheckDotNet
# No output
 Var DN3Dir
 Var dotnet3 ; is .NET 3 installed?
 Var dotnet35 ; is .NET 3.5 installed?
 Var dotnet35ver ; 3.5 version
 Var dotnet35sp1 ; sp1
!macro CheckDotNET DotNetReqVer
  !define DOTNET_URL "http://download.microsoft.com/download/2/0/e/20e90413-712f-438c-988e-fdaa79a8ac3d/dotnetfx35.exe"
  DetailPrint "Checking your .NET Framework version..."
  Push $0
  Push $1
  Push $2
  Push $3
  Push $4
  Push $5
  Push $6 ;backup of intsalled ver
  Push $7 ;backup of DoNetReqVer
 
# NSIS
 
  ReadRegStr $dotnet3 HKEY_LOCAL_MACHINE "Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "InstallSuccess"
  ReadRegStr $dotnet35 HKEY_LOCAL_MACHINE "Software\Microsoft\NET Framework Setup\NDP\v3.5" "Install"
  ReadRegStr $dotnet35ver HKEY_LOCAL_MACHINE "Software\Microsoft\NET Framework Setup\NDP\v3.5" "Version"
  ReadRegStr $dotnet35sp1 HKEY_LOCAL_MACHINE "Software\Microsoft\NET Framework Setup\NDP\v3.5" "SP"
  StrCpy $7 ${DotNetReqVer}
 
  System::Call "mscoree::GetCORVersion(w .r0, i ${NSIS_MAX_STRLEN}, *i r2r2) i .r1 ?u"
  DetailPrint ".NET Framework Version $0 found"
  ${If} $0 == 0
  	DetailPrint ".NET Framework not found, download is required for program to run."
    Goto NoDotNET
  ${EndIf}
  ${If} $0 == ""
  	DetailPrint ".NET Framework not found, download is required for program to run."
    Goto NoDotNET
  ${EndIf}
 
  ;at this point, $0 has maybe v2.345.678.
  StrCpy $0 $0 $2 1 ;remove the starting "v", $0 has the installed version num as a string
  StrCpy $6 $0
  StrCpy $1 $7 ;$1 has the requested verison num as a string
 
  ;now let's compare the versions, installed against required <part0>.<part1>.<part2>.
  ${Do}
    StrCpy $2 "" ;clear out the installed part
    StrCpy $3 "" ;clear out the required part
 
    ${Do}
      ${If} $0 == "" ;if there are no more characters in the version
        StrCpy $4 "." ;fake the end of the version string
      ${Else}
        StrCpy $4 $0 1 0 ;$4 = character from the installed ver
        ${If} $4 != "."
          StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
        ${EndIf}
      ${EndIf}
 
      ${If} $1 == ""  ;if there are no more characters in the version
        StrCpy $5 "." ;fake the end of the version string
      ${Else}
        StrCpy $5 $1 1 0 ;$5 = character from the required ver
        ${If} $5 != "."
          StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
        ${EndIf}
      ${EndIf}
      ${If} $4 == "."
      ${AndIf} $5 == "."
        ${ExitDo} ;we're at the end of the part
      ${EndIf}
 
      ${If} $4 == "." ;if we're at the end of the current installed part
        StrCpy $2 "0$2" ;put a zero on the front
      ${Else} ;we have another character
        StrCpy $2 "$2$4" ;put the next character on the back
      ${EndIf}
      ${If} $5 == "." ;if we're at the end of the current required part
        StrCpy $3 "0$3" ;put a zero on the front
      ${Else} ;we have another character
        StrCpy $3 "$3$5" ;put the next character on the back
      ${EndIf}
    ${Loop}
 
    ${If} $0 != "" ;let's remove the leading period on installed part if it exists
      StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1
    ${EndIf}
    ${If} $1 != "" ;let's remove the leading period on required part if it exists
      StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1
    ${EndIf}
 
    ;$2 has the installed part, $3 has the required part
    ${If} $2 S< $3
      IntOp $0 0 - 1 ;$0 = -1, installed less than required
      ${ExitDo}
    ${ElseIf} $2 S> $3
      IntOp $0 0 + 1 ;$0 = 1, installed greater than required
      ${ExitDo}
    ${ElseIf} $2 == ""
    ${AndIf} $3 == ""
      IntOp $0 0 + 0 ;$0 = 0, the versions are identical
      ${ExitDo}
    ${EndIf} ;otherwise we just keep looping through the parts
  ${Loop}
  ;check to see if v3 and/or v3.5 is installed
 
 
  ${If} $0 < 0
    ${If} $dotnet3 == "1"
          DetailPrint ".NET Framework Version 3 found"
          ${If} $dotnet35 == "1"
                DetailPrint ".NET Framework Version 3.5 found"
                             ${If} $dotnet35sp1 != "1"
                                  DetailPrint "SP1 needed. Installing..."
                                  Goto DownloadDotNET
                            ${EndIf}
                DetailPrint ".NET Framework Version 3.5 SP1 found"
                Goto NewDotNET
          ${EndIf}
    ${EndIf}
    DetailPrint ".NET Framework Version found: $6, but is older than the required version: $7"
    Goto OldDotNET
  ${Else}
    DetailPrint ".NET Framework Version found: $6, equal or newer to required version: $7."
    Goto NewDotNET
  ${EndIf}
 
NoDotNET:
    goto DownloadDotNET
OldDotNET:
    ReadRegStr $DN3Dir HKLM "SOFTWARE\Microsoft\.NETFramework" "InstallRoot"
    StrCpy $DN3Dir "$DN3Dir\v3.5\csc.exe"
 
    IfFileExists $DN3Dir NewDotNET
    goto DownloadDotNET
 
DownloadDotNET:
  DetailPrint "Beginning download of latest .NET Framework version."
  inetc::get /TIMEOUT=30000 ${DOTNET_URL} "$TEMP\dotnetfx35.exe" /END
  Pop $0
  DetailPrint "Result: $0"
  StrCmp $0 "OK" InstallDotNet
  StrCmp $0 "cancelled" GiveUpDotNET
  inetc::get /TIMEOUT=30000 /NOPROXY ${DOTNET_URL} "$TEMP\dotnetfx35.exe" /END
  Pop $0
  DetailPrint "Result: $0"
  StrCmp $0 "OK" InstallDotNet
 
  MessageBox MB_ICONSTOP "Download failed: $0"
  Abort
  InstallDotNet:
  DetailPrint "Completed download."
  Pop $0
  ${If} $0 == "cancel"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download cancelled.  Continue Installation?" \
    IDYES NewDotNET IDNO GiveUpDotNET
  ${EndIf}
;  TryFailedDownload:
  DetailPrint "Pausing installation while downloaded .NET Framework installer runs."
  ExecWait '$TEMP\dotnetfx35.exe /q /norestart /c:"install /q"'
  DetailPrint "Completed .NET Framework install/update. Removing .NET Framework installer."
  Delete "$TEMP\dotnetfx35.exe"
  DetailPrint ".NET Framework installer removed."
  goto NewDotNet
 
GiveUpDotNET:
  Abort "Installation cancelled by user."
 
NewDotNET:
  Pop $7
  Pop $6
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Pop $0
!macroend

97.117.134.8 22:20, 28 February 2010 (UTC)

MSI Check Overview

Added check for MSI3. MSI check code based on [1]

What it does

  1. Checks the version of the MSI on the end-user's computer.
  2. Prompts them with a yes/no dialog if their version is lower than 3.
  3. If they press yes, the installer downloads the latest MSI3.1 installer and runs it.
  4. Checks the version of the .NET framework on the end-user's computer.
  5. Prompts them with a yes/no dialog if their version is different than the defined DOTNET_VERSION in the installer's main script.
  6. If they press yes, the installer downloads the latest .NET Framework 2.0 installer and runs it.

The Script With MSI Check

# DotNET and MSI version checking macro.
# Written by AnarkiNet(AnarkiNet@gmail.com) originally, modified by eyal0 (for use in http://www.sourceforge.net/projects/itwister)
# MSI check code based on http://www.codeproject.com/useritems/NSIS.asp
# Downloads the MSI version 3.1 and runs it if the user does not have the correct version.
# Downloads and runs the Microsoft .NET Framework version 2.0 Redistributable and runs it if the user does not have the correct version.
# To use, call the macro with a string:
# Example: non real version numbers
# !insertmacro CheckDotNET "2"
# !insertmacro CheckDotNET "2.0.9"
# (Version 2.0.9 is less than version 2.0.10.)
# Example: latest real version number at time of writing
# !insertmacro CheckDotNET "2.0.50727"
# All register variables are saved and restored by CheckDotNet
# No output
 
!macro CheckDotNET DotNetReqVer
  !define DOTNET_URL "http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=en&SrcCategoryId=&SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe"
  !define MSI31_URL "http://download.microsoft.com/download/1/4/7/147ded26-931c-4daf-9095-ec7baf996f46/WindowsInstaller-KB893803-v2-x86.exe"
 
  DetailPrint "Checking your .NET Framework version..."
  ;callee register save
  Push $0
  Push $1
  Push $2
  Push $3
  Push $4
  Push $5
  Push $6 ;backup of intsalled ver
  Push $7 ;backup of DoNetReqVer
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;                               MSI                                          ;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  GetDLLVersion "$SYSDIR\msi.dll" $R0 $R1
  IntOp $R2 $R0 / 0x00010000 ; $R2 now contains major version
  IntOp $R3 $R0 & 0x0000FFFF ; $R3 now contains minor version
  IntOp $R4 $R1 / 0x00010000 ; $R4 now contains release
  IntOp $R5 $R1 & 0x0000FFFF ; $R5 now contains build
  StrCpy $0 "$R2.$R3.$R4.$R5" ; $0 now contains string like "1.2.0.192"
 
  ${If} $R2 < '3'
    ;options
    SetOutPath "$TEMP"
    SetOverwrite on
 
    MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
    "Your MSI version: $0.$\nRequired Version: 3 or greater.$\nDownload MSI version from www.microsoft.com?" \
    /SD IDYES IDYES DownloadMSI IDNO NewMSI
    goto GiveUpDotNET ;IDCANCEL
 
  ${Else}
 
    DetailPrint "MSI3.1 already installed"
    goto NewMSI
  ${EndIf}
 
DownloadMSI:
  DetailPrint "Beginning download of MSI3.1."
  NSISDL::download ${MSI31_URL} "$TEMP\WindowsInstaller-KB893803-v2-x86.exe"
  DetailPrint "Completed download."
  Pop $0
  ${If} $0 == "cancel"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download cancelled.  Continue Installation?" \
    IDYES NewMSI IDNO GiveUpDotNET
  ${ElseIf} $0 != "success"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download failed:$\n$0$\n$\nContinue Installation?" \
    IDYES NewMSI IDNO GiveUpDotNET
  ${EndIf}
  DetailPrint "Pausing installation while downloaded MSI3.1 installer runs."
  ExecWait '$TEMP\WindowsInstaller-KB893803-v2-x86.exe /quiet /norestart' $0
  DetailPrint "Completed MSI3.1 install/update. Exit code = '$0'. Removing MSI3.1 installer."
  Delete "$TEMP\WindowsInstaller-KB893803-v2-x86.exe"
  DetailPrint "MSI3.1 installer removed."
  goto NewMSI
 
NewMSI:
  DetailPrint "MSI3.1 installation done. Proceeding with remainder of installation."
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;                                  NetFX                                     ;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  StrCpy $7 ${DotNetReqVer}
 
  System::Call "mscoree::GetCORVersion(w .r0, i ${NSIS_MAX_STRLEN}, *i r2r2) i .r1 ?u"
 
  ${If} $0 == 0
  	DetailPrint ".NET Framework not found, download is required for program to run."
    Goto NoDotNET
  ${EndIf}
 
  ;at this point, $0 has maybe v2.345.678.
  StrCpy $0 $0 $2 1 ;remove the starting "v", $0 has the installed version num as a string
  StrCpy $6 $0
  StrCpy $1 $7 ;$1 has the requested verison num as a string
 
  ;MessageBox MB_OKCANCEL "found $0" IDCANCEL GiveUpDotNET
 
  ;MessageBox MB_OKCANCEL "looking for $1" IDCANCEL GiveUpDotNET
 
  ;now let's compare the versions, installed against required <part0>.<part1>.<part2>.
  ${Do}
    StrCpy $2 "" ;clear out the installed part
    StrCpy $3 "" ;clear out the required part
 
    ${Do}
      ${If} $0 == "" ;if there are no more characters in the version
        StrCpy $4 "." ;fake the end of the version string
      ${Else}
        StrCpy $4 $0 1 0 ;$4 = character from the installed ver
        ${If} $4 != "."
          StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
        ${EndIf}
      ${EndIf}
 
      ${If} $1 == ""  ;if there are no more characters in the version
        StrCpy $5 "." ;fake the end of the version string
      ${Else}
        StrCpy $5 $1 1 0 ;$5 = character from the required ver
        ${If} $5 != "."
          StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1 ;remove that first character from the remaining
        ${EndIf}
      ${EndIf}
      ;MessageBox MB_OKCANCEL "installed $2,$4,$0 required $3,$5,$1" IDCANCEL GiveUpDotNET
      ${If} $4 == "."
      ${AndIf} $5 == "."
        ${ExitDo} ;we're at the end of the part
      ${EndIf}
 
      ${If} $4 == "." ;if we're at the end of the current installed part
        StrCpy $2 "0$2" ;put a zero on the front
      ${Else} ;we have another character
        StrCpy $2 "$2$4" ;put the next character on the back
      ${EndIf}
      ${If} $5 == "." ;if we're at the end of the current required part
        StrCpy $3 "0$3" ;put a zero on the front
      ${Else} ;we have another character
        StrCpy $3 "$3$5" ;put the next character on the back
      ${EndIf}
    ${Loop}
    ;MessageBox MB_OKCANCEL "finished parts: installed $2,$4,$0 required $3,$5,$1" IDCANCEL GiveUpDotNET
 
    ${If} $0 != "" ;let's remove the leading period on installed part if it exists
      StrCpy $0 $0 ${NSIS_MAX_STRLEN} 1
    ${EndIf}
    ${If} $1 != "" ;let's remove the leading period on required part if it exists
      StrCpy $1 $1 ${NSIS_MAX_STRLEN} 1
    ${EndIf}
 
    ;$2 has the installed part, $3 has the required part
    ${If} $2 S< $3
      IntOp $0 0 - 1 ;$0 = -1, installed less than required
      ${ExitDo}
    ${ElseIf} $2 S> $3
      IntOp $0 0 + 1 ;$0 = 1, installed greater than required
      ${ExitDo}
    ${ElseIf} $2 == ""
    ${AndIf} $3 == ""
      IntOp $0 0 + 0 ;$0 = 0, the versions are identical
      ${ExitDo}
    ${EndIf} ;otherwise we just keep looping through the parts
  ${Loop}
 
  ${If} $0 < 0
    DetailPrint ".NET Framework Version found: $6, but is older than the required version: $7"
    Goto OldDotNET
  ${Else}
    DetailPrint ".NET Framework Version found: $6, equal or newer to required version: $7."
    Goto NewDotNET
  ${EndIf}
 
NoDotNET:
    MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
    ".NET Framework not installed.$\nRequired Version: $7 or greater.$\nDownload .NET Framework version from www.microsoft.com?" \
    /SD IDYES IDYES DownloadDotNET IDNO NewDotNET
    goto GiveUpDotNET ;IDCANCEL
OldDotNET:
    MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
    "Your .NET Framework version: $6.$\nRequired Version: $7 or greater.$\nDownload .NET Framework version from www.microsoft.com?" \
    /SD IDYES IDYES DownloadDotNET IDNO NewDotNET
    goto GiveUpDotNET ;IDCANCEL
 
DownloadDotNET:
  DetailPrint "Beginning download of latest .NET Framework version."
  NSISDL::download ${DOTNET_URL} "$TEMP\dotnetfx.exe"
  DetailPrint "Completed download."
  Pop $0
  ${If} $0 == "cancel"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download cancelled.  Continue Installation?" \
    IDYES NewDotNET IDNO GiveUpDotNET
  ${ElseIf} $0 != "success"
    MessageBox MB_YESNO|MB_ICONEXCLAMATION \
    "Download failed:$\n$0$\n$\nContinue Installation?" \
    IDYES NewDotNET IDNO GiveUpDotNET
  ${EndIf}
  DetailPrint "Pausing installation while downloaded .NET Framework installer runs."
  ExecWait '$TEMP\dotnetfx.exe /q /c:"install /q"'
  DetailPrint "Completed .NET Framework install/update. Removing .NET Framework installer."
  Delete "$TEMP\dotnetfx.exe"
  DetailPrint ".NET Framework installer removed."
  goto NewDotNet
 
GiveUpDotNET:
  Abort "Installation cancelled by user."
 
NewDotNET:
  DetailPrint "Proceeding with remainder of installation."
  Pop $7
  Pop $6
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Pop $0
!macroend