Skip to content

Instantly share code, notes, and snippets.

@daemondevin
Last active November 8, 2025 05:35
Show Gist options
  • Select an option

  • Save daemondevin/44c16c28fce4ebcddf4d853200c86109 to your computer and use it in GitHub Desktop.

Select an option

Save daemondevin/44c16c28fce4ebcddf4d853200c86109 to your computer and use it in GitHub Desktop.
NSIS Include that manages VC++ (2005-2022) and .NET (Framework/Core) redistributables. Supports x86, x64, and ARM64 architectures.
; RuntimeManager.nsh - Complete Runtime Detection, Download, and Installation
; Manages VC++ (2005-2022) and .NET (Framework/Core) redistributables
; Supports x86, x64, and ARM64 architectures
!ifndef RUNTIME_MANAGER_NSH
!define RUNTIME_MANAGER_NSH
!ifndef LOGICLIB
!include LogicLib.nsh
!endif
!ifndef ___X64__NSH___
!include x64.nsh
!endif
; Required plugins:
; - nsExec (for silent installation)
; - inetc (for HTTPS downloads)
; Required macros:
; - ${VersionCompare} from WordFunc.nsh or custom implementation
!ifndef DebugMsg
!macro DebugMsg MSG
DetailPrint "${MSG}"
!macroend
!define DebugMsg "!insertmacro DebugMsg"
!endif
; WordFunc.nsh macros if not already included
!ifndef VersionCompare
!include WordFunc.nsh
!insertmacro VersionCompare
!endif
; Custom version check helper
!macro VersionCheck VER1 VER2 RESULT
Push "${VER1}"
Push "${VER2}"
Call __VersionCheck
Pop ${RESULT}
!macroend
!define VersionCheck "!insertmacro VersionCheck"
Function __VersionCheck
Exch $1 ; VER2
Exch
Exch $0 ; VER1
Push $2
Push $3
Push $4
Push $5
; Simple version comparison: returns -1 if VER1 < VER2, 0 if equal, 1 if VER1 > VER2
${VersionCompare} $0 $1 $2
; Convert VersionCompare output (0=equal, 1=newer, 2=older) to standard (-1, 0, 1)
${If} $2 == 0
StrCpy $3 0
${ElseIf} $2 == 1
StrCpy $3 1
${Else}
StrCpy $3 -1
${EndIf}
Pop $5
Pop $4
Pop $2
Pop $1
Pop $0
Push $3
FunctionEnd
; WordReplace helper if not available
!ifndef WordReplace
!macro WordReplace INPUT WORD1 WORD2 OPTION OUTPUT
Push "${INPUT}"
Push "${WORD1}"
Push "${WORD2}"
Push "${OPTION}"
Call __WordReplace
Pop ${OUTPUT}
!macroend
!define WordReplace "!insertmacro WordReplace"
Function __WordReplace
Exch $3 ; OPTION
Exch
Exch $2 ; WORD2
Exch
Exch 2
Exch $1 ; WORD1
Exch 2
Exch 3
Exch $0 ; INPUT
Push $4
Push $5
StrCpy $4 $0
${StrRep} $4 $1 $2
StrCpy $5 $4
Pop $4
Pop $0
Pop $1
Pop $2
Pop $3
Push $5
FunctionEnd
; StrRep - String Replace
!macro StrRep OUTPUT INPUT OLD NEW
Push "${INPUT}"
Push "${OLD}"
Push "${NEW}"
Call __StrRep
Pop ${OUTPUT}
!macroend
!define StrRep "!insertmacro StrRep"
Function __StrRep
Exch $2 ; NEW
Exch
Exch $1 ; OLD
Exch
Exch 2
Exch $0 ; INPUT
Push $3
Push $4
Push $5
Push $6
Push $7
StrCpy $3 ""
StrCpy $4 0
StrLen $5 $1
loop:
StrCpy $6 $0 $5 $4
StrCmp $6 "" done
StrCmp $6 $1 replace
StrCpy $7 $0 1 $4
StrCpy $3 "$3$7"
IntOp $4 $4 + 1
Goto loop
replace:
StrCpy $3 "$3$2"
IntOp $4 $4 + $5
Goto loop
done:
StrCpy $6 $0 "" $4
StrCpy $3 "$3$6"
Pop $7
Pop $6
Pop $5
Pop $4
Pop $2
Pop $1
Pop $0
Push $3
FunctionEnd
!endif
;============================================================
; DETECTION FUNCTIONS
;============================================================
;------------------------------------------------------------
; VCRuntime_IsInstalled
; Check if a specific VC++ runtime version is installed
;
; Stack Usage:
; Push "2022" ; version
; Push "x64" ; arch (x86, x64, or arm64)
; Call VCRuntime_IsInstalled
; Pop $R1 ; Installed version string (empty if not found)
; Pop $R0 ; Result: "true" or "false"
;------------------------------------------------------------
Function VCRuntime_IsInstalled
Exch $1 ; _ARCH
Exch
Exch $0 ; _VERSION
Push $2
Push $3
Push $R8 ; Registry GUID
Push $R9 ; Expected minimum version
StrCpy $R0 "false"
StrCpy $R1 ""
; Determine registry GUID and minimum version
${Switch} "$0"
${Case} "2005"
${If} "$1" == "x86"
StrCpy $R8 "{7299052b-02a4-4627-81f2-1818da5d550d}"
StrCpy $R9 "8.0.56336"
${ElseIf} "$1" == "x64"
StrCpy $R8 "{071c9b48-7c32-4621-a0ac-3f809523288f}"
StrCpy $R9 "8.0.56336"
${Else}
Goto VCRuntimeCheckEnd
${EndIf}
${Break}
${Case} "2008"
${If} "$1" == "x86"
StrCpy $R8 "{9A25302D-30C0-39D9-BD6F-21E6EC160475}"
StrCpy $R9 "9.0.30729"
${ElseIf} "$1" == "x64"
StrCpy $R8 "{8220EEFE-38CD-377E-8595-13398D740ACE}"
StrCpy $R9 "9.0.30729"
${Else}
Goto VCRuntimeCheckEnd
${EndIf}
${Break}
${Case} "2010"
${If} "$1" == "x86"
StrCpy $R8 "{196BB40D-1578-3D01-B289-BEFC77A11A1E}"
StrCpy $R9 "10.0.40219"
${ElseIf} "$1" == "x64"
StrCpy $R8 "{DA5E371C-6333-3D8A-93A4-6FD5B20BCC6E}"
StrCpy $R9 "10.0.40219"
${Else}
Goto VCRuntimeCheckEnd
${EndIf}
${Break}
${Case} "2012"
${If} "$1" == "x86"
StrCpy $R8 "{33d1fd90-4274-48a1-9bc1-97e33d9c2d6f}"
StrCpy $R9 "11.0.61030"
${ElseIf} "$1" == "x64"
StrCpy $R8 "{ca67548a-5ebe-413a-b50c-4b9ceb6d66c6}"
StrCpy $R9 "11.0.61030"
${Else}
Goto VCRuntimeCheckEnd
${EndIf}
${Break}
${Case} "2013"
${If} "$1" == "x86"
StrCpy $R8 "{f65db027-aff3-4070-886a-0d87064aabb1}"
StrCpy $R9 "12.0.40664"
${ElseIf} "$1" == "x64"
StrCpy $R8 "{050d4fc8-5d48-4b8f-8972-47c82c46020f}"
StrCpy $R9 "12.0.40664"
${Else}
Goto VCRuntimeCheckEnd
${EndIf}
${Break}
${Case} "2015"
${Case} "2017"
${Case} "2019"
${Case} "2022"
; VC++ 2015-2022 share the same v14.x runtime
${If} "$1" == "x86"
StrCpy $R8 "{e2803110-78b3-4664-a479-3611a381656a}"
StrCpy $R9 "14.0.24215"
${ElseIf} "$1" == "x64"
StrCpy $R8 "{d992c12e-cab2-426f-bde3-fb8c53950b0d}"
StrCpy $R9 "14.0.24215"
${ElseIf} "$1" == "arm64"
StrCpy $R8 "{5e791fcf-45a5-40b1-9b78-fb1cf5680841}"
StrCpy $R9 "14.0.24215"
${Else}
Goto VCRuntimeCheckEnd
${EndIf}
${Break}
${Default}
${DebugMsg} "Unknown VC++ runtime version: $0"
Goto VCRuntimeCheckEnd
${Break}
${EndSwitch}
; Check registry for installation
${If} "$1" == "x86"
${If} ${RunningX64}
SetRegView 32
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "Version"
${If} $2 == ""
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "DisplayVersion"
${EndIf}
${If} $2 == ""
ReadRegStr $2 HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "Version"
${EndIf}
${If} $2 == ""
ReadRegStr $2 HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "DisplayVersion"
${EndIf}
SetRegView lastused
${Else}
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "Version"
${If} $2 == ""
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "DisplayVersion"
${EndIf}
${If} $2 == ""
ReadRegStr $2 HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "Version"
${EndIf}
${EndIf}
${ElseIf} "$1" == "x64"
${If} ${RunningX64}
SetRegView 64
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "Version"
${If} $2 == ""
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "DisplayVersion"
${EndIf}
${If} $2 == ""
ReadRegStr $2 HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "Version"
${EndIf}
${If} $2 == ""
ReadRegStr $2 HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "DisplayVersion"
${EndIf}
SetRegView lastused
${Else}
Goto VCRuntimeCheckEnd
${EndIf}
${ElseIf} "$1" == "arm64"
SetRegView 64
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "Version"
${If} $2 == ""
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "DisplayVersion"
${EndIf}
${If} $2 == ""
ReadRegStr $2 HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R8" "Version"
${EndIf}
SetRegView lastused
${EndIf}
; Fallback: Check for DLL files
${If} $2 == ""
${If} "$1" == "x86"
${If} ${FileExists} "$SYSDIR\vcruntime140.dll"
StrCpy $2 "14.0.0.0"
${ElseIf} ${FileExists} "$SYSDIR\msvcr120.dll"
StrCpy $2 "12.0.0.0"
${ElseIf} ${FileExists} "$SYSDIR\msvcr110.dll"
StrCpy $2 "11.0.0.0"
${ElseIf} ${FileExists} "$SYSDIR\msvcr100.dll"
StrCpy $2 "10.0.0.0"
${ElseIf} ${FileExists} "$SYSDIR\msvcr90.dll"
StrCpy $2 "9.0.0.0"
${ElseIf} ${FileExists} "$SYSDIR\msvcr80.dll"
StrCpy $2 "8.0.0.0"
${EndIf}
${ElseIf} "$1" == "x64"
${If} ${FileExists} "$WINDIR\System32\vcruntime140.dll"
StrCpy $2 "14.0.0.0"
${ElseIf} ${FileExists} "$WINDIR\System32\msvcr120.dll"
StrCpy $2 "12.0.0.0"
${ElseIf} ${FileExists} "$WINDIR\System32\msvcr110.dll"
StrCpy $2 "11.0.0.0"
${ElseIf} ${FileExists} "$WINDIR\System32\msvcr100.dll"
StrCpy $2 "10.0.0.0"
${EndIf}
${EndIf}
${EndIf}
; Validate version meets minimum requirement
${If} $2 != ""
StrCpy $R1 $2
${VersionCompare} $2 $R9 $3
${If} $3 == 0
${OrIf} $3 == 1
StrCpy $R0 "true"
${DebugMsg} "VC++ Runtime $0 ($1) found: version $R1"
${Else}
${DebugMsg} "VC++ Runtime $0 ($1) version $R1 < required $R9"
${EndIf}
${Else}
${DebugMsg} "VC++ Runtime $0 ($1) not found"
${EndIf}
VCRuntimeCheckEnd:
Pop $R9
Pop $R8
Pop $3
Pop $2
Pop $0
Pop $1
Exch $R1 ; Version
Exch
Exch $R0 ; Result
FunctionEnd
;------------------------------------------------------------
; NETRuntime_IsInstalled
; Check if a specific .NET runtime/framework is installed
;
; Stack Usage:
; Push "net48" ; framework (net20, net35, net40, net45-48, net50-80)
; Push "x64" ; arch (x86, x64, anycpu)
; Call NETRuntime_IsInstalled
; Pop $R9 ; Installed version string (empty if not found)
; Pop $R8 ; Result: "true" or "false"
;------------------------------------------------------------
Function NETRuntime_IsInstalled
Exch $R9 ; _INSTALLED_VERSION
Exch
Exch $R8 ; _RESULT
Exch
Exch 2
Exch $1 ; _ARCH
Exch
Exch 3
Exch $0 ; _FRAMEWORK
Push $2
Push $3
Push $4
StrCpy $R8 "false"
StrCpy $R9 ""
${Switch} "$0"
${Case} "net20"
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Version"
${IfNot} ${Errors}
StrCpy $R8 "true"
StrCpy $R9 "$2"
${DebugMsg} ".NET Framework 2.0 found: version $2"
${Else}
${DebugMsg} ".NET Framework 2.0 not found"
${EndIf}
${Break}
${Case} "net35"
ReadRegDWORD $2 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Install"
${If} $2 == 1
ReadRegStr $3 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Version"
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 3.5 found: version $3"
${Else}
${DebugMsg} ".NET Framework 3.5 not found"
${EndIf}
${Break}
${Case} "net40"
ReadRegDWORD $2 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Install"
${If} $2 == 1
ReadRegStr $3 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Version"
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.0 found: version $3"
${Else}
${DebugMsg} ".NET Framework 4.0 not found"
${EndIf}
${Break}
${Case} "net45"
${Case} "net451"
${Case} "net452"
${Case} "net46"
${Case} "net461"
${Case} "net462"
${Case} "net47"
${Case} "net471"
${Case} "net472"
${Case} "net48"
${Case} "net481"
; .NET Framework 4.5+ all use the same registry location
ReadRegDWORD $2 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Release"
${IfNot} ${Errors}
ReadRegStr $3 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Version"
; Check release number against minimum requirements
${Switch} "$0"
${Case} "net45"
IntCmp $2 378389 found_net45 not_found found_net45
found_net45:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.5+ found: version $3 (release $2)"
${Break}
${Case} "net451"
IntCmp $2 378675 found_net451 not_found found_net451
found_net451:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.5.1+ found: version $3 (release $2)"
${Break}
${Case} "net452"
IntCmp $2 379893 found_net452 not_found found_net452
found_net452:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.5.2+ found: version $3 (release $2)"
${Break}
${Case} "net46"
IntCmp $2 393295 found_net46 not_found found_net46
found_net46:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.6+ found: version $3 (release $2)"
${Break}
${Case} "net461"
IntCmp $2 394254 found_net461 not_found found_net461
found_net461:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.6.1+ found: version $3 (release $2)"
${Break}
${Case} "net462"
IntCmp $2 394802 found_net462 not_found found_net462
found_net462:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.6.2+ found: version $3 (release $2)"
${Break}
${Case} "net47"
IntCmp $2 460798 found_net47 not_found found_net47
found_net47:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.7+ found: version $3 (release $2)"
${Break}
${Case} "net471"
IntCmp $2 461308 found_net471 not_found found_net471
found_net471:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.7.1+ found: version $3 (release $2)"
${Break}
${Case} "net472"
IntCmp $2 461808 found_net472 not_found found_net472
found_net472:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.7.2+ found: version $3 (release $2)"
${Break}
${Case} "net48"
IntCmp $2 528040 found_net48 not_found found_net48
found_net48:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.8+ found: version $3 (release $2)"
${Break}
${Case} "net481"
IntCmp $2 533320 found_net481 not_found found_net481
found_net481:
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET Framework 4.8.1+ found: version $3 (release $2)"
${Break}
${EndSwitch}
Goto net_framework_done
not_found:
${DebugMsg} ".NET Framework $0 not found (release $2 too old)"
net_framework_done:
${Else}
${DebugMsg} ".NET Framework 4.5+ not found"
${EndIf}
${Break}
${Case} "net50"
${Case} "net60"
${Case} "net70"
${Case} "net80"
${Case} "net90"
; .NET Core/5+
${WordReplace} "$0" "net" "" "+" $2
${If} "$1" == "x64"
${OrIf} "$1" == "anycpu"
; Check 64-bit registry
${If} ${RunningX64}
SetRegView 64
ReadRegStr $3 HKLM "SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost" "Version"
${If} ${Errors}
ReadRegStr $3 HKLM "SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\x64\sharedhost" "Version"
${EndIf}
SetRegView lastused
${EndIf}
${ElseIf} "$1" == "x86"
; Check 32-bit registry
${If} ${RunningX64}
SetRegView 32
${EndIf}
ReadRegStr $3 HKLM "SOFTWARE\dotnet\Setup\InstalledVersions\x86\sharedhost" "Version"
${If} ${RunningX64}
SetRegView lastused
${EndIf}
${ElseIf} "$1" == "arm64"
; Check ARM64 registry
SetRegView 64
ReadRegStr $3 HKLM "SOFTWARE\dotnet\Setup\InstalledVersions\arm64\sharedhost" "Version"
SetRegView lastused
${EndIf}
${IfNot} ${Errors}
; Version check: ensure installed version >= requested version
${VersionCheck} $3 "$2.0" $4
${If} $4 >= 0
StrCpy $R8 "true"
StrCpy $R9 "$3"
${DebugMsg} ".NET $2+ found: version $3"
${Else}
${DebugMsg} ".NET $2+ found but version $3 is too old"
${EndIf}
${Else}
${DebugMsg} ".NET $2+ ($1) not found"
${EndIf}
${Break}
${Default}
${DebugMsg} "Unknown .NET framework: $0"
${Break}
${EndSwitch}
Pop $4
Pop $3
Pop $2
Exch $0
Exch 3
Exch $1
Exch 2
Exch $R8
Exch
Exch $R9
FunctionEnd
;============================================================
; DOWNLOAD FUNCTIONS
;============================================================
;------------------------------------------------------------
; Runtime_Download
; Download runtime redistributable from official sources
;
; Stack Usage:
; Push "vcruntime" ; type (vcruntime or dotnet)
; Push "2022" ; version
; Push "x64" ; arch
; Push "$TEMP\vc.exe" ; destination
; Call Runtime_Download
; Pop $R0 ; Result: "true" or "false"
;------------------------------------------------------------
Function Runtime_Download
Exch $R9 ; _RESULT
Exch
Exch $R8 ; _DESTINATION
Exch
Exch 2
Exch $1 ; _ARCH
Exch
Exch 3
Exch $0 ; _VERSION
Exch 4
Exch $2 ; _TYPE
Push $3
Push $4
Push $5
StrCpy $R9 "false"
StrCpy $3 ""
; Determine download URL
${If} "$2" == "vcruntime"
${Switch} "$0"
${Case} "2015"
${Case} "2017"
${Case} "2019"
${Case} "2022"
${If} "$1" == "x86"
StrCpy $3 "https://aka.ms/vs/17/release/vc_redist.x86.exe"
${ElseIf} "$1" == "x64"
StrCpy $3 "https://aka.ms/vs/17/release/vc_redist.x64.exe"
${ElseIf} "$1" == "arm64"
StrCpy $3 "https://aka.ms/vs/17/release/vc_redist.arm64.exe"
${Else}
${DebugMsg} "Unsupported architecture for VC++ runtime: $1"
Goto DownloadEnd
${EndIf}
${Break}
${Case} "2013"
${If} "$1" == "x86"
StrCpy $3 "https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x86.exe"
${ElseIf} "$1" == "x64"
StrCpy $3 "https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe"
${EndIf}
${Break}
${Case} "2012"
${If} "$1" == "x86"
StrCpy $3 "https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe"
${ElseIf} "$1" == "x64"
StrCpy $3 "https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe"
${EndIf}
${Break}
${Case} "2010"
${If} "$1" == "x86"
StrCpy $3 "https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe"
${ElseIf} "$1" == "x64"
StrCpy $3 "https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x64.exe"
${EndIf}
${Break}
${Default}
${DebugMsg} "No download URL for VC++ runtime version: $0"
Goto DownloadEnd
${Break}
${EndSwitch}
${ElseIf} "$2" == "dotnet"
${Switch} "$0"
${Case} "net48"
StrCpy $3 "https://download.microsoft.com/download/6/E/4/6E48E8AB-DC00-419E-9704-06DD46E5F81D/NDP48-x86-x64-AllOS-ENU.exe"
${Break}
${Case} "net462"
StrCpy $3 "https://download.microsoft.com/download/F/9/4/F942F07D-F26F-4F30-B4E3-EBD54FABA377/NDP462-KB3151800-x86-x64-AllOS-ENU.exe"
${Break}
${Case} "net60"
${If} "$1" == "x64"
StrCpy $3 "https://download.visualstudio.microsoft.com/download/pr/5d2a1ebf-2c3f-42d4-9770-7cf8faeb7f1d/2b1e6e5a87cf2df3c4c65d247b7a177a/dotnet-runtime-6.0.36-win-x64.exe"
${ElseIf} "$1" == "x86"
StrCpy $3 "https://download.visualstudio.microsoft.com/download/pr/5d2a1ebf-2c3f-42d4-9770-7cf8faeb7f1d/2b1e6e5a87cf2df3c4c65d247b7a177a/dotnet-runtime-6.0.36-win-x86.exe"
${ElseIf} "$1" == "arm64"
StrCpy $3 "https://download.visualstudio.microsoft.com/download/pr/5d2a1ebf-2c3f-42d4-9770-7cf8faeb7f1d/2b1e6e5a87cf2df3c4c65d247b7a177a/dotnet-runtime-6.0.36-win-arm64.exe"
${EndIf}
${Break}
${Case} "net80"
${If} "$1" == "x64"
StrCpy $3 "https://download.visualstudio.microsoft.com/download/pr/85c04f7e-ec7a-42a3-a23b-9fa1a445b6f9/b741713ef3a8722c7fd6efbaa4e71b96/dotnet-runtime-8.0.11-win-x64.exe"
${ElseIf} "$1" == "x86"
StrCpy $3 "https://download.visualstudio.microsoft.com/download/pr/85c04f7e-ec7a-42a3-a23b-9fa1a445b6f9/b741713ef3a8722c7fd6efbaa4e71b96/dotnet-runtime-8.0.11-win-x86.exe"
${ElseIf} "$1" == "arm64"
StrCpy $3 "https://download.visualstudio.microsoft.com/download/pr/85c04f7e-ec7a-42a3-a23b-9fa1a445b6f9/b741713ef3a8722c7fd6efbaa4e71b96/dotnet-runtime-8.0.11-win-arm64.exe"
${EndIf}
${Break}
${Default}
${DebugMsg} "No download URL for .NET runtime: $0"
Goto DownloadEnd
${Break}
${EndSwitch}
${Else}
${DebugMsg} "Unknown runtime type: $2"
Goto DownloadEnd
${EndIf}
${If} "$3" == ""
${DebugMsg} "Could not determine download URL"
Goto DownloadEnd
${EndIf}
${DebugMsg} "Downloading $2 $0 ($1) from: $3"
; Download using inetc plugin
inetc::get /popup "" /caption "Downloading $2 $0..." "$3" "$R8" /end
Pop $5
${If} $5 == "OK"
StrCpy $R9 "true"
${DebugMsg} "Downloaded successfully to: $R8"
${Else}
${DebugMsg} "Download failed: $5"
${EndIf}
DownloadEnd:
Pop $5
Pop $4
Pop $3
Exch $2
Exch 4
Exch $0
Exch 3
Exch $1
Exch 2
Exch $R8
Exch
Exch $R9
FunctionEnd
;============================================================
; INSTALLATION FUNCTIONS
;============================================================
;------------------------------------------------------------
; VCRuntime_Install
; Install VC++ runtime from an installer file
;
; Stack Usage:
; Push "2022" ; version
; Push "x64" ; arch
; Push "$TEMP\vc_redist.exe" ; source
; Call VCRuntime_Install
; Pop $R1 ; Error message (if any)
; Pop $R0 ; Result: "true" or "false"
;------------------------------------------------------------
Function VCRuntime_Install
Exch $R8 ; _SOURCE
Exch
Exch $1 ; _ARCH
Exch
Exch 2
Exch $0 ; _VERSION
Push $2
Push $3
Push $4
Push $R9
StrCpy $R0 "false"
StrCpy $R1 ""
; Check if source file exists
${If} ${FileExists} "$R8"
${DebugMsg} "Installing VC++ $0 ($1) from: $R8"
; Determine command line arguments
${If} "$0" == "2005"
${OrIf} "$0" == "2008"
${OrIf} "$0" == "2010"
StrCpy $R9 "/q"
${Else}
StrCpy $R9 "/install /quiet /norestart"
${EndIf}
; Execute installer
nsExec::ExecToStack '"$R8" $R9'
Pop $2 ; Exit code
Pop $3 ; Output
; Interpret exit codes
${Switch} $2
${Case} 0
StrCpy $R0 "true"
${DebugMsg} "VC++ $0 installed successfully"
${Break}
${Case} 3010
StrCpy $R0 "true"
StrCpy $R1 "Installation successful, restart required"
${DebugMsg} "VC++ $0 installed (restart required)"
${Break}
${Case} 1638
StrCpy $R0 "true"
StrCpy $R1 "A newer or same version is already installed"
${DebugMsg} "VC++ $0 already installed"
${Break}
${Case} 1603
StrCpy $R1 "Fatal installation error (1603)"
${DebugMsg} "VC++ $0 fatal error 1603"
${Break}
${Case} 1618
StrCpy $R1 "Another installation is in progress (1618)"
${DebugMsg} "VC++ $0 blocked by concurrent install"
${Break}
${Default}
StrCpy $R1 "Installation failed with exit code $2"
${DebugMsg} "VC++ $0 failed with exit code $2"
${Break}
${EndSwitch}
${Else}
StrCpy $R1 "Source file not found: $R8"
${DebugMsg} "VC++ installer missing at $R8"
${EndIf}
Pop $R9
Pop $4
Pop $3
Pop $2
Exch $0
Exch 2
Exch $1
Exch
Exch $R8
Pop $R8
Exch $R1 ; Error
Exch
Exch $R0 ; Result
FunctionEnd
;============================================================
; HELPER MACROS
;============================================================
; Check if VC++ runtime is installed
!macro CheckVCRuntime VERSION ARCH RESULT_VAR VERSION_VAR
Push "${VERSION}"
Push "${ARCH}"
Call VCRuntime_IsInstalled
Pop ${RESULT_VAR}
Pop ${VERSION_VAR}
!macroend
!define CheckVCRuntime "!insertmacro CheckVCRuntime"
; Check if .NET runtime is installed
!macro CheckNETRuntime FRAMEWORK ARCH RESULT_VAR VERSION_VAR
Push "${FRAMEWORK}"
Push "${ARCH}"
StrCpy ${RESULT_VAR} ""
StrCpy ${VERSION_VAR} ""
Call NETRuntime_IsInstalled
Pop ${VERSION_VAR}
Pop ${RESULT_VAR}
!macroend
!define CheckNETRuntime "!insertmacro CheckNETRuntime"
; Download runtime
!macro DownloadRuntime TYPE VERSION ARCH DEST RESULT_VAR
Push "${TYPE}"
Push "${VERSION}"
Push "${ARCH}"
Push "${DEST}"
Call Runtime_Download
Pop ${RESULT_VAR}
!macroend
!define DownloadRuntime "!insertmacro DownloadRuntime"
; Install VC++ runtime
!macro InstallVCRuntime VERSION ARCH SOURCE RESULT_VAR ERROR_VAR
Push "${VERSION}"
Push "${ARCH}"
Push "${SOURCE}"
Call VCRuntime_Install
Pop ${RESULT_VAR}
Pop ${ERROR_VAR}
!macroend
!define InstallVCRuntime "!insertmacro InstallVCRuntime"
; Complete workflow: Check, Download if needed, Install
!macro EnsureVCRuntime VERSION ARCH FINAL_RESULT
Push $0
Push $1
Push $2
Push $3
; Check if already installed
${CheckVCRuntime} "${VERSION}" "${ARCH}" $0 $1
${If} $0 == "true"
DetailPrint "VC++ ${VERSION} (${ARCH}) already installed: $1"
StrCpy ${FINAL_RESULT} "true"
${Else}
DetailPrint "VC++ ${VERSION} (${ARCH}) not found, downloading..."
; Download
StrCpy $2 "$TEMP\vc_redist_${VERSION}_${ARCH}.exe"
${DownloadRuntime} "vcruntime" "${VERSION}" "${ARCH}" $2 $0
${If} $0 == "true"
DetailPrint "Download complete, installing..."
; Install
${InstallVCRuntime} "${VERSION}" "${ARCH}" $2 $0 $3
${If} $0 == "true"
DetailPrint "VC++ ${VERSION} (${ARCH}) installed successfully"
${If} $3 != ""
DetailPrint "Note: $3"
${EndIf}
StrCpy ${FINAL_RESULT} "true"
${Else}
DetailPrint "Installation failed: $3"
StrCpy ${FINAL_RESULT} "false"
${EndIf}
; Clean up
Delete $2
${Else}
DetailPrint "Download failed"
StrCpy ${FINAL_RESULT} "false"
${EndIf}
${EndIf}
Pop $3
Pop $2
Pop $1
Pop $0
!macroend
!define EnsureVCRuntime "!insertmacro EnsureVCRuntime"
; Complete workflow for .NET: Check, Download if needed, Install
!macro EnsureNETRuntime FRAMEWORK ARCH FINAL_RESULT
Push $0
Push $1
Push $2
Push $3
; Check if already installed
${CheckNETRuntime} "${FRAMEWORK}" "${ARCH}" $0 $1
${If} $0 == "true"
DetailPrint ".NET ${FRAMEWORK} (${ARCH}) already installed: $1"
StrCpy ${FINAL_RESULT} "true"
${Else}
DetailPrint ".NET ${FRAMEWORK} (${ARCH}) not found, downloading..."
; Download
StrCpy $2 "$TEMP\dotnet_${FRAMEWORK}_${ARCH}.exe"
${DownloadRuntime} "dotnet" "${FRAMEWORK}" "${ARCH}" $2 $0
${If} $0 == "true"
DetailPrint "Download complete, installing..."
; Install (using similar logic as VC++ but with /install /quiet /norestart)
${If} ${FileExists} $2
nsExec::ExecToStack '"$2" /install /quiet /norestart'
Pop $0
Pop $3
${Switch} $0
${Case} 0
${Case} 3010
${Case} 1638
DetailPrint ".NET ${FRAMEWORK} installed successfully"
StrCpy ${FINAL_RESULT} "true"
${Break}
${Default}
DetailPrint ".NET installation failed with exit code $0"
StrCpy ${FINAL_RESULT} "false"
${Break}
${EndSwitch}
${Else}
DetailPrint "Downloaded file not found"
StrCpy ${FINAL_RESULT} "false"
${EndIf}
; Clean up
Delete $2
${Else}
DetailPrint "Download failed"
StrCpy ${FINAL_RESULT} "false"
${EndIf}
${EndIf}
Pop $3
Pop $2
Pop $1
Pop $0
!macroend
!define EnsureNETRuntime "!insertmacro EnsureNETRuntime"
;============================================================
; USAGE EXAMPLES
;============================================================
/*
Example 1: Check for VC++ Runtime
-----------------------------------
${CheckVCRuntime} "2022" "x64" $IsInstalled $Version
${If} $IsInstalled == "true"
DetailPrint "VC++ 2022 x64 is installed: $Version"
${Else}
DetailPrint "VC++ 2022 x64 is not installed"
${EndIf}
Example 2: Check for .NET Framework
------------------------------------
${CheckNETRuntime} "net48" "anycpu" $IsInstalled $Version
${If} $IsInstalled == "true"
DetailPrint ".NET Framework 4.8 is installed: $Version"
${EndIf}
Example 3: Check for .NET Core/5+
----------------------------------
${CheckNETRuntime} "net80" "x64" $IsInstalled $Version
${If} $IsInstalled == "true"
DetailPrint ".NET 8.0 x64 is installed: $Version"
${EndIf}
Example 4: Download Runtime
----------------------------
${DownloadRuntime} "vcruntime" "2022" "x64" "$TEMP\vc.exe" $Success
${If} $Success == "true"
DetailPrint "Downloaded successfully"
${EndIf}
Example 5: Install VC++ Runtime
--------------------------------
${InstallVCRuntime} "2022" "x64" "$TEMP\vc_redist.x64.exe" $Success $Error
${If} $Success == "true"
DetailPrint "Installed successfully"
${Else}
MessageBox MB_OK "Installation failed: $Error"
${EndIf}
Example 6: Complete Workflow (Recommended)
-------------------------------------------
Section "Install"
; Ensure VC++ 2022 x64 is installed
${EnsureVCRuntime} "2022" "x64" $0
${If} $0 != "true"
MessageBox MB_OK "Failed to install VC++ 2022 runtime"
Abort
${EndIf}
; Ensure .NET 8.0 x64 is installed
${EnsureNETRuntime} "net80" "x64" $0
${If} $0 != "true"
MessageBox MB_OK "Failed to install .NET 8.0 runtime"
Abort
${EndIf}
; Continue with your installation
File "MyApp.exe"
SectionEnd
Example 7: Multiple Runtime Checks
-----------------------------------
Function .onInit
; Check all required runtimes
${CheckVCRuntime} "2022" "x64" $0 $1
${CheckVCRuntime} "2022" "x86" $2 $3
${CheckNETRuntime} "net48" "anycpu" $4 $5
${CheckNETRuntime} "net60" "x64" $6 $7
StrCpy $R0 ""
${If} $0 != "true"
StrCpy $R0 "$R0- VC++ 2022 x64$\n"
${EndIf}
${If} $2 != "true"
StrCpy $R0 "$R0- VC++ 2022 x86$\n"
${EndIf}
${If} $4 != "true"
StrCpy $R0 "$R0- .NET Framework 4.8$\n"
${EndIf}
${If} $6 != "true"
StrCpy $R0 "$R0- .NET 6.0 x64$\n"
${EndIf}
${If} $R0 != ""
MessageBox MB_YESNO "Missing runtimes:$\n$\n$R0$\nInstall them now?" IDYES install
Abort
install:
; Proceed with installation
${EndIf}
FunctionEnd
*/
!endif ; RUNTIME_MANAGER_NSH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment