Last active
November 8, 2025 05:35
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ; 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