# ================================================== # Windows Runtime Installer (WRI) - GUI Edition # VC++ 2005-2022 + DirectX + .NET + More # Works with PowerShell 5.1+ or PowerShell 7+ # ================================================== # Require admin elevation $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { $scriptPath = if ($PSCommandPath) { $PSCommandPath } elseif ($MyInvocation.MyCommand.Path) { $MyInvocation.MyCommand.Path } else { $null } if ($scriptPath) { try { $shell = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh.exe" } else { "powershell.exe" } Start-Process $shell -ArgumentList "-ExecutionPolicy Bypass -File `"$scriptPath`"" -Verb RunAs } catch { Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show("This application requires administrator privileges.", "Admin Required", "OK", "Warning") } } exit } Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $ErrorActionPreference = "SilentlyContinue" $ProgressPreference = "SilentlyContinue" # ===================== # Load Redistributable Data from GitHub # ===================== $JsonUrl = "https://raw.githubusercontent.com/KaladinDMP/AG-WRI-GUI/main/redists.json" $Redists = @{ x86 = @(); x64 = @(); dotnet = @(); extras = @() } try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $jsonContent = Invoke-WebRequest -Uri $JsonUrl -UseBasicParsing -ErrorAction Stop -TimeoutSec 15 $jsonData = $jsonContent.Content | ConvertFrom-Json foreach ($item in $jsonData.vc_redists.x86) { $Redists.x86 += @{ Name = $item.name; Version = $item.version; Url = $item.url; Args = $item.args } } foreach ($item in $jsonData.vc_redists.x64) { $Redists.x64 += @{ Name = $item.name; Version = $item.version; Url = $item.url; Args = $item.args } } foreach ($item in $jsonData.dotnet) { $entry = @{ Name = $item.name; Version = $item.version; Args = $item.args; Arch = $item.arch } if ($item.url) { $entry.Url = $item.url } if ($item.url_x86) { $entry.Url_x86 = $item.url_x86 } if ($item.url_x64) { $entry.Url_x64 = $item.url_x64 } $Redists.dotnet += $entry } foreach ($item in $jsonData.extras) { $entry = @{ Name = $item.name; Version = $item.version; Args = $item.args } if ($item.url) { $entry.Url = $item.url } if ($item.url_x86) { $entry.Url_x86 = $item.url_x86 } if ($item.url_x64) { $entry.Url_x64 = $item.url_x64 } if ($item.isDirectX -eq $true) { $entry.IsDirectX = $true } if ($item.isOpenAL -eq $true) { $entry.IsOpenAL = $true } if ($item.isMsi -eq $true) { $entry.IsMsi = $true } $Redists.extras += $entry } } catch { [System.Windows.Forms.MessageBox]::Show("Failed to load package list from GitHub.`n`nError: $($_.Exception.Message)", "Load Error", "OK", "Error") exit } $script:failures = @() $script:installCompleted = $false # ===================== # Main Form # ===================== $form = New-Object System.Windows.Forms.Form $form.Text = "AG Windows Runtime Installer (WRI) GUI" $form.Size = New-Object System.Drawing.Size(620, 720) $form.StartPosition = "CenterScreen" $form.FormBorderStyle = "FixedSingle" $form.MaximizeBox = $false $form.BackColor = [System.Drawing.Color]::FromArgb(26, 26, 26) $form.ForeColor = [System.Drawing.Color]::White $form.Font = New-Object System.Drawing.Font("Segoe UI", 9) $titleLabel = New-Object System.Windows.Forms.Label $titleLabel.Text = "AG Windows Runtime Installer GUI" $titleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 18, [System.Drawing.FontStyle]::Bold) $titleLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 174, 219) $titleLabel.AutoSize = $true $titleLabel.Location = New-Object System.Drawing.Point(160, 12) $form.Controls.Add($titleLabel) $subtitleLabel = New-Object System.Windows.Forms.Label $subtitleLabel.Text = "VC++ / DirectX / .NET Runtimes / Extras" $subtitleLabel.ForeColor = [System.Drawing.Color]::FromArgb(140, 140, 140) $subtitleLabel.AutoSize = $true $subtitleLabel.Location = New-Object System.Drawing.Point(185, 48) $form.Controls.Add($subtitleLabel) $adminLabel = New-Object System.Windows.Forms.Label $adminLabel.Text = "[OK] Running as Administrator" $adminLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 180, 0) $adminLabel.Font = New-Object System.Drawing.Font("Segoe UI", 8) $adminLabel.AutoSize = $true $adminLabel.Location = New-Object System.Drawing.Point(220, 68) $form.Controls.Add($adminLabel) $tabControl = New-Object System.Windows.Forms.TabControl $tabControl.Location = New-Object System.Drawing.Point(20, 95) $tabControl.Size = New-Object System.Drawing.Size(560, 405) $tabControl.Font = New-Object System.Drawing.Font("Segoe UI", 9) $form.Controls.Add($tabControl) $tabVC = New-Object System.Windows.Forms.TabPage $tabVC.Text = " Visual C++ " $tabVC.BackColor = [System.Drawing.Color]::FromArgb(30, 30, 30) $tabDotNet = New-Object System.Windows.Forms.TabPage $tabDotNet.Text = " .NET Runtimes " $tabDotNet.BackColor = [System.Drawing.Color]::FromArgb(30, 30, 30) $tabExtras = New-Object System.Windows.Forms.TabPage $tabExtras.Text = " Extras " $tabExtras.BackColor = [System.Drawing.Color]::FromArgb(30, 30, 30) $tabControl.TabPages.AddRange(@($tabVC, $tabDotNet, $tabExtras)) $script:allCheckboxes = @() function Add-RuntimeCheckbox { param($Parent, $Name, $Version, $Y, $Tag) $cb = New-Object System.Windows.Forms.CheckBox $cb.Text = "$Name" $cb.Tag = $Tag $cb.Location = New-Object System.Drawing.Point(20, $Y) $cb.Size = New-Object System.Drawing.Size(300, 22) $cb.ForeColor = [System.Drawing.Color]::White $cb.FlatStyle = "Flat" [void]$Parent.Controls.Add($cb) $verLabel = New-Object System.Windows.Forms.Label $verLabel.Text = "v$Version" $verLabel.Location = New-Object System.Drawing.Point(330, ($Y + 2)) $verLabel.Size = New-Object System.Drawing.Size(180, 20) $verLabel.ForeColor = [System.Drawing.Color]::FromArgb(100, 100, 100) [void]$Parent.Controls.Add($verLabel) $script:allCheckboxes += $cb } # Visual C++ Tab $vcLabel = New-Object System.Windows.Forms.Label $vcLabel.Text = "Visual C++ Redistributables (x86 + x64)" $vcLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) $vcLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 174, 219) $vcLabel.Location = New-Object System.Drawing.Point(20, 15) $vcLabel.AutoSize = $true $tabVC.Controls.Add($vcLabel) $yPos = 50 for ($i = 0; $i -lt $Redists.x86.Count; $i++) { $item = $Redists.x86[$i] Add-RuntimeCheckbox -Parent $tabVC -Name $item.Name -Version $item.Version -Y $yPos -Tag @{Type="VC"; Index=$i} $yPos += 32 } $vcSelectAll = New-Object System.Windows.Forms.Button $vcSelectAll.Text = "Select All VC++" $vcSelectAll.Location = New-Object System.Drawing.Point(20, ($yPos + 20)) $vcSelectAll.Size = New-Object System.Drawing.Size(130, 30) $vcSelectAll.FlatStyle = "Flat" $vcSelectAll.BackColor = [System.Drawing.Color]::FromArgb(50, 50, 50) $vcSelectAll.ForeColor = [System.Drawing.Color]::White $vcSelectAll.Add_Click({ $script:allCheckboxes | ForEach-Object { if ($_.Tag.Type -eq "VC") { $_.Checked = $true } } | Out-Null }) $tabVC.Controls.Add($vcSelectAll) $vcClearAll = New-Object System.Windows.Forms.Button $vcClearAll.Text = "Clear All" $vcClearAll.Location = New-Object System.Drawing.Point(160, ($yPos + 20)) $vcClearAll.Size = New-Object System.Drawing.Size(100, 30) $vcClearAll.FlatStyle = "Flat" $vcClearAll.BackColor = [System.Drawing.Color]::FromArgb(50, 50, 50) $vcClearAll.ForeColor = [System.Drawing.Color]::White $vcClearAll.Add_Click({ $script:allCheckboxes | ForEach-Object { if ($_.Tag.Type -eq "VC") { $_.Checked = $false } } | Out-Null }) $tabVC.Controls.Add($vcClearAll) # .NET Tab $dotnetLabel = New-Object System.Windows.Forms.Label $dotnetLabel.Text = ".NET Framework and Runtime" $dotnetLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) $dotnetLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 174, 219) $dotnetLabel.Location = New-Object System.Drawing.Point(20, 15) $dotnetLabel.AutoSize = $true $tabDotNet.Controls.Add($dotnetLabel) $yPos = 50 for ($i = 0; $i -lt $Redists.dotnet.Count; $i++) { $item = $Redists.dotnet[$i] Add-RuntimeCheckbox -Parent $tabDotNet -Name $item.Name -Version $item.Version -Y $yPos -Tag @{Type="DotNet"; Index=$i} $yPos += 32 } # Extras Tab $extrasLabel = New-Object System.Windows.Forms.Label $extrasLabel.Text = "DirectX, OpenAL, XNA and More" $extrasLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) $extrasLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 174, 219) $extrasLabel.Location = New-Object System.Drawing.Point(20, 15) $extrasLabel.AutoSize = $true $tabExtras.Controls.Add($extrasLabel) $yPos = 50 for ($i = 0; $i -lt $Redists.extras.Count; $i++) { $item = $Redists.extras[$i] $ver = if ($item.Version) { $item.Version } else { "Latest" } Add-RuntimeCheckbox -Parent $tabExtras -Name $item.Name -Version $ver -Y $yPos -Tag @{Type="Extra"; Index=$i} $yPos += 32 } # Bottom Panel $bottomPanel = New-Object System.Windows.Forms.Panel $bottomPanel.Location = New-Object System.Drawing.Point(20, 510) $bottomPanel.Size = New-Object System.Drawing.Size(560, 160) $bottomPanel.BackColor = [System.Drawing.Color]::FromArgb(26, 26, 26) $form.Controls.Add($bottomPanel) $selectAllBtn = New-Object System.Windows.Forms.Button $selectAllBtn.Text = "Select All" $selectAllBtn.Location = New-Object System.Drawing.Point(0, 0) $selectAllBtn.Size = New-Object System.Drawing.Size(100, 30) $selectAllBtn.FlatStyle = "Flat" $selectAllBtn.BackColor = [System.Drawing.Color]::FromArgb(50, 50, 50) $selectAllBtn.ForeColor = [System.Drawing.Color]::White $selectAllBtn.Add_Click({ $script:allCheckboxes | ForEach-Object { $_.Checked = $true } | Out-Null }) $bottomPanel.Controls.Add($selectAllBtn) $selectNoneBtn = New-Object System.Windows.Forms.Button $selectNoneBtn.Text = "Select None" $selectNoneBtn.Location = New-Object System.Drawing.Point(110, 0) $selectNoneBtn.Size = New-Object System.Drawing.Size(100, 30) $selectNoneBtn.FlatStyle = "Flat" $selectNoneBtn.BackColor = [System.Drawing.Color]::FromArgb(50, 50, 50) $selectNoneBtn.ForeColor = [System.Drawing.Color]::White $selectNoneBtn.Add_Click({ $script:allCheckboxes | ForEach-Object { $_.Checked = $false } | Out-Null }) $bottomPanel.Controls.Add($selectNoneBtn) $selectEssentialBtn = New-Object System.Windows.Forms.Button $selectEssentialBtn.Text = "Essentials" $selectEssentialBtn.Location = New-Object System.Drawing.Point(220, 0) $selectEssentialBtn.Size = New-Object System.Drawing.Size(110, 30) $selectEssentialBtn.FlatStyle = "Flat" $selectEssentialBtn.BackColor = [System.Drawing.Color]::FromArgb(60, 60, 60) $selectEssentialBtn.ForeColor = [System.Drawing.Color]::Cyan $selectEssentialBtn.Add_Click({ $script:allCheckboxes | ForEach-Object { $_.Checked = $false } | Out-Null $script:allCheckboxes | ForEach-Object { if ($_.Tag.Type -eq "VC" -or $_.Text -match "DirectX") { $_.Checked = $true } } | Out-Null }) $bottomPanel.Controls.Add($selectEssentialBtn) $selectGamingBtn = New-Object System.Windows.Forms.Button $selectGamingBtn.Text = "Gaming" $selectGamingBtn.Location = New-Object System.Drawing.Point(340, 0) $selectGamingBtn.Size = New-Object System.Drawing.Size(100, 30) $selectGamingBtn.FlatStyle = "Flat" $selectGamingBtn.BackColor = [System.Drawing.Color]::FromArgb(60, 60, 60) $selectGamingBtn.ForeColor = [System.Drawing.Color]::Magenta $selectGamingBtn.Add_Click({ $script:allCheckboxes | ForEach-Object { $_.Checked = $false } | Out-Null $script:allCheckboxes | ForEach-Object { if ($_.Tag.Type -eq "VC" -or $_.Text -match "DirectX|OpenAL|XNA") { $_.Checked = $true } } | Out-Null }) $bottomPanel.Controls.Add($selectGamingBtn) $progressBar = New-Object System.Windows.Forms.ProgressBar $progressBar.Location = New-Object System.Drawing.Point(0, 45) $progressBar.Size = New-Object System.Drawing.Size(560, 25) $progressBar.Style = "Continuous" $bottomPanel.Controls.Add($progressBar) $statusLabel = New-Object System.Windows.Forms.Label $statusLabel.Text = "Ready - Select items and click Install" $statusLabel.Location = New-Object System.Drawing.Point(0, 75) $statusLabel.Size = New-Object System.Drawing.Size(560, 20) $statusLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 200, 0) $bottomPanel.Controls.Add($statusLabel) $installBtn = New-Object System.Windows.Forms.Button $installBtn.Text = "Install Selected" $installBtn.Location = New-Object System.Drawing.Point(175, 105) $installBtn.Size = New-Object System.Drawing.Size(200, 45) $installBtn.FlatStyle = "Flat" $installBtn.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 215) $installBtn.ForeColor = [System.Drawing.Color]::White $installBtn.Font = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold) $installBtn.FlatAppearance.BorderSize = 0 $bottomPanel.Controls.Add($installBtn) function Update-Status { param([string]$Text, [string]$Color = "Green") $statusLabel.Text = $Text switch ($Color) { "Green" { $statusLabel.ForeColor = [System.Drawing.Color]::FromArgb(0, 200, 0) } "Yellow" { $statusLabel.ForeColor = [System.Drawing.Color]::FromArgb(255, 200, 0) } "Red" { $statusLabel.ForeColor = [System.Drawing.Color]::FromArgb(255, 80, 80) } "Cyan" { $statusLabel.ForeColor = [System.Drawing.Color]::Cyan } } [System.Windows.Forms.Application]::DoEvents() } function Install-Package { param([string]$Name, [string]$Url, [string]$Arguments, [bool]$IsMsi = $false, [bool]$IsDirectX = $false, [bool]$IsOpenAL = $false) Update-Status "Downloading $Name..." "Yellow" try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 if ($IsDirectX) { $dxExe = Join-Path $env:TEMP "directx_jun2010_redist.exe" $dxPath = Join-Path $env:TEMP "DXSETUP" Invoke-WebRequest -Uri $Url -OutFile $dxExe -UseBasicParsing -ErrorAction Stop Update-Status "Extracting $Name..." "Cyan" if (Test-Path $dxPath) { Remove-Item $dxPath -Recurse -Force } New-Item -ItemType Directory -Path $dxPath -Force | Out-Null $null = Start-Process -FilePath $dxExe -ArgumentList "/Q /T:`"$dxPath`"" -Wait -PassThru -WindowStyle Hidden Update-Status "Installing $Name..." "Cyan" $proc = Start-Process -FilePath "$dxPath\DXSETUP.exe" -ArgumentList "/silent" -Wait -PassThru -WindowStyle Hidden Remove-Item $dxExe -Force -ErrorAction SilentlyContinue Remove-Item $dxPath -Recurse -Force -ErrorAction SilentlyContinue return @{Success=$true; Message=""} } if ($IsOpenAL) { $zipPath = Join-Path $env:TEMP "oalinst.zip" $extractPath = Join-Path $env:TEMP "OpenAL" Invoke-WebRequest -Uri $Url -OutFile $zipPath -UseBasicParsing -ErrorAction Stop Update-Status "Extracting $Name..." "Cyan" if (Test-Path $extractPath) { Remove-Item $extractPath -Recurse -Force } Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force Update-Status "Installing $Name..." "Cyan" $installer = Get-ChildItem -Path $extractPath -Filter "*.exe" -Recurse | Select-Object -First 1 if ($installer) { $null = Start-Process -FilePath $installer.FullName -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden } Remove-Item $zipPath -Force -ErrorAction SilentlyContinue Remove-Item $extractPath -Recurse -Force -ErrorAction SilentlyContinue return @{Success=$true; Message=""} } $ext = if ($IsMsi) { ".msi" } else { ".exe" } $fileName = ($Name -replace '[^\w\-]', '_') + $ext $filePath = Join-Path $env:TEMP $fileName Invoke-WebRequest -Uri $Url -OutFile $filePath -UseBasicParsing -ErrorAction Stop Update-Status "Installing $Name..." "Cyan" if ($IsMsi) { $proc = Start-Process "msiexec.exe" -ArgumentList "/i `"$filePath`" $Arguments" -Wait -PassThru -WindowStyle Hidden } else { $proc = Start-Process -FilePath $filePath -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden } $exitCode = $proc.ExitCode Remove-Item $filePath -Force -ErrorAction SilentlyContinue if ($exitCode -in @(0, 1638, 3010, 1641, 5100)) { return @{Success=$true; Message=""} } return @{Success=$false; Message="Exit code: $exitCode"} } catch { return @{Success=$false; Message=$_.Exception.Message} } } $installBtn.Add_Click({ if ($script:installCompleted) { $result = [System.Windows.Forms.MessageBox]::Show("Are you sure you want to restart your computer?", "Confirm Reboot", "YesNo", "Question") if ($result -eq "Yes") { Restart-Computer -Force } return } $selected = @($script:allCheckboxes | Where-Object { $_.Checked }) if ($selected.Count -eq 0) { [System.Windows.Forms.MessageBox]::Show("Please select at least one item to install.", "Nothing Selected", "OK", "Warning"); return } $script:failures = @() $installBtn.Enabled = $false $selectAllBtn.Enabled = $false $selectNoneBtn.Enabled = $false $selectEssentialBtn.Enabled = $false $selectGamingBtn.Enabled = $false $script:allCheckboxes | ForEach-Object { $_.Enabled = $false } | Out-Null $progressBar.Value = 0 $progressBar.Maximum = $selected.Count $successCount = 0 $is64Bit = [Environment]::Is64BitOperatingSystem foreach ($cb in $selected) { $tag = $cb.Tag $itemSuccess = $false switch ($tag.Type) { "VC" { $x86 = $Redists.x86[$tag.Index] $x64 = $Redists.x64[$tag.Index] $result = Install-Package -Name "$($x86.Name) x86" -Url $x86.Url -Arguments $x86.Args if ($result.Success) { if ($is64Bit) { $null = Install-Package -Name "$($x64.Name) x64" -Url $x64.Url -Arguments $x64.Args }; $itemSuccess = $true } else { $script:failures += @{Name=$x86.Name; Error=$result.Message} } } "DotNet" { $item = $Redists.dotnet[$tag.Index] if ($item.Url) { $result = Install-Package -Name $item.Name -Url $item.Url -Arguments $item.Args; $itemSuccess = $result.Success; if (-not $result.Success) { $script:failures += @{Name=$item.Name; Error=$result.Message} } } elseif ($item.Url_x86 -and $item.Url_x64) { $result = Install-Package -Name "$($item.Name) x86" -Url $item.Url_x86 -Arguments $item.Args if ($result.Success) { if ($is64Bit) { $null = Install-Package -Name "$($item.Name) x64" -Url $item.Url_x64 -Arguments $item.Args }; $itemSuccess = $true } else { $script:failures += @{Name=$item.Name; Error=$result.Message} } } } "Extra" { $item = $Redists.extras[$tag.Index] if ($item.Url_x86 -and $item.Url_x64) { $url = if ($is64Bit) { $item.Url_x64 } else { $item.Url_x86 }; $result = Install-Package -Name $item.Name -Url $url -Arguments $item.Args } elseif ($item.IsDirectX) { $result = Install-Package -Name $item.Name -Url $item.Url -Arguments $item.Args -IsDirectX $true } elseif ($item.IsOpenAL) { $result = Install-Package -Name $item.Name -Url $item.Url -Arguments $item.Args -IsOpenAL $true } elseif ($item.IsMsi) { $result = Install-Package -Name $item.Name -Url $item.Url -Arguments $item.Args -IsMsi $true } else { $result = Install-Package -Name $item.Name -Url $item.Url -Arguments $item.Args } $itemSuccess = $result.Success if (-not $result.Success) { $script:failures += @{Name=$item.Name; Error=$result.Message} } } } if ($itemSuccess) { $successCount++ } $progressBar.Value++ [System.Windows.Forms.Application]::DoEvents() } $selectAllBtn.Enabled = $true $selectNoneBtn.Enabled = $true $selectEssentialBtn.Enabled = $true $selectGamingBtn.Enabled = $true $script:allCheckboxes | ForEach-Object { $_.Enabled = $true } | Out-Null $progressBar.Value = $progressBar.Maximum $failCount = $script:failures.Count if ($failCount -eq 0) { Update-Status "Complete! All $successCount packages installed successfully." "Green"; $message = "Installation complete!`n`nAll $successCount packages installed successfully.`n`nA reboot is recommended." } else { Update-Status "Complete with $failCount failure(s). See details." "Yellow"; $failList = ($script:failures | ForEach-Object { "- $($_.Name): $($_.Error)" }) -join "`n"; $message = "Installation complete!`n`nSuccessful: $successCount`nFailed: $failCount`n`nFailed packages:`n$failList`n`nA reboot is recommended." } [System.Windows.Forms.MessageBox]::Show($message, "Installation Complete", "OK", "Information") $script:installCompleted = $true $installBtn.Text = "Reboot System" $installBtn.BackColor = [System.Drawing.Color]::FromArgb(200, 80, 0) $installBtn.Enabled = $true }) $form.Add_FormClosing({ param($sender, $e) if ($script:installCompleted) { $result = [System.Windows.Forms.MessageBox]::Show("You have installed runtime packages but have not rebooted.`n`nIt is recommended to reboot your computer to complete the installation.`n`nDo you want to close anyway?", "Reboot Recommended", "YesNo", "Warning") if ($result -eq "No") { $e.Cancel = $true } } }) [void]$form.ShowDialog()