From bc3eee5f32c22df41ec719c3d2d1799ab80619b2 Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:21:55 -0500 Subject: [PATCH] Re-add Dell Display and Peripheral Manager Windows FMA, validate on client-OS runner (#50313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Related issue:** NA (Windows FMA workstream; follow-up to #49127, which dropped DDPM) Re-adds **Dell Display and Peripheral Manager** (`Dell.DisplayAndPeripheralManager` 2.2.2.8) as a Windows Fleet-maintained app, and adds a `requires_client_os` routing override so its CI validation always runs on the `windows-11-arm` runner. ## Why DDPM was dropped before, and why it's viable now DDPM was dropped from the earlier re-add because its InstallShield setup aborted with `0x80042000` under every documented silent switch, which was diagnosed at the time as a .NET-prerequisite/headless-chaining problem. A new debug run with Dell's own `/CreateDebugLog` switch shows the real cause: the setup evaluates the OS at `OFUIBefore` and terminates because the runner reports **Microsoft Windows Server 2025**. DDPM is a Windows 10/11 client application and refuses to install on Server SKUs — which is exactly what GitHub's x64 `windows-latest` image is. ``` OSetUMode() 0 AP:2.2.2.8 OFUIBefore Os Major10 Minor0 OS - 44444 // End Log File... ``` ## `requires_client_os` CI routing - New optional winget input field `requires_client_os: true` (documented in `ee/maintained-apps/README.md` and on the Go input struct; ignored by ingestion). - `.github/scripts/partition-fma-apps.sh` routes any app with this flag to `windows-11-arm` — the only GitHub-hosted client-OS Windows runner — regardless of `installer_arch`. The x64 installer runs there under Prism emulation; DDPM's gate is the OS SKU, not the architecture. - Verified locally: partitioning the full 421-app Windows catalog reroutes only `dell-display-and-peripheral-manager/windows`. ## App identity (verified against the real installer) - Downloaded `DDPM-Setup_2.2.2.8.exe` from `dl.dell.com` (Chrome UA per #49123); SHA256 matches the winget manifest. - Embedded InstallShield `[Application]` block: `Name=Dell Display and Peripheral Manager`, `Company=Dell Technologies`; ProductCode matches the manifest GUID. The setup log reports `AP:2.2.2.8` as the registering version. - Installs with Dell's documented managed-deployment switches `/Silent /HeadlessMode=true /TelemetryConsent=false /TurnOffCA` — the final pre-drop iteration (6d0f2c00af), which also declines telemetry and disables DDPM's self-updater on Fleet-managed hosts. Uninstalls via `msiexec /x` on the ProductCode looked up in the registry by DisplayName. Input/uninstall script/icon are restored from the pre-drop state; the install script is the final pre-drop iteration with its root-cause comment corrected (Server-SKU OS gate, not headless-SYSTEM chaining). Output regenerated (winget still at 2.2.2.8; script refs verified). # Checklist for submitter If some of the following don't apply, delete the relevant line. - [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 - [x] QA'd all new/changed functionality manually (partition script exercised locally over the full catalog and a mixed PR-style slug list; ingester regenerated with no output drift; `go test ./ee/maintained-apps/ingesters/winget/` passes) - [ ] `test-fma-windows-pr-only` validates DDPM on the `windows-11-arm` runner in this PR's CI ## Summary by CodeRabbit * **New Features** * Added Dell Display and Peripheral Manager to the Windows software catalog, including installation, uninstallation, detection, metadata, and an app icon. * Added support for routing applications that require a Windows client operating system to the appropriate Windows 11 ARM test environment. * **Documentation** * Documented Windows client operating system routing behavior and test environment architecture details. --- .github/scripts/partition-fma-apps.sh | 16 +++ .../workflows/test-fma-windows-pr-only.yml | 5 +- .github/workflows/test-fma-windows.yml | 5 +- ee/maintained-apps/README.md | 1 + .../ingesters/winget/ingester.go | 5 + .../dell-display-and-peripheral-manager.json | 14 ++ ...display-and-peripheral-manager_install.ps1 | 34 +++++ ...splay-and-peripheral-manager_uninstall.ps1 | 121 ++++++++++++++++++ ee/maintained-apps/outputs/apps.json | 7 + .../windows.json | 22 ++++ .../icons/DellDisplayAndPeripheralManager.tsx | 14 ++ .../SoftwarePage/components/icons/index.ts | 2 + ...isplay-and-peripheral-manager-60x60@2x.png | Bin 0 -> 11868 bytes 13 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 ee/maintained-apps/inputs/winget/dell-display-and-peripheral-manager.json create mode 100644 ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_install.ps1 create mode 100644 ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_uninstall.ps1 create mode 100644 ee/maintained-apps/outputs/dell-display-and-peripheral-manager/windows.json create mode 100644 frontend/pages/SoftwarePage/components/icons/DellDisplayAndPeripheralManager.tsx create mode 100644 website/assets/images/app-icon-dell-display-and-peripheral-manager-60x60@2x.png diff --git a/.github/scripts/partition-fma-apps.sh b/.github/scripts/partition-fma-apps.sh index c8f0cd9273..5696a89ff5 100755 --- a/.github/scripts/partition-fma-apps.sh +++ b/.github/scripts/partition-fma-apps.sh @@ -11,6 +11,13 @@ # ee/maintained-apps/inputs/winget/7-zip.json (.installer_arch). # Slugs whose input file or installer_arch is missing default to # the x64 runner. +# +# Exception: inputs with "requires_client_os": true always route +# to windows-11-arm regardless of installer_arch. The x64 runner +# image is Windows Server, and some installers (e.g. Dell Display +# and Peripheral Manager) refuse to install on Server SKUs; +# windows-11-arm is the only GitHub-hosted client-OS Windows +# runner, and it runs x64/x86 installers under Prism emulation. # darwin All apps run on macos-latest (arm64; x86-only casks run under # Rosetta 2, matching how customer Macs run them). No architecture # partitioning is needed. @@ -112,11 +119,20 @@ case "$PLATFORM" in name="${slug%/windows}" input_file="${WINGET_INPUTS_DIR}/${name}.json" arch="" + requires_client_os="false" if [ -f "$input_file" ]; then arch=$(jq -r '.installer_arch // empty' "$input_file" 2>/dev/null || echo "") + requires_client_os=$(jq -r '.requires_client_os // false' "$input_file" 2>/dev/null || echo "false") else echo "Warning: no winget input file for '$slug' at $input_file, assuming x64" >&2 fi + if [ "$requires_client_os" == "true" ]; then + # The app won't install on Windows Server (the x64 runner + # image), so validate it on the client-OS arm64 runner. + arm64_slugs+=("$slug") + echo " - $slug -> windows-11-arm (requires_client_os)" + continue + fi case "$arch" in arm64) arm64_slugs+=("$slug") diff --git a/.github/workflows/test-fma-windows-pr-only.yml b/.github/workflows/test-fma-windows-pr-only.yml index 7ffcc73169..b4666c7673 100644 --- a/.github/workflows/test-fma-windows-pr-only.yml +++ b/.github/workflows/test-fma-windows-pr-only.yml @@ -33,8 +33,9 @@ jobs: # cheap Linux runner. The (much more expensive) Windows runners below only # spin up when there are Windows apps to validate, and each app is routed to # a runner whose native architecture matches its installer: arm64 apps to - # windows-11-arm, x64/x86/neutral apps to the x64 runner. Large PRs are split - # into shards that validate in parallel. + # windows-11-arm, x64/x86/neutral apps to the x64 runner. Apps marked + # requires_client_os always go to windows-11-arm (the x64 runner is Windows + # Server). Large PRs are split into shards that validate in parallel. detect-changed-apps: runs-on: ubuntu-latest outputs: diff --git a/.github/workflows/test-fma-windows.yml b/.github/workflows/test-fma-windows.yml index dcd26ddaf3..fb36f30631 100644 --- a/.github/workflows/test-fma-windows.yml +++ b/.github/workflows/test-fma-windows.yml @@ -28,8 +28,9 @@ jobs: # Partition every Windows app in apps.json by installer architecture on a # cheap Linux runner, then fan out to Windows runners whose native # architecture matches each app's installer: arm64 apps to windows-11-arm, - # x64/x86/neutral apps to the x64 runner. Each architecture bucket is split - # into shards that validate in parallel. + # x64/x86/neutral apps to the x64 runner. Apps marked requires_client_os + # always go to windows-11-arm (the x64 runner is Windows Server). Each + # architecture bucket is split into shards that validate in parallel. partition: runs-on: ubuntu-latest outputs: diff --git a/ee/maintained-apps/README.md b/ee/maintained-apps/README.md index 6b4046d1f0..d7b933910a 100644 --- a/ee/maintained-apps/README.md +++ b/ee/maintained-apps/README.md @@ -124,6 +124,7 @@ go run cmd/maintained-apps/main.go --slug="box-drive/windows" --debug | `install_script_path` | string | Filepath to a custom install script (`.ps1`). Overrides the generated install script. Script must be placed in `inputs/winget/scripts/`. For `.msi` apps, the ingestor automatically generates install scripts. Do not add scripts unless you need to override the generated behavior. For `.exe` apps, you must provide PowerShell scripts that run the installer file directly. Fleet stores the installer and sends it to the host at install time; your script must execute it using the `INSTALLER_PATH` environment variable. | | `uninstall_script_path` | string | Filepath to a custom uninstall script (`.ps1`). Overrides the generated uninstall script. Script must be placed in `inputs/winget/scripts/`. For `.msi` apps, the ingestor automatically generates uninstall scripts. Do not add scripts unless you need to override the generated behavior. For `.exe` apps, you must provide a script to uninstall the app. Scripts for `.exe` apps are vendor-specific. Use the vendor’s documented silent uninstall switch or the registered UninstallString (if available), ensuring the script runs silently and returns the installer’s exit code. | | `fuzzy_match_name` | boolean | If the `unique_identifier` doesn't match the `DisplayName`, use `fuzzy_match_name` to specify that Fleet uses "fuzzy matching" to match the Fleet-maintained app and the inventoried software. For example, for Pritunl, the `unique_identifier` is "Pritunl" and the inventories software's `DisplayName` is "Pritunl Client". With `fuzzy_match_name` set to true, Pritunl app will be matched to the inventories software. | +| `requires_client_os` | boolean | Set to `true` when the installer refuses to run on Windows Server SKUs (e.g., Dell Display and Peripheral Manager). Fleet's ingestion ignores this field; CI reads it to route validation to the `windows-11-arm` runner (the only GitHub-hosted client-OS Windows runner) instead of the default Windows Server x64 runner. | #### Windows troubleshooting diff --git a/ee/maintained-apps/ingesters/winget/ingester.go b/ee/maintained-apps/ingesters/winget/ingester.go index b766a5821f..9c17f410ec 100644 --- a/ee/maintained-apps/ingesters/winget/ingester.go +++ b/ee/maintained-apps/ingesters/winget/ingester.go @@ -652,6 +652,11 @@ type inputApp struct { DefaultCategories []string `json:"default_categories"` Frozen bool `json:"frozen"` PatchPolicyPath string `json:"patch_policy_path"` + // RequiresClientOS marks installers that refuse to run on Windows Server + // SKUs (e.g. Dell Display and Peripheral Manager). The ingester ignores it; + // CI (.github/scripts/partition-fma-apps.sh) reads it to route validation to + // the windows-11-arm runner, the only GitHub-hosted client-OS Windows runner. + RequiresClientOS bool `json:"requires_client_os"` } type installerManifest struct { diff --git a/ee/maintained-apps/inputs/winget/dell-display-and-peripheral-manager.json b/ee/maintained-apps/inputs/winget/dell-display-and-peripheral-manager.json new file mode 100644 index 0000000000..5c944a334e --- /dev/null +++ b/ee/maintained-apps/inputs/winget/dell-display-and-peripheral-manager.json @@ -0,0 +1,14 @@ +{ + "name": "Dell Display and Peripheral Manager", + "slug": "dell-display-and-peripheral-manager/windows", + "package_identifier": "Dell.DisplayAndPeripheralManager", + "unique_identifier": "Dell Display and Peripheral Manager", + "program_publisher": "Dell Technologies", + "installer_arch": "x64", + "installer_type": "exe", + "installer_scope": "machine", + "requires_client_os": true, + "install_script_path": "ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_install.ps1", + "uninstall_script_path": "ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_uninstall.ps1", + "default_categories": ["Productivity"] +} diff --git a/ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_install.ps1 b/ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_install.ps1 new file mode 100644 index 0000000000..03a1f9e30f --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_install.ps1 @@ -0,0 +1,34 @@ +# Learn more about .exe install scripts: +# http://fleetdm.com/learn-more-about/exe-install-scripts + +$exeFilePath = "${env:INSTALLER_PATH}" + +try { + +# DDPM is a Windows 10/11 client app: the InstallShield setup's OS check aborts +# with exit 0x80042000 on Windows Server SKUs (per its /CreateDebugLog output), +# so it only installs on client editions. Dell's documented managed-deployment +# switches: +# /Silent - no UI +# /HeadlessMode=true - required for SYSTEM/session-0 (no desktop) installs +# /TelemetryConsent=false - decline telemetry (no consent prompt) +# /TurnOffCA - disable the app's own auto-update (Fleet manages updates) +$processOptions = @{ + FilePath = "$exeFilePath" + ArgumentList = "/Silent /HeadlessMode=true /TelemetryConsent=false /TurnOffCA" + PassThru = $true + Wait = $true +} + +# Start process and track exit code +$process = Start-Process @processOptions +$exitCode = $process.ExitCode + +# Prints the exit code +Write-Host "Install exit code: $exitCode" +Exit $exitCode + +} catch { + Write-Host "Error: $_" + Exit 1 +} diff --git a/ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_uninstall.ps1 b/ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_uninstall.ps1 new file mode 100644 index 0000000000..88ea7ad179 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/dell-display-and-peripheral-manager_uninstall.ps1 @@ -0,0 +1,121 @@ +# Uninstalls Dell Display and Peripheral Manager. DDPM is an InstallShield +# InstallScript (non-MSI) product: its uninstall registry key is named like an +# MSI ProductCode GUID, but no MSI product is registered, so msiexec /x fails +# with 1605. Its UninstallString ("\Installer\setup.exe +# -runfromtemp -removeonly") hangs under Dell's /Silent wrapper switch because +# in -removeonly mode the InstallScript engine drives the dialogs and only +# honors its own /s silent switch with a response file. The installer ships +# exactly this removal response file embedded in its overlay (SdWelcomeMaint +# Result=303 / MessageBox Result=6 / SdFinishReboot Result=1); we recreate it +# with the GUID from the registry key and pass it via /f1. Success is gated on +# the uninstall registry entry disappearing, not the launcher exit code, since +# -runfromtemp relaunches from %TEMP% and can return early. + +$softwareName = "Dell Display and Peripheral Manager" + +$machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' +$machineKey32on64 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' + +function Get-InstalledEntry { + Get-ChildItem -Path @($machineKey, $machineKey32on64) -ErrorAction SilentlyContinue | + ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } | + Where-Object { $_.DisplayName -eq $softwareName } | + Select-Object -First 1 +} + +try { + +# DDPM can auto-launch after install; a running instance can block the +# uninstaller. +Get-Process -Name "DDPM*" -ErrorAction SilentlyContinue | + Stop-Process -Force -ErrorAction SilentlyContinue + +$key = Get-InstalledEntry +if (-not $key) { + Write-Host "Uninstall entry not found for '$softwareName'." + Exit 1 +} + +$u = $key.UninstallString +if (-not $u) { + Write-Host "No UninstallString registered for '$softwareName'." + Exit 1 +} + +# Parse defensively: quoted path, unquoted path with spaces, bare token. +if ($u -match '^\s*"([^"]+)"\s*(.*)$') { + $exe = $Matches[1]; $uninstallArgs = $Matches[2] +} elseif ($u -match '(?i)^\s*(.+?\.exe)\s*(.*)$') { + $exe = $Matches[1]; $uninstallArgs = $Matches[2] +} else { + Write-Host "Unrecognized UninstallString format: $u" + Exit 1 +} + +if ($exe -match '(?i)msiexec') { + # Defensive: if a future release registers an MSI-style uninstall. + $uninstallArgs = "$uninstallArgs /qn /norestart".Trim() + $process = Start-Process -FilePath $exe -ArgumentList $uninstallArgs ` + -NoNewWindow -PassThru -Wait + Write-Host "msiexec exit code: $($process.ExitCode)" +} else { + # The registry key name is the InstallScript product GUID; the response + # file's section names must use it. + $guid = $key.PSChildName + $issPath = Join-Path $env:TEMP "ddpm-uninstall.iss" + $logPath = Join-Path $env:TEMP "ddpm-uninstall.log" + Remove-Item -Path $logPath -Force -ErrorAction SilentlyContinue + @" +[InstallShield Silent] +Version=v7.00 +File=Response File +[File Transfer] +OverwrittenReadOnly=NoToAll +[$guid-DlgOrder] +Dlg0=$guid-SdWelcomeMaint-0 +Count=3 +Dlg1=$guid-MessageBox-0 +Dlg2=$guid-SdFinishReboot-0 +[$guid-SdWelcomeMaint-0] +Result=303 +[$guid-MessageBox-0] +Result=6 +[$guid-SdFinishReboot-0] +Result=1 +BootOption=0 +"@ | Set-Content -Path $issPath -Encoding ASCII + + $uninstallArgs = "$uninstallArgs /s /f1`"$issPath`" /f2`"$logPath`"".Trim() + Write-Host "Uninstalling via: `"$exe`" $uninstallArgs" + $process = Start-Process -FilePath $exe -ArgumentList $uninstallArgs -PassThru + if (-not $process.WaitForExit(180000)) { + Write-Host "Uninstaller still running after 180s; killing it." + Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue + } else { + Write-Host "Launcher exit code: $($process.ExitCode)" + } + if (Test-Path $logPath) { + Write-Host "--- InstallShield uninstall log ---" + Get-Content $logPath | Write-Host + Write-Host "-----------------------------------" + } +} + +# The launcher can return before the %TEMP% copy finishes removal; poll the +# registry entry to decide success. +$deadline = (Get-Date).AddSeconds(120) +while ((Get-Date) -lt $deadline) { + if (-not (Get-InstalledEntry)) { + Write-Host "'$softwareName' uninstalled." + Exit 0 + } + Start-Sleep -Seconds 5 +} + +Write-Host "'$softwareName' is still registered after uninstall." +Exit 1 + +} catch { + Write-Host "Error: $_" + Exit 1 +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index b61026ff88..610fa62cd9 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -2423,6 +2423,13 @@ "unique_identifier": "Dell Command", "description": "Dell Command Update is a standalone application that delivers BIOS, firmware, driver, and application updates for Dell client hardware." }, + { + "name": "Dell Display and Peripheral Manager", + "slug": "dell-display-and-peripheral-manager/windows", + "platform": "windows", + "unique_identifier": "Dell Display and Peripheral Manager", + "description": "Dell Display and Peripheral Manager sets up and configures Dell monitors and peripherals such as keyboards, mice, webcams, audio devices, and active pens." + }, { "name": "Descript", "slug": "descript/darwin", diff --git a/ee/maintained-apps/outputs/dell-display-and-peripheral-manager/windows.json b/ee/maintained-apps/outputs/dell-display-and-peripheral-manager/windows.json new file mode 100644 index 0000000000..cc303c8d99 --- /dev/null +++ b/ee/maintained-apps/outputs/dell-display-and-peripheral-manager/windows.json @@ -0,0 +1,22 @@ +{ + "versions": [ + { + "version": "2.2.2.8", + "queries": { + "exists": "SELECT 1 FROM programs WHERE name = 'Dell Display and Peripheral Manager' AND publisher = 'Dell Technologies';", + "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = 'Dell Display and Peripheral Manager' AND publisher = 'Dell Technologies' AND version_compare(version, '2.2.2.8') < 0);" + }, + "installer_url": "https://dl.dell.com/FOLDER14474164M/1/DDPM-Setup_2.2.2.8.exe", + "install_script_ref": "56cc0c50", + "uninstall_script_ref": "575cb5c4", + "sha256": "d8ef308817fd729bdc9ec7ecaa0fc125fad9eed7f6ed35f4c9e282570cfca56e", + "default_categories": [ + "Productivity" + ] + } + ], + "refs": { + "56cc0c50": "# Learn more about .exe install scripts:\n# http://fleetdm.com/learn-more-about/exe-install-scripts\n\n$exeFilePath = \"${env:INSTALLER_PATH}\"\n\ntry {\n\n# DDPM is a Windows 10/11 client app: the InstallShield setup's OS check aborts\n# with exit 0x80042000 on Windows Server SKUs (per its /CreateDebugLog output),\n# so it only installs on client editions. Dell's documented managed-deployment\n# switches:\n# /Silent - no UI\n# /HeadlessMode=true - required for SYSTEM/session-0 (no desktop) installs\n# /TelemetryConsent=false - decline telemetry (no consent prompt)\n# /TurnOffCA - disable the app's own auto-update (Fleet manages updates)\n$processOptions = @{\n FilePath = \"$exeFilePath\"\n ArgumentList = \"/Silent /HeadlessMode=true /TelemetryConsent=false /TurnOffCA\"\n PassThru = $true\n Wait = $true\n}\n\n# Start process and track exit code\n$process = Start-Process @processOptions\n$exitCode = $process.ExitCode\n\n# Prints the exit code\nWrite-Host \"Install exit code: $exitCode\"\nExit $exitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n", + "575cb5c4": "# Uninstalls Dell Display and Peripheral Manager. DDPM is an InstallShield\n# InstallScript (non-MSI) product: its uninstall registry key is named like an\n# MSI ProductCode GUID, but no MSI product is registered, so msiexec /x fails\n# with 1605. Its UninstallString (\"\\Installer\\setup.exe\n# -runfromtemp -removeonly\") hangs under Dell's /Silent wrapper switch because\n# in -removeonly mode the InstallScript engine drives the dialogs and only\n# honors its own /s silent switch with a response file. The installer ships\n# exactly this removal response file embedded in its overlay (SdWelcomeMaint\n# Result=303 / MessageBox Result=6 / SdFinishReboot Result=1); we recreate it\n# with the GUID from the registry key and pass it via /f1. Success is gated on\n# the uninstall registry entry disappearing, not the launcher exit code, since\n# -runfromtemp relaunches from %TEMP% and can return early.\n\n$softwareName = \"Dell Display and Peripheral Manager\"\n\n$machineKey = 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*'\n$machineKey32on64 = 'HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*'\n\nfunction Get-InstalledEntry {\n Get-ChildItem -Path @($machineKey, $machineKey32on64) -ErrorAction SilentlyContinue |\n ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } |\n Where-Object { $_.DisplayName -eq $softwareName } |\n Select-Object -First 1\n}\n\ntry {\n\n# DDPM can auto-launch after install; a running instance can block the\n# uninstaller.\nGet-Process -Name \"DDPM*\" -ErrorAction SilentlyContinue |\n Stop-Process -Force -ErrorAction SilentlyContinue\n\n$key = Get-InstalledEntry\nif (-not $key) {\n Write-Host \"Uninstall entry not found for '$softwareName'.\"\n Exit 1\n}\n\n$u = $key.UninstallString\nif (-not $u) {\n Write-Host \"No UninstallString registered for '$softwareName'.\"\n Exit 1\n}\n\n# Parse defensively: quoted path, unquoted path with spaces, bare token.\nif ($u -match '^\\s*\"([^\"]+)\"\\s*(.*)$') {\n $exe = $Matches[1]; $uninstallArgs = $Matches[2]\n} elseif ($u -match '(?i)^\\s*(.+?\\.exe)\\s*(.*)$') {\n $exe = $Matches[1]; $uninstallArgs = $Matches[2]\n} else {\n Write-Host \"Unrecognized UninstallString format: $u\"\n Exit 1\n}\n\nif ($exe -match '(?i)msiexec') {\n # Defensive: if a future release registers an MSI-style uninstall.\n $uninstallArgs = \"$uninstallArgs /qn /norestart\".Trim()\n $process = Start-Process -FilePath $exe -ArgumentList $uninstallArgs `\n -NoNewWindow -PassThru -Wait\n Write-Host \"msiexec exit code: $($process.ExitCode)\"\n} else {\n # The registry key name is the InstallScript product GUID; the response\n # file's section names must use it.\n $guid = $key.PSChildName\n $issPath = Join-Path $env:TEMP \"ddpm-uninstall.iss\"\n $logPath = Join-Path $env:TEMP \"ddpm-uninstall.log\"\n Remove-Item -Path $logPath -Force -ErrorAction SilentlyContinue\n @\"\n[InstallShield Silent]\nVersion=v7.00\nFile=Response File\n[File Transfer]\nOverwrittenReadOnly=NoToAll\n[$guid-DlgOrder]\nDlg0=$guid-SdWelcomeMaint-0\nCount=3\nDlg1=$guid-MessageBox-0\nDlg2=$guid-SdFinishReboot-0\n[$guid-SdWelcomeMaint-0]\nResult=303\n[$guid-MessageBox-0]\nResult=6\n[$guid-SdFinishReboot-0]\nResult=1\nBootOption=0\n\"@ | Set-Content -Path $issPath -Encoding ASCII\n\n $uninstallArgs = \"$uninstallArgs /s /f1`\"$issPath`\" /f2`\"$logPath`\"\".Trim()\n Write-Host \"Uninstalling via: `\"$exe`\" $uninstallArgs\"\n $process = Start-Process -FilePath $exe -ArgumentList $uninstallArgs -PassThru\n if (-not $process.WaitForExit(180000)) {\n Write-Host \"Uninstaller still running after 180s; killing it.\"\n Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue\n } else {\n Write-Host \"Launcher exit code: $($process.ExitCode)\"\n }\n if (Test-Path $logPath) {\n Write-Host \"--- InstallShield uninstall log ---\"\n Get-Content $logPath | Write-Host\n Write-Host \"-----------------------------------\"\n }\n}\n\n# The launcher can return before the %TEMP% copy finishes removal; poll the\n# registry entry to decide success.\n$deadline = (Get-Date).AddSeconds(120)\nwhile ((Get-Date) -lt $deadline) {\n if (-not (Get-InstalledEntry)) {\n Write-Host \"'$softwareName' uninstalled.\"\n Exit 0\n }\n Start-Sleep -Seconds 5\n}\n\nWrite-Host \"'$softwareName' is still registered after uninstall.\"\nExit 1\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/DellDisplayAndPeripheralManager.tsx b/frontend/pages/SoftwarePage/components/icons/DellDisplayAndPeripheralManager.tsx new file mode 100644 index 0000000000..a22f6ca0bd --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/DellDisplayAndPeripheralManager.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const DellDisplayAndPeripheralManager = (props: SVGProps) => ( + + + +); +export default DellDisplayAndPeripheralManager; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index df398f60b4..d464131566 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -275,6 +275,7 @@ import Deezer from "./Deezer"; import DefaultFolderX from "./DefaultFolderX"; import DelineaConnectionManager from "./DelineaConnectionManager"; import DellCommandUpdate from "./DellCommandUpdate"; +import DellDisplayAndPeripheralManager from "./DellDisplayAndPeripheralManager"; import Descript from "./Descript"; import Deskpad from "./Deskpad"; import Desktime from "./Desktime"; @@ -1434,6 +1435,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { "default folder x": DefaultFolderX, "delinea connection manager": DelineaConnectionManager, "dell command update": DellCommandUpdate, + "dell display and peripheral manager": DellDisplayAndPeripheralManager, descript: Descript, deskpad: Deskpad, desktime: Desktime, diff --git a/website/assets/images/app-icon-dell-display-and-peripheral-manager-60x60@2x.png b/website/assets/images/app-icon-dell-display-and-peripheral-manager-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e0bdb96ce14081f876a4b6cd7c9275d78c9f5cea GIT binary patch literal 11868 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