; Modified from https://github.com/DomGries/InnoDependencyInstaller [Code] // types and variables type TDependency_Entry = record Filename: String; Parameters: String; Title: String; URL: String; Checksum: String; ForceSuccess: Boolean; RestartAfter: Boolean; end; var Dependency_Memo: String; Dependency_List: array of TDependency_Entry; Dependency_NeedRestart, Dependency_ForceX86: Boolean; Dependency_DownloadPage: TDownloadWizardPage; procedure Dependency_Add(const Filename, Parameters, Title, URL, Checksum: String; const ForceSuccess, RestartAfter: Boolean); var Dependency: TDependency_Entry; DependencyCount: Integer; begin Dependency_Memo := Dependency_Memo + #13#10 + '%1' + Title; Dependency.Filename := Filename; Dependency.Parameters := Parameters; Dependency.Title := Title; if FileExists(ExpandConstant('{tmp}{\}') + Filename) then begin Dependency.URL := ''; end else begin Dependency.URL := URL; end; Dependency.Checksum := Checksum; Dependency.ForceSuccess := ForceSuccess; Dependency.RestartAfter := RestartAfter; DependencyCount := GetArrayLength(Dependency_List); SetArrayLength(Dependency_List, DependencyCount + 1); Dependency_List[DependencyCount] := Dependency; end; procedure Dependency_Internal1; begin Dependency_DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil); end; function Dependency_Internal2(var NeedsRestart: Boolean): String; var DependencyCount, DependencyIndex, ResultCode: Integer; Retry: Boolean; TempValue: String; begin DependencyCount := GetArrayLength(Dependency_List); if DependencyCount > 0 then begin Dependency_DownloadPage.Show; for DependencyIndex := 0 to DependencyCount - 1 do begin if Dependency_List[DependencyIndex].URL <> '' then begin Dependency_DownloadPage.Clear; Dependency_DownloadPage.Add(Dependency_List[DependencyIndex].URL, Dependency_List[DependencyIndex].Filename, Dependency_List[DependencyIndex].Checksum); Retry := True; while Retry do begin Retry := False; try Dependency_DownloadPage.Download; except if Dependency_DownloadPage.AbortedByUser then begin Result := Dependency_List[DependencyIndex].Title; DependencyIndex := DependencyCount; end else begin case SuppressibleMsgBox(AddPeriod(GetExceptionMessage), mbError, MB_ABORTRETRYIGNORE, IDIGNORE) of IDABORT: begin Result := Dependency_List[DependencyIndex].Title; DependencyIndex := DependencyCount; end; IDRETRY: begin Retry := True; end; end; end; end; end; end; end; if Result = '' then begin for DependencyIndex := 0 to DependencyCount - 1 do begin Dependency_DownloadPage.SetText(Dependency_List[DependencyIndex].Title, ''); Dependency_DownloadPage.SetProgress(DependencyIndex + 1, DependencyCount + 1); while True do begin ResultCode := 0; if ShellExec('', ExpandConstant('{tmp}{\}') + Dependency_List[DependencyIndex].Filename, Dependency_List[DependencyIndex].Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode) then begin if Dependency_List[DependencyIndex].RestartAfter then begin if DependencyIndex = DependencyCount - 1 then begin Dependency_NeedRestart := True; end else begin NeedsRestart := True; Result := Dependency_List[DependencyIndex].Title; end; break; end else if (ResultCode = 0) or Dependency_List[DependencyIndex].ForceSuccess then begin // ERROR_SUCCESS (0) break; end else if ResultCode = 1641 then begin // ERROR_SUCCESS_REBOOT_INITIATED (1641) NeedsRestart := True; Result := Dependency_List[DependencyIndex].Title; break; end else if ResultCode = 3010 then begin // ERROR_SUCCESS_REBOOT_REQUIRED (3010) Dependency_NeedRestart := True; break; end; end; case SuppressibleMsgBox(FmtMessage(SetupMessage(msgErrorFunctionFailed), [Dependency_List[DependencyIndex].Title, IntToStr(ResultCode)]), mbError, MB_ABORTRETRYIGNORE, IDIGNORE) of IDABORT: begin Result := Dependency_List[DependencyIndex].Title; break; end; IDIGNORE: begin break; end; end; end; if Result <> '' then begin break; end; end; if NeedsRestart then begin TempValue := '"' + ExpandConstant('{srcexe}') + '" /restart=1 /LANG="' + ExpandConstant('{language}') + '" /DIR="' + WizardDirValue + '" /GROUP="' + WizardGroupValue + '" /TYPE="' + WizardSetupType(False) + '" /COMPONENTS="' + WizardSelectedComponents(False) + '" /TASKS="' + WizardSelectedTasks(False) + '"'; if WizardNoIcons then begin TempValue := TempValue + ' /NOICONS'; end; RegWriteStringValue(HKA, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', '{#SetupSetting("AppName")}', TempValue); end; end; Dependency_DownloadPage.Hide; end; end; function Dependency_Internal3(const Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; begin Result := ''; if MemoUserInfoInfo <> '' then begin Result := Result + MemoUserInfoInfo + Newline + NewLine; end; if MemoDirInfo <> '' then begin Result := Result + MemoDirInfo + Newline + NewLine; end; if MemoTypeInfo <> '' then begin Result := Result + MemoTypeInfo + Newline + NewLine; end; if MemoComponentsInfo <> '' then begin Result := Result + MemoComponentsInfo + Newline + NewLine; end; if MemoGroupInfo <> '' then begin Result := Result + MemoGroupInfo + Newline + NewLine; end; if MemoTasksInfo <> '' then begin Result := Result + MemoTasksInfo; end; if Dependency_Memo <> '' then begin if MemoTasksInfo = '' then begin Result := Result + SetupMessage(msgReadyMemoTasks); end; Result := Result + FmtMessage(Dependency_Memo, [Space]); end; end; function Dependency_Internal4: Boolean; begin Result := Dependency_NeedRestart; end; function Dependency_IsX64: Boolean; begin Result := not Dependency_ForceX86 and Is64BitInstallMode; end; function Dependency_String(const x86, x64: String): String; begin if Dependency_IsX64 then begin Result := x64; end else begin Result := x86; end; end; function Dependency_ArchSuffix: String; begin Result := Dependency_String('_x64', '_x64'); end; function Dependency_ArchTitle: String; begin Result := Dependency_String(' (x86)', ' (x64)'); end; function Dependency_IsNetCoreInstalled(const Version: String): Boolean; var ResultCode: Integer; begin // source code: https://github.com/dotnet/deployment-tools/tree/main/src/clickonce/native/projects/NetCoreCheck if not FileExists(ExpandConstant('{tmp}{\}') + 'netcorecheck' + Dependency_ArchSuffix + '.exe') then begin ExtractTemporaryFile('netcorecheck' + Dependency_ArchSuffix + '.exe'); end; Result := ShellExec('', ExpandConstant('{tmp}{\}') + 'netcorecheck' + Dependency_ArchSuffix + '.exe', Version, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0); end; function Dependency_IsVCRuntimeInstalled(const Arch: String; const Major, Minor, Bld: Cardinal): Boolean; var InstalledFlag, InstMajor, InstMinor, InstBld: Cardinal; Key: String; begin // Canonical VC++ runtime detection per Microsoft docs: // https://learn.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files // The redistributable installer writes these values to both registry views, // so a plain HKLM read is correct from Inno Setup's 32-bit process. Key := 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\' + Arch; Result := RegQueryDWordValue(HKLM, Key, 'Installed', InstalledFlag) and (InstalledFlag = 1) and RegQueryDWordValue(HKLM, Key, 'Major', InstMajor) and RegQueryDWordValue(HKLM, Key, 'Minor', InstMinor) and RegQueryDWordValue(HKLM, Key, 'Bld', InstBld) and ((InstMajor > Major) or ((InstMajor = Major) and (InstMinor > Minor)) or ((InstMajor = Major) and (InstMinor = Minor) and (InstBld >= Bld))); end; procedure Dependency_AddVC2015To2022; var Arch, Url: String; MinMajor, MinMinor, MinBld: Cardinal; begin // https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist MinMajor := 14; MinMinor := 30; MinBld := 30704; if IsARM64 then begin Arch := 'arm64'; Url := 'https://aka.ms/vc14/vc_redist.arm64.exe'; end else begin Arch := 'x64'; Url := 'https://aka.ms/vc14/vc_redist.x64.exe'; end; // Primary: registry-based detection (Microsoft's documented method, stable // across installer builds). Fallback: MSI UpgradeCode lookup for x64 only // (the x64 UpgradeCode {36F68A90-...} is verified; keeping a fallback helps // on unusual install states). See issue #4596 for the regression this // replaces, where a ProductCode was mistakenly used with IsMsiProductInstalled. if Dependency_IsVCRuntimeInstalled(Arch, MinMajor, MinMinor, MinBld) then Exit; if (Arch = 'x64') and IsMsiProductInstalled('{36F68A90-239C-34DF-B58C-64B30153CE35}', PackVersionComponents(MinMajor, MinMinor, MinBld, 0)) then Exit; Dependency_Add('vcredist2022_' + Arch + '.exe', '/passive /norestart', 'Visual C++ 2015-2022 Redistributable (' + Arch + ')', Url, '', False, False); end; procedure Dependency_AddWebView2; var WebView2URL, WebView2Title: String; begin // https://developer.microsoft.com/en-us/microsoft-edge/webview2 if not RegValueExists(HKLM, Dependency_String('SOFTWARE', 'SOFTWARE\WOW6432Node') + '\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}', 'pv') then begin if IsARM64 then begin WebView2URL := 'https://go.microsoft.com/fwlink/?linkid=2099616'; WebView2Title := 'WebView2 Runtime (ARM64)'; end else begin WebView2URL := 'https://go.microsoft.com/fwlink/?linkid=2124701'; WebView2Title := 'WebView2 Runtime (x64)'; end; Dependency_Add('MicrosoftEdgeWebview2Setup.exe', '/silent /install', WebView2Title, WebView2URL, '', False, False); end; end; [Files] #ifdef Dependency_Path_NetCoreCheck ; download netcorecheck_x64.exe: https://www.nuget.org/packages/Microsoft.NET.Tools.NETCoreCheck.x64 Source: "{#Dependency_Path_NetCoreCheck}netcorecheck_x64.exe"; Flags: dontcopy noencryption #endif #ifdef Dependency_Path_DirectX Source: "{#Dependency_Path_DirectX}dxwebsetup.exe"; Flags: dontcopy noencryption #endif