How to ensure a required version of .NET Framework is installed

From NSIS Wiki
Jump to navigationJump to search
Author: John J. Pike (talk, contrib)


Description

This function allows you to select a minimum version of the .NET Framework be installed. If it or a higher version is not present the function will abort the installation.

The Function

; Usage
; Define in your script two constants:
;   DOT_MAJOR "(Major framework version)"
;   DOT_MINOR "{Minor framework version)"
; 
; Call IsDotNetInstalled
; This function will abort the installation if the required version 
; or higher version of the .NET Framework is not installed.  Place it in
; either your .onInit function or your first install section before 
; other code.
Function IsDotNetInstalled
 
  StrCpy $0 "0"
  StrCpy $1 "SOFTWARE\Microsoft\.NETFramework" ;registry entry to look in.
  StrCpy $2 0
 
  StartEnum:
    ;Enumerate the versions installed.
    EnumRegKey $3 HKLM "$1\policy" $2
 
    ;If we don't find any versions installed, it's not here.
    StrCmp $3 "" noDotNet notEmpty
 
    ;We found something.
    notEmpty:
      ;Find out if the RegKey starts with 'v'.  
      ;If it doesn't, goto the next key.
      StrCpy $4 $3 1 0
      StrCmp $4 "v" +1 goNext
      StrCpy $4 $3 1 1
 
      ;It starts with 'v'.  Now check to see how the installed major version
      ;relates to our required major version.
      ;If it's equal check the minor version, if it's greater, 
      ;we found a good RegKey.
      IntCmp $4 ${DOT_MAJOR} +1 goNext yesDotNetReg
      ;Check the minor version.  If it's equal or greater to our requested 
      ;version then we're good.
      StrCpy $4 $3 1 3
      IntCmp $4 ${DOT_MINOR} yesDotNetReg goNext yesDotNetReg
 
    goNext:
      ;Go to the next RegKey.
      IntOp $2 $2 + 1
      goto StartEnum
 
  yesDotNetReg:
    ;Now that we've found a good RegKey, let's make sure it's actually
    ;installed by getting the install path and checking to see if the 
    ;mscorlib.dll exists.
    EnumRegValue $2 HKLM "$1\policy\$3" 0
    ;$2 should equal whatever comes after the major and minor versions 
    ;(ie, v1.1.4322)
    StrCmp $2 "" noDotNet
    ReadRegStr $4 HKLM $1 "InstallRoot"
    ;Hopefully the install root isn't empty.
    StrCmp $4 "" noDotNet
    ;build the actuall directory path to mscorlib.dll.
    StrCpy $4 "$4$3.$2\mscorlib.dll"
    IfFileExists $4 yesDotNet noDotNet
 
  noDotNet:
    ;Nope, something went wrong along the way.  Looks like the 
    ;proper .NET Framework isn't installed.  
    MessageBox MB_OK "You must have v${DOT_MAJOR}.${DOT_MINOR} or greater of the .NET Framework installed.  Aborting!"
    Abort
 
  yesDotNet:
    ;Everything checks out.  Go on with the rest of the installation.
 
FunctionEnd

I hope this is helpful.

Description

Hi John,

thanks for writing this function, I've used it in my installer. However I did a slight modification to it, because I needed to recognize minor version as well (e.g. 2.0.50727), because our project does not work with .NET 2.0 beta which differs only in the last number. Here is the code:

The Function

; Usage
; Define in your script two constants:
;   DOT_MAJOR "(Major framework version)"
;   DOT_MINOR "{Minor framework version)"
;   DOT_MINOR_MINOR "{Minor framework version - last number after the second dot)"
; 
; Call IsDotNetInstalledAdv
; This function will abort the installation if the required version 
; or higher version of the .NET Framework is not installed.  Place it in
; either your .onInit function or your first install section before 
; other code.
Function IsDotNetInstalledAdv
   Push $0
   Push $1
   Push $2
   Push $3
   Push $4
   Push $5
 
  StrCpy $0 "0"
  StrCpy $1 "SOFTWARE\Microsoft\.NETFramework" ;registry entry to look in.
  StrCpy $2 0
 
  StartEnum:
    ;Enumerate the versions installed.
    EnumRegKey $3 HKLM "$1\policy" $2
 
    ;If we don't find any versions installed, it's not here.
    StrCmp $3 "" noDotNet notEmpty
 
    ;We found something.
    notEmpty:
      ;Find out if the RegKey starts with 'v'.  
      ;If it doesn't, goto the next key.
      StrCpy $4 $3 1 0
      StrCmp $4 "v" +1 goNext
      StrCpy $4 $3 1 1
 
      ;It starts with 'v'.  Now check to see how the installed major version
      ;relates to our required major version.
      ;If it's equal check the minor version, if it's greater, 
      ;we found a good RegKey.
      IntCmp $4 ${DOT_MAJOR} +1 goNext yesDotNetReg
      ;Check the minor version.  If it's equal or greater to our requested 
      ;version then we're good.
      StrCpy $4 $3 1 3
      IntCmp $4 ${DOT_MINOR} +1 goNext yesDotNetReg
 
      ;detect sub-version - e.g. 2.0.50727
      ;takes a value of the registry subkey - it contains the small version number
      EnumRegValue $5 HKLM "$1\policy\$3" 0
 
      IntCmpU $5 ${DOT_MINOR_MINOR} yesDotNetReg goNext yesDotNetReg
 
    goNext:
      ;Go to the next RegKey.
      IntOp $2 $2 + 1
      goto StartEnum
 
  yesDotNetReg: 
    ;Now that we've found a good RegKey, let's make sure it's actually
    ;installed by getting the install path and checking to see if the 
    ;mscorlib.dll exists.
    EnumRegValue $2 HKLM "$1\policy\$3" 0
    ;$2 should equal whatever comes after the major and minor versions 
    ;(ie, v1.1.4322)
    StrCmp $2 "" noDotNet
    ReadRegStr $4 HKLM $1 "InstallRoot"
    ;Hopefully the install root isn't empty.
    StrCmp $4 "" noDotNet
    ;build the actuall directory path to mscorlib.dll.
    StrCpy $4 "$4$3.$2\mscorlib.dll"
    IfFileExists $4 yesDotNet noDotNet
 
  noDotNet:
    ;Nope, something went wrong along the way.  Looks like the 
    ;proper .NET Framework isn't installed.  
 
    ;Uncomment the following line to make this function throw a message box right away 
   ; MessageBox MB_OK "You must have v${DOT_MAJOR}.${DOT_MINOR}.${DOT_MINOR_MINOR} or greater of the .NET Framework installed.  Aborting!"
   ; Abort
     StrCpy $0 0
     Goto done
 
     yesDotNet:
    ;Everything checks out.  Go on with the rest of the installation.
    StrCpy $0 1
 
   done:
     Pop $4
     Pop $3
     Pop $2
     Pop $1
     Exch $0
 FunctionEnd

Macro Conversion

Hello lads, I was very pleased with this function. However, copying constantly "IsDotNetInstalled" or "IsDotNetInstalledAdv" (or even saving them to a .nsh file) may be a bother to some people. As well, I did this just to make your script more organized, sense-full, and well, I was bored.

Instead, create a new file into your "${NSISDIR}\Includes" folder, and name it "DotNetSearch.nsh"

Open it for editing, and copy all the code below then save it. (simple ZIP download may be available soon, however I do not often do this as I like users to see the code they are copying)

!macro DotNetSearch DOTNETVMAJOR DOTNETVMINOR DOTNETVMINORMINOR DOTNETLASTFUNCTION DOTNETPATH
	Var /GLOBAL DOTNET1
	Var /GLOBAL DOTNET2
	Var /GLOBAL DOTNET3
	Var /GLOBAL DOTNET4
	Var /GLOBAL DOTNET5
	Var /GLOBAL DOTNET6
		Push $DOTNET1
		Push $DOTNET2
		Push $DOTNET3
		Push $DOTNET4
		Push $DOTNET5
		Push $DOTNET6
 
			StrCpy $DOTNET1 "0"
			StrCpy $DOTNET2 "SOFTWARE\Microsoft\.NETFramework"
			StrCpy $DOTNET3 0
 
	DotNetStartEnum:
		EnumRegKey $DOTNET4 HKLM "$DOTNET2\policy" $DOTNET3
			StrCmp $DOTNET4 "" noDotNet dotNetFound
 
	dotNetFound:
		StrCpy $DOTNET5 $DOTNET4 1 0
		StrCmp $DOTNET5 "v" +1 goNextDotNet
		StrCpy $DOTNET5 $DOTNET4 1 1
 
	IntCmp $DOTNET5 ${DOTNETVMAJOR} +1 goNextDotNet yesDotNetReg
    StrCpy $DOTNET5 $DOTNET4 1 3
    IntCmp $DOTNET5 ${DOTNETVMINOR} +1 goNextDotNet yesDotNetReg
 
		StrCmp ${DOTNETVMINORMINOR} "" yesDotNetReg +1 yesDotNetReg
		;StrCmp ${DOTNETVMINORMINOR} "" yesDotNetReg +1
 		;Changed this line (otherwise it would not work with my setup!) - Vinz0r
 
	IntCmpU $DOTNET5 ${DOTNETVMINORMINOR} yesDotNetReg goNextDotNet yesDotNetReg
 
		goNextDotNet:
			IntOp $DOTNET3 $DOTNET3 + 1
			Goto DotNetStartEnum
 
	yesDotNetReg: 
		EnumRegValue $DOTNET3 HKLM "$DOTNET2\policy\$DOTNET4" 0
		StrCmp $DOTNET3 "" noDotNet
		ReadRegStr $DOTNET5 HKLM $DOTNET2 "InstallRoot"
		StrCmp $DOTNET5 "" noDotNet
		StrCpy $DOTNET5 "$DOTNET5$DOTNET4.$DOTNET3\mscorlib.dll"
		IfFileExists $DOTNET5 yesDotNet noDotNet
 
	noDotNet:
		StrCmp ${DOTNETLASTFUNCTION} "INSTALL_ABORT" +1 nDN2
			MessageBox MB_YESNO|MB_ICONQUESTION \
			"You must have Microsoft .NET Framework version ${DOTNETVMAJOR}.${DOTNETVMINOR}.${DOTNETVMINORMINOR}$\nor higher installed. Install now?" \
			IDYES +2 IDNO +1
			Abort
			ExecWait '${DOTNETPATH}'
			Goto DotNetStartEnum
	nDN2:
		StrCmp ${DOTNETLASTFUNCTION} "INSTALL_NOABORT" +1 nDN3
			MessageBox MB_YESNO|MB_ICONQUESTION \
			"Microsoft .NET Framework version ${DOTNETVMAJOR}.${DOTNETVMINOR}.${DOTNETVMINORMINOR} is not installed.$\nDo so now?" \
			IDYES +1 IDNO +3
			ExecWait '${DOTNETPATH}'
			Goto DotNetStartEnum
			StrCpy $DOTNET1 0
			Goto DotNetFinish
	nDN3:
		StrCmp ${DOTNETLASTFUNCTION} "WARNING" +1 nDN4
			MessageBox MB_OK|MB_ICONEXCLAMATION \
			"Warning:$\n$\n$\t$\tMicrosoft .NET Framework version$\n$\t$\t${DOTNETVMAJOR}.${DOTNETVMINOR}.${DOTNETVMINORMINOR} is not installed!" \
			IDOK 0
			StrCpy $DOTNET1 0
			Goto DotNetFinish
	nDN4:
		StrCmp ${DOTNETLASTFUNCTION} "ABORT" +1 nDN5
			MessageBox MB_OK|MB_ICONEXCLAMATION \
			"Error:$\n$\n$\t$\tMicrosoft .NET Framework version$\n$\t$\t${DOTNETVMAJOR}.${DOTNETVMINOR}.${DOTNETVMINORMINOR} is not installed, aborting!" \
			IDOK 0
			Abort
	nDN5:
		StrCmp ${DOTNETLASTFUNCTION} "IGNORE" +1 nDN6
			StrCpy $DOTNET1 0
			Goto DotNetFinish
	nDN6:
		MessageBox MB_OK \
		"$(^Name) Setup internal error.$\nMacro 'DotNetSearch', parameter '4'(${DOTNETLASTFUNCTION})invalid.$\nValue must be INSTALL_ABORT|INSTALL_NOABORT|WARNING|ABORT|IGNORE$\nSorry for the inconvenience.$\n$\tAborting..." \
		IDOK 0
		Abort
 
	yesDotNet:
		StrCpy $DOTNET1 1
 
	DotNetFinish:
		Pop $DOTNET6
		Pop $DOTNET5
		Pop $DOTNET4
		Pop $DOTNET3
		Pop $DOTNET2
		!define ${DOTNETOUTCOME} $DOTNET1
		Exch $DOTNET1
!macroend

This macro is just a slightly edited (for easier use as a macro, user choices, and avoiding possible conflicts). It was actually edited off of "DotNetSearchAdv" and was made able to have the following capabilities pointed out below.

Using the Macro

When using the DotNetSearch macro, there are 5 simple parameters that will be explained. Parameter 1 is the Major version of the required .NET Framework. Parameter 2 is the Minor version of the required .NET Framework. Parameter 3 is the Secondary Minor version of the required .NET Framework. Keep in mind, the DotNetSearch macro must be used within a function/section. (In Example:)

Function .onInit
	!insertmacro DotNetSearch 2 0 50727 "" ""
FunctionEnd
;or
Function .onInit
	!insertmacro DotNetSearch 2 0 "" "" ""
FunctionEnd

Parameter 4 defines what happens if the required version of .NET Framework is not found. There are 5 different options:

  1. INSTALL_ABORT
    1. Shows a message box asking whether or not to install .NET Framework (pressing no aborts setup)
  2. INSTALL_NOABORT
    1. Shows a message box asking whether or not to install .NET Framework (pressing no continues setup without installing .NET Framework)
  3. WARNING
    1. Shows a message box warning the user that they do not have the required .NET Framework version, then after clicking OK, simply continues the setup.
  4. ABORT
    1. Shows a message box informing the user that they do not have the required .NET Framework version, then after clicking OK, aborts the setup.
  5. IGNORE
    1. Ignore continues the setup even if the required version of .NET Framework version is not installed, without warning, aborting, or installing.

Parameter 5 defines the location that the executable file to install .NET Framework is. This is so that if the file is external, it can be called upon. If it's not, then you can try other tricks to install it. Perhaps defining parameter 4 as IGNORE, then telling your setup to extract the setup file from your setup file to a temporary folder (IE: $TEMP) then installing, or finding a way to install from online. However, this macro does not support online installing, and I do not plan on modifying it too. But, I am very open to anyone modifying the code, in-fact, the result of whether or not .NET Framework is installed is saved to ;;${DOTNETOUTCOME}, so if you wish to include code after the macro depending on whether it is installed (${DOTNETOUTCOME} will equal 0) or whether it is (${DOTNETOUTCOME} will equal 1), you can. Using this you could probably easily insert your own message box or download .NET Framework online.

Yet again, that is you burden.

Description

There are some problems in the detecting .NET version in a such way. First of all, not all the frameworks write their versions under the "Policy" branch. For example, I have Framework 3.5 on my Windows XP, but in "Policy" branch there are only v1.1 and v2.0. Maybe, the better approach is to cycle through "Software\Microsoft\NET Framework Setup\NDP" branch? Also, the way to ensure that's FW is installed by checking the existing of the mscorlib.dll in the directory with FW version in its path is rather tricky and risky. Again in my case with Framework 3.5, I have no mscorlib.dll in C:\Windows\Microsoft.NET\Framework\v3.5, nor in C:\Windows\Microsoft.NET\Framework\v3.0, only in C:\Windows\Microsoft.NET\Framework\v1.1 and C:\Windows\Microsoft.NET\Framework\v2.0. So this check I'm failing too. Maybe my issues will be helpful to someone...

See also How to Detect any .NET Framework, which appears to use the method suggested here.