How to Detect any .NET Framework: Difference between revisions
From NSIS Wiki
Jump to navigationJump to search
(2 intermediate revisions by 2 users not shown) | |||
Line 12: | Line 12: | ||
== The Abort If Bad Function == | == The Abort If Bad Function == | ||
<highlight-nsis> | <highlight-nsis> | ||
;;USAGE: | ;; USAGE: | ||
!define MIN_FRA_MAJOR "3" | !define MIN_FRA_MAJOR "3" | ||
!define MIN_FRA_MINOR "0" | !define MIN_FRA_MINOR "0" | ||
!define MIN_FRA_BUILD "*" | !define MIN_FRA_BUILD "*" | ||
; | ; | ||
;;NB Use an asterisk to match anything. | ;; NB Use an asterisk to match anything. | ||
;Call AbortIfBadFramework | ; Call AbortIfBadFramework | ||
;; No pops. It just aborts inside the function, or returns if all is well. | ;; No pops. It just aborts inside the function, or returns if all is well. | ||
;; Change this if you like. | ;; Change this if you like. | ||
Function AbortIfBadFramework | Function AbortIfBadFramework | ||
;Save the variables in case something else is using them | ; Save the variables in case something else is using them | ||
Push $0 | Push $0 | ||
Push $1 | Push $1 | ||
Line 38: | Line 38: | ||
Push $R8 | Push $R8 | ||
; Major | |||
StrCpy $R5 "0" | StrCpy $R5 "0" | ||
; Minor | |||
StrCpy $R6 "0" | StrCpy $R6 "0" | ||
; Build | |||
StrCpy $R7 "0" | StrCpy $R7 "0" | ||
; No Framework | |||
StrCpy $R8 "0.0.0" | StrCpy $R8 "0.0.0" | ||
StrCpy $0 0 | StrCpy $0 0 | ||
loop: | loop: | ||
;Get each sub key under "SOFTWARE\Microsoft\NET Framework Setup\NDP" | ; Get each sub key under "SOFTWARE\Microsoft\NET Framework Setup\NDP" | ||
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP" $0 | EnumRegKey $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP" $0 | ||
StrCmp $1 "" done ;jump to end if no more registry keys | StrCmp $1 "" done ;jump to end if no more registry keys | ||
Line 53: | Line 61: | ||
StrCpy $3 $1 "" 1 ;Remainder of string | StrCpy $3 $1 "" 1 ;Remainder of string | ||
;Loop if first character is not a 'v' | ; Loop if first character is not a 'v' | ||
StrCmpS $2 "v" start_parse loop | StrCmpS $2 "v" start_parse loop | ||
;Parse the string | ; Parse the string | ||
start_parse: | start_parse: | ||
StrCpy $R1 "" | StrCpy $R1 "" | ||
Line 66: | Line 74: | ||
parse: | parse: | ||
StrCmp $3 "" parse_done ;If string is empty, we are finished | StrCmp $3 "" parse_done ; If string is empty, we are finished | ||
StrCpy $2 $3 1 ;Cut off the first character | StrCpy $2 $3 1 ; Cut off the first character | ||
StrCpy $3 $3 "" 1 ;Remainder of string | StrCpy $3 $3 "" 1 ; Remainder of string | ||
StrCmp $2 "." is_dot not_dot ;Move to next part if it's a dot | StrCmp $2 "." is_dot not_dot ; Move to next part if it's a dot | ||
is_dot: | is_dot: | ||
IntOp $4 $4 + 1 ; Move to the next section | IntOp $4 $4 + 1 ; Move to the next section | ||
goto parse ;Carry on parsing | goto parse ; Carry on parsing | ||
not_dot: | not_dot: | ||
Line 83: | Line 91: | ||
major_ver: | major_ver: | ||
StrCpy $R1 $R1$2 | StrCpy $R1 $R1$2 | ||
goto parse ;Carry on parsing | goto parse ; Carry on parsing | ||
minor_ver: | minor_ver: | ||
StrCpy $R2 $R2$2 | StrCpy $R2 $R2$2 | ||
goto parse ;Carry on parsing | goto parse ; Carry on parsing | ||
build_ver: | build_ver: | ||
StrCpy $R3 $R3$2 | StrCpy $R3 $R3$2 | ||
goto parse ;Carry on parsing | goto parse ; Carry on parsing | ||
parse_done: | parse_done: | ||
Line 121: | Line 129: | ||
done: | done: | ||
;Have we got the framework we need? | ; Have we got the framework we need? | ||
IntCmp $R5 ${MIN_FRA_MAJOR} max_major_same fail end | IntCmp $R5 ${MIN_FRA_MAJOR} max_major_same fail end | ||
max_major_same: | max_major_same: | ||
; Versions > 4.5 have different logic. | |||
IntCmp ${MIN_FRA_MAJOR} 4 0 before45 end | |||
IntCmp ${MIN_FRA_MINOR} 5 max_major_greaterthan45 before45 max_major_greaterthan45 | |||
before45: | |||
IntCmp $R6 ${MIN_FRA_MINOR} max_minor_same fail end | IntCmp $R6 ${MIN_FRA_MINOR} max_minor_same fail end | ||
max_minor_same: | max_minor_same: | ||
IntCmp $R7 ${MIN_FRA_BUILD} end fail end | IntCmp $R7 ${MIN_FRA_BUILD} end fail end | ||
max_major_greaterthan45: | |||
!if ${MIN_FRA_MINOR} == "5" | |||
!if ${MIN_FRA_BUILD} == "0" | |||
!define MIN_FRA_RELEASE "378389" | |||
!else if ${MIN_FRA_BUILD} == "1" | |||
!define MIN_FRA_RELEASE "378675" | |||
!else if ${MIN_FRA_BUILD} == "2" | |||
!define MIN_FRA_RELEASE "379893" | |||
!else | |||
!error "AbortIfBadFramework was passed a bad framework version ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD}." | |||
!endif | |||
!else if ${MIN_FRA_MINOR} == "6" | |||
!if ${MIN_FRA_BUILD} == "0" | |||
!define MIN_FRA_RELEASE "393295" | |||
!else if ${MIN_FRA_BUILD} == "1" | |||
!define MIN_FRA_RELEASE "394254" | |||
!else if ${MIN_FRA_BUILD} == "2" | |||
!define MIN_FRA_RELEASE "394802" | |||
!else | |||
!error "AbortIfBadFramework was passed a bad framework version ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD}." | |||
!endif | |||
!else if ${MIN_FRA_MINOR} == "7" | |||
!if ${MIN_FRA_BUILD} == "0" | |||
!define MIN_FRA_RELEASE "460798" | |||
!else if ${MIN_FRA_BUILD} == "1" | |||
!define MIN_FRA_RELEASE "461308" | |||
!else if ${MIN_FRA_BUILD} == "2" | |||
!define MIN_FRA_RELEASE "461808" | |||
!else | |||
!error "AbortIfBadFramework was passed a bad framework version ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD}." | |||
!endif | |||
!else if ${MIN_FRA_MINOR} == "8" | |||
!if ${MIN_FRA_BUILD} == "0" | |||
!define MIN_FRA_RELEASE "528040" | |||
!else | |||
!error "AbortIfBadFramework was passed a bad framework version ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD}." | |||
!endif | |||
!else | |||
!error "AbortIfBadFramework is not sure how to check .NET framework versions > V4.8." | |||
!endif | |||
ReadRegDWORD $R9 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" Release | |||
IntCmp $R9 ${MIN_FRA_RELEASE} end wrong_framework end | |||
fail: | fail: | ||
Line 150: | Line 216: | ||
end: | end: | ||
;Pop the variables we pushed earlier | ; Pop the variables we pushed earlier | ||
Pop $R8 | Pop $R8 | ||
Pop $R7 | Pop $R7 |
Latest revision as of 09:18, 14 April 2022
Author: circumpunct (talk, contrib) |
Why?
There are plenty of functions here to detect the .NET Framework, but most (all?) predate Framework 3 and make assumptions that will break them if not met. Specifically that all frameworks will have a build number that can be found under Policy, and that Major (or even Minor) build numbers will not exceed one digit. This may not be true in future.
This function should get around that problem. Frameworks higher than the ones specified are accepted.
I think it's pretty robust, but e-mail me if you find any bugs. Or even better, just fix them. This is a wiki.
Below is another function that gives you the latest version of the installed .NET version. It is based on first script, but uses the VersionCompare macro.
The Abort If Bad Function
;; USAGE: !define MIN_FRA_MAJOR "3" !define MIN_FRA_MINOR "0" !define MIN_FRA_BUILD "*" ; ;; NB Use an asterisk to match anything. ; Call AbortIfBadFramework ;; No pops. It just aborts inside the function, or returns if all is well. ;; Change this if you like. Function AbortIfBadFramework ; Save the variables in case something else is using them Push $0 Push $1 Push $2 Push $3 Push $4 Push $R1 Push $R2 Push $R3 Push $R4 Push $R5 Push $R6 Push $R7 Push $R8 ; Major StrCpy $R5 "0" ; Minor StrCpy $R6 "0" ; Build StrCpy $R7 "0" ; No Framework StrCpy $R8 "0.0.0" StrCpy $0 0 loop: ; Get each sub key under "SOFTWARE\Microsoft\NET Framework Setup\NDP" EnumRegKey $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP" $0 StrCmp $1 "" done ;jump to end if no more registry keys IntOp $0 $0 + 1 StrCpy $2 $1 1 ;Cut off the first character StrCpy $3 $1 "" 1 ;Remainder of string ; Loop if first character is not a 'v' StrCmpS $2 "v" start_parse loop ; Parse the string start_parse: StrCpy $R1 "" StrCpy $R2 "" StrCpy $R3 "" StrCpy $R4 $3 StrCpy $4 1 parse: StrCmp $3 "" parse_done ; If string is empty, we are finished StrCpy $2 $3 1 ; Cut off the first character StrCpy $3 $3 "" 1 ; Remainder of string StrCmp $2 "." is_dot not_dot ; Move to next part if it's a dot is_dot: IntOp $4 $4 + 1 ; Move to the next section goto parse ; Carry on parsing not_dot: IntCmp $4 1 major_ver IntCmp $4 2 minor_ver IntCmp $4 3 build_ver IntCmp $4 4 parse_done major_ver: StrCpy $R1 $R1$2 goto parse ; Carry on parsing minor_ver: StrCpy $R2 $R2$2 goto parse ; Carry on parsing build_ver: StrCpy $R3 $R3$2 goto parse ; Carry on parsing parse_done: IntCmp $R1 $R5 this_major_same loop this_major_more this_major_more: StrCpy $R5 $R1 StrCpy $R6 $R2 StrCpy $R7 $R3 StrCpy $R8 $R4 goto loop this_major_same: IntCmp $R2 $R6 this_minor_same loop this_minor_more this_minor_more: StrCpy $R6 $R2 StrCpy $R7 $R3 StrCpy $R8 $R4 goto loop this_minor_same: IntCmp R3 $R7 loop loop this_build_more this_build_more: StrCpy $R7 $R3 StrCpy $R8 $R4 goto loop done: ; Have we got the framework we need? IntCmp $R5 ${MIN_FRA_MAJOR} max_major_same fail end max_major_same: ; Versions > 4.5 have different logic. IntCmp ${MIN_FRA_MAJOR} 4 0 before45 end IntCmp ${MIN_FRA_MINOR} 5 max_major_greaterthan45 before45 max_major_greaterthan45 before45: IntCmp $R6 ${MIN_FRA_MINOR} max_minor_same fail end max_minor_same: IntCmp $R7 ${MIN_FRA_BUILD} end fail end max_major_greaterthan45: !if ${MIN_FRA_MINOR} == "5" !if ${MIN_FRA_BUILD} == "0" !define MIN_FRA_RELEASE "378389" !else if ${MIN_FRA_BUILD} == "1" !define MIN_FRA_RELEASE "378675" !else if ${MIN_FRA_BUILD} == "2" !define MIN_FRA_RELEASE "379893" !else !error "AbortIfBadFramework was passed a bad framework version ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD}." !endif !else if ${MIN_FRA_MINOR} == "6" !if ${MIN_FRA_BUILD} == "0" !define MIN_FRA_RELEASE "393295" !else if ${MIN_FRA_BUILD} == "1" !define MIN_FRA_RELEASE "394254" !else if ${MIN_FRA_BUILD} == "2" !define MIN_FRA_RELEASE "394802" !else !error "AbortIfBadFramework was passed a bad framework version ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD}." !endif !else if ${MIN_FRA_MINOR} == "7" !if ${MIN_FRA_BUILD} == "0" !define MIN_FRA_RELEASE "460798" !else if ${MIN_FRA_BUILD} == "1" !define MIN_FRA_RELEASE "461308" !else if ${MIN_FRA_BUILD} == "2" !define MIN_FRA_RELEASE "461808" !else !error "AbortIfBadFramework was passed a bad framework version ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD}." !endif !else if ${MIN_FRA_MINOR} == "8" !if ${MIN_FRA_BUILD} == "0" !define MIN_FRA_RELEASE "528040" !else !error "AbortIfBadFramework was passed a bad framework version ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD}." !endif !else !error "AbortIfBadFramework is not sure how to check .NET framework versions > V4.8." !endif ReadRegDWORD $R9 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" Release IntCmp $R9 ${MIN_FRA_RELEASE} end wrong_framework end fail: StrCmp $R8 "0.0.0" no_framework goto wrong_framework no_framework: MessageBox MB_OK|MB_ICONSTOP "Installation failed.$\n$\n\ This software requires Windows Framework version \ ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD} or higher.$\n$\n\ No version of Windows Framework is installed.$\n$\n\ Please update your computer at http://windowsupdate.microsoft.com/." abort wrong_framework: MessageBox MB_OK|MB_ICONSTOP "Installation failed!$\n$\n\ This software requires Windows Framework version \ ${MIN_FRA_MAJOR}.${MIN_FRA_MINOR}.${MIN_FRA_BUILD} or higher.$\n$\n\ The highest version on this computer is $R8.$\n$\n\ Please update your computer at http://windowsupdate.microsoft.com/." abort end: ; Pop the variables we pushed earlier Pop $R8 Pop $R7 Pop $R6 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Pop $R1 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd
The Get-Latest-DotNET Function (warning: register backup/restore code is backwards and generally messed up)
!include "WordFunc.nsh" !insertmacro VersionCompare Function GetLatestDotNETVersion ;Save the variables in case something else is using them Push $0 ; Registry key enumerator index Push $1 ; Registry value Push $2 ; Temp var Push $R0 ; Max version number Push $R1 ; Looping version number StrCpy $R0 "0.0.0" StrCpy $0 0 loop: ; Get each sub key under "SOFTWARE\Microsoft\NET Framework Setup\NDP" EnumRegKey $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP" $0 StrCmp $1 "" done ; jump to end if no more registry keys IntOp $0 $0 + 1 ; Increase registry key index StrCpy $R1 $1 "" 1 ; Looping version number, cut of leading 'v' ${VersionCompare} $R1 $R0 $2 ; $2=0 Versions are equal, ignore ; $2=1 Looping version $R1 is newer ; $2=2 Looping version $R1 is older, ignore IntCmp $2 1 newer_version loop loop newer_version: StrCpy $R0 $R1 goto loop done: ; If the latest version is 0.0.0, there is no .NET installed ?! ${VersionCompare} $R0 "0.0.0" $2 IntCmp $2 0 no_dotnet clean clean no_dotnet: StrCpy $R0 "" clean: ; Pop the variables we pushed earlier Pop $0 Pop $1 Pop $2 Pop $R1 ; $R0 contains the latest .NET version or empty string if no .NET is available FunctionEnd