Files
Allen Houchins 7ba5d4cfcc Add Windows FMAs (letter F): 5 apps (#49218)
**Related issue:** N/A — part of the Windows Fleet-maintained apps
catalog expansion (letter F batch; follows #48872, #48881, #48950,
#48969, #49086, #49186).

Adds eight new Windows Fleet-maintained apps:

| App | winget package | Installer | Notes |
|-----|----------------|-----------|-------|
| Foxit PDF Editor | `Foxit.PhantomPDF` | WiX bootstrapper EXE, machine,
x64 | Covers both "Foxit PDF Editor" and "…Pro" from the inventory (one
package). Runs an updater service → process-stopping uninstall. ARP key
lives in the WOW6432Node hive. |
| Foxit PDF Reader | `Foxit.FoxitReader` | WiX bootstrapper EXE,
machine, x64 | Distinct DisplayName from the Editor (verified via
msiinfo). Updater service → process-stopping uninstall. |
| FreeCAD | `FreeCAD.FreeCAD` | NSIS (MultiUser), machine, x64 |
`/AllUsers /S`; versioned ARP name ("FreeCAD 1.1.1") → `FreeCAD%` fuzzy.
|
| FastPictureViewer Professional |
`AxelRietschin.FastPictureViewer.Professional` | MSI, machine, x64 |
Versioned ARP name → fuzzy. Unversioned URL → `ignore_hash`. Declares
VCRedist deps (near-ubiquitous; noted). |
| FastStone Capture | `FastStone.Capture` | NSIS, machine, x86 | `/S`;
versioned ARP name → fuzzy. Paid trialware, but silent
install/detect/uninstall are clean. |
| FastStone Image Viewer | `FastStone.Viewer` | NSIS, machine, x86 |
`/S`; versioned ARP name → fuzzy. |
| FlexWhere for Desktop | `Dutchview.Flexwhere` | MSI, machine, x64 |
Auto-start tray app → process-stopping uninstall (stop process, then
msiexec /x via UpgradeCode). |
| Fortify | `PeculiarVentures.Fortify` | WiX MSI, machine, x64 (en-US) |
Smart-card/cert bridge; auto-start tray → process-stopping uninstall.
Per-arch+locale ProductCode, so `installer_locale: en-US`. |

Considered but **not** added (recorded in the workstream tracker):
- **FactSet Workstation** (`FactSet.FactSetWorkstation`): MSI defaults
to per-user (ALLUSERS=2 + MSIINSTALLPERUSER=1) with no machine-scope
override, AND a `SpawnFDSWorkstation` custom action launches the app at
install (headless-hang risk in a SYSTEM session). Niche licensed
terminal.
- **Filius** (`StefanFreischlad.Filius`): winget manifest is de-DE only
(no en-US locale); the ingester hard-codes the en-US locale fetch (same
limitation that deferred Araxis Merge).
- **FlashFXP** (`OpenSight.FlashFXP`): abandoned (frozen at 2017), the
vendor site returns HTTP 500, only a 16×16 icon is available, and its
InstallAware uninstall needs a fragile cached-setup `/s` injection.
- **Front** (`FrontApp.Front`): per-user-only electron-builder installer
(`Front-user-*.exe` → `%LocalAppData%`, HKCU); no machine/all-users
artifact in winget.
- **Autodesk Fusion** (`Autodesk.Fusion`): the winget "installer" is
`Fusion Client Downloader.exe`, a per-user streaming/web bootstrapper
that downloads at runtime, hangs headless, and needs interactive
Autodesk sign-in.

Identities verified per app (msiinfo Property tables; NSIS header
decompilation; winget AppsAndFeaturesEntries; uninstall-database
corroboration). Apps that run a service or auto-start tray (both Foxit
products, FlexWhere, Fortify) get process-stopping uninstalls up front
to avoid the MSI-rollback failure class. SHAs verified against manifests
for pinned URLs; `ignore_hash` only for FastPictureViewer's
actively-maintained latest-pointer URL. Icons via
`tools/software/icons/generate-icons.sh` (all ≥256px except
FastPictureViewer/Fortify at 256/180).

# Checklist for submitter

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops

## Testing

- [ ] QA'd all new/changed functionality manually (relying on the FMA CI
validator for Windows install/uninstall validation)
2026-07-13 15:07:06 -05:00

65 lines
2.3 KiB
PowerShell

# Uninstalls Foxit PDF Reader.
#
# Foxit PDF Reader is a Foxit WiX MultiLangBootstrapper install that runs an auto-updater
# service; its visible ARP entry is the inner MSI (its UninstallString uses
# "MsiExec.exe /I{ProductCode}", the maintenance/repair form). We stop the
# Foxit services/processes first so files aren't locked, then resolve the
# ProductCode GUID and run a clean "msiexec /x {ProductCode} /qn /norestart"
# (never reuse the /I from the registry string). The ARP entry lives in the
# WOW6432Node (32-bit) hive even on x64.
$softwareName = "Foxit PDF Reader"
$machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
$machineKey32on64 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
$successCodes = @(0, 3010, 1641)
$exitCode = $null
try {
foreach ($svc in @("FoxitPDFReaderUpdateService")) {
Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
}
foreach ($p in @("FoxitPDFReader", "FoxitReader", "FoxitUpdater")) {
Stop-Process -Name $p -Force -ErrorAction SilentlyContinue
}
Start-Sleep -Seconds 3
[array]$uninstallKeys = Get-ChildItem `
-Path @($machineKey, $machineKey32on64) `
-ErrorAction SilentlyContinue |
ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue }
$selected = $uninstallKeys | Where-Object { $_.DisplayName -eq $softwareName } | Select-Object -First 1
if (-not $selected) {
Write-Host "Uninstall entry not found for '$softwareName'."
Exit 1
}
# ProductCode: prefer the ARP key name (it is the MSI ProductCode), else pull
# the first GUID out of the UninstallString. Never carry over its /I switch.
$productCode = $selected.PSChildName
if ($productCode -notmatch '^\{[0-9A-Fa-f-]+\}$') {
$raw = $selected.UninstallString
if ($raw -match '(\{[0-9A-Fa-f-]+\})') { $productCode = $matches[1] }
}
if ($productCode -notmatch '^\{[0-9A-Fa-f-]+\}$') {
Write-Host "Could not determine ProductCode for '$softwareName'."
Exit 1
}
Write-Host "Uninstalling product code: $productCode"
$process = Start-Process msiexec.exe `
-ArgumentList "/x $productCode /qn /norestart" `
-NoNewWindow -PassThru -Wait
$exitCode = $process.ExitCode
Write-Host "Uninstall exit code: $exitCode"
} catch {
Write-Host "Error: $_"
Exit 1
}
if ($successCodes -contains $exitCode) { Exit 0 }
Exit $exitCode