diff --git a/src-tauri/src/winget.rs b/src-tauri/src/winget.rs index db4731b..e305897 100644 --- a/src-tauri/src/winget.rs +++ b/src-tauri/src/winget.rs @@ -28,6 +28,7 @@ struct WingetPackage { pub name: String, pub installed_version: Option, pub available_versions: Option>, + pub icon_url: Option, } pub fn ensure_winget_dependencies(handle: &AppHandle) -> Result<(), String> { @@ -133,15 +134,84 @@ pub fn list_updates(handle: &AppHandle) -> Vec { let script = r#" $OutputEncoding = [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $ErrorActionPreference = 'SilentlyContinue' + Add-Type -AssemblyName System.Drawing Import-Module Microsoft.WinGet.Client -ErrorAction SilentlyContinue + $pkgs = Get-WinGetPackage -ErrorAction SilentlyContinue | Where-Object { $_.IsUpdateAvailable } + if ($pkgs) { - $pkgs | ForEach-Object { + $wshShell = New-Object -ComObject WScript.Shell + + # 预加载开始菜单快捷方式 + $startMenuPaths = @( + "$env:ProgramData\Microsoft\Windows\Start Menu\Programs", + "$env:AppData\Microsoft\Windows\Start Menu\Programs" + ) + $lnkFiles = Get-ChildItem -Path $startMenuPaths -Filter "*.lnk" -Recurse -File + + # 预加载注册表项 + $registryPaths = @( + "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", + "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*", + "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" + ) + $regItems = Get-ItemProperty $registryPaths + + $pkgs | ForEach-Object { + $p = $_ + $iconUrl = $null + $foundPath = "" + + # 策略 1: 寻找并解析开始菜单快捷方式 (去除箭头关键点) + $matchedLnk = $lnkFiles | Where-Object { $_.BaseName -eq $p.Name -or $p.Name -like "*$($_.BaseName)*" } | Select-Object -First 1 + if ($matchedLnk) { + try { + # 解析快捷方式指向的真实目标 + $target = $wshShell.CreateShortcut($matchedLnk.FullName).TargetPath + if ($target -and (Test-Path $target) -and $target.EndsWith(".exe")) { + $foundPath = $target + } else { + # 如果目标不可读或是 UWP 快捷方式,仍使用快捷方式文件提取 + $foundPath = $matchedLnk.FullName + } + } catch { + $foundPath = $matchedLnk.FullName + } + } + + # 策略 2: 注册表 DisplayIcon + if (-not $foundPath) { + $matchedReg = $regItems | Where-Object { $_.DisplayName -eq $p.Name -or $_.PSChildName -eq $p.Id } | Select-Object -First 1 + if ($matchedReg.DisplayIcon) { + $foundPath = $matchedReg.DisplayIcon.Split(',')[0].Trim('"') + } elseif ($matchedReg.InstallLocation) { + $loc = $matchedReg.InstallLocation.Trim('"') + if (Test-Path $loc) { + $exe = Get-ChildItem -Path $loc -Filter "*.exe" -File | Select-Object -First 1 + if ($exe) { $foundPath = $exe.FullName } + } + } + } + + # 提取并转 Base64 + if ($foundPath -and (Test-Path $foundPath)) { + try { + $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($foundPath) + $bitmap = $icon.ToBitmap() + $ms = New-Object System.IO.MemoryStream + $bitmap.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png) + $base64 = [Convert]::ToBase64String($ms.ToArray()) + $iconUrl = "data:image/png;base64,$base64" + $ms.Dispose(); $bitmap.Dispose(); $icon.Dispose() + } catch {} + } + [PSCustomObject]@{ - Name = [string]$_.Name; - Id = [string]$_.Id; - InstalledVersion = [string]$_.InstalledVersion; - AvailableVersions = $_.AvailableVersions + Name = [string]$p.Name; + Id = [string]$p.Id; + InstalledVersion = [string]$p.InstalledVersion; + AvailableVersions = $p.AvailableVersions; + IconUrl = $iconUrl } } | ConvertTo-Json -Compress } else { @@ -203,7 +273,7 @@ fn map_package(p: WingetPackage) -> Software { description: None, version: p.installed_version, available_version: p.available_versions.and_then(|v| v.first().cloned()), - icon_url: None, + icon_url: p.icon_url, status: "idle".to_string(), progress: 0.0, }