deb / build-publish-client-arm64 (push) Failing after 4s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 13s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 11s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
ci / web (push) Successful in 1m53s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 13s
ci / docs-site (push) Successful in 2m3s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 15s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m24s
windows-drivers / driver-build (push) Successful in 2m30s
docker / builders-arm64cross (push) Successful in 6s
docker / deploy-docs (push) Successful in 29s
android / android (push) Successful in 5m7s
apple / swift (push) Canceled after 4m50s
apple / screenshots (push) Canceled after 0s
deb / build-publish (push) Successful in 4m42s
arch / build-publish (push) Canceled after 5m25s
ci / rust (push) Canceled after 5m26s
ci / rust-arm64 (push) Canceled after 5m31s
deb / build-publish-host (push) Canceled after 5m19s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 3m30s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 3m26s
windows-host / package (push) Canceled after 2m29s
windows-host / winget-source (push) Canceled after 0s
windows-drivers / probe-and-proto (push) Successful in 37s
Device Manager, the firewall list and the monitor name all said "punktfunk". The brand
is Punktfunk.
Renamed: the [Strings] blocks of all four driver INFs (device descriptions, install
disks, provider, manufacturer), the `description` on every SwDeviceProfile the host
creates, pf-mouse's HID manufacturer + product strings, pf-vdisplay's IddCx endpoint
friendly + manufacturer names, the EDID 0xFC display-name descriptor — so Windows now
shows `Generic Monitor (Punktfunk)` — and the netsh firewall rule names.
The EDID edit is a single byte (0x70 -> 0x50) and needs no hand-patched checksum:
Edid::generate_with already recomputes both block checksums after patching the serial.
Deliberately left lowercase, because these are IDENTITIES rather than display names and
renaming them would orphan installed state:
* the SwDeviceCreate enumerator `w!("punktfunk")` — it IS the SWD\PUNKTFUNK\... path
every pad instance id is built from
* pf-paths' `join("punktfunk")` — C:\ProgramData\punktfunk
* the CN=punktfunk-driver cert subject, which purge_driver_certs and both driver build
scripts match by string
* install.rs' `lo.contains("punktfunk virtual display")` probes, whose haystack is
to_ascii_lowercase()d, so they already match the capitalised name
Nothing is orphaned by the renames that DID happen either: netsh rule names,
Get-NetFirewallRule -DisplayName and PowerShell's -match are all case-insensitive, so
the firewall delete paths and reset-pf-vdisplay.ps1's -AdapterName / -GhostMatch
defaults still reap what every release up to 0.22.1 created.
Cosmetic, with two consequences worth knowing: it takes a driver rebuild + re-sign to
appear at all, and an existing devnode keeps its cached FriendlyName until it is
recreated.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
131 lines
5.9 KiB
PowerShell
131 lines
5.9 KiB
PowerShell
#requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS
|
|
Recover the pf-vdisplay (punktfunk) virtual-display driver after it WEDGES under rapid ADD/REMOVE
|
|
churn - no reboot. The dev-iteration counterpart to redeploy-pf-vdisplay.ps1.
|
|
|
|
.DESCRIPTION
|
|
Sustained connect/disconnect churn (e.g. a client reconnect loop x the host's 8 pipeline-build
|
|
retries - ~100 ADD/REMOVE cycles) exhausts the driver's IddCx monitor slots: the per-monitor
|
|
target_ids climb, ghost "Generic Monitor (Punktfunk)" device nodes pile up, and eventually
|
|
IOCTL_ADD returns 0x80070490 ERROR_NOT_FOUND ("Element nicht gefunden"). Every session then fails
|
|
to create a virtual output -> the client gets a hard blackscreen. A host-service restart's
|
|
IOCTL_CLEAR_ALL does NOT recover it; the driver instance itself must be reloaded.
|
|
|
|
Steps (run ELEVATED):
|
|
1. Stop the host service (it holds the driver's control device).
|
|
2. pnputil /remove-device the GHOST (Status != OK = not-present) punktfunk virtual-monitor nodes
|
|
that accumulated - the root of the slot exhaustion.
|
|
3. Disable + Enable the pf-vdisplay adapter (ROOT\DISPLAY\*, "Punktfunk Virtual Display") to
|
|
reload the IddCx driver instance and reset its monitor list. (Restart-PnpDevice does NOT exist
|
|
on this box's PowerShell, so we disable+enable explicitly.)
|
|
4. Restart the host service.
|
|
Avoids a reboot on purpose (this box boots to Proxmox).
|
|
|
|
.PARAMETER Service Host service name. Default PunktfunkHost.
|
|
.PARAMETER AdapterName FriendlyName substring of the IddCx adapter to cycle. Default "Punktfunk
|
|
Virtual Display" (NOT SudoVDA's "SudoMaker Virtual Display Adapter").
|
|
.PARAMETER GhostMatch FriendlyName substring of the virtual monitors to reap. Default "Punktfunk".
|
|
.PARAMETER KeepGhosts Skip the ghost-node cleanup; only cycle the adapter.
|
|
.PARAMETER NoHost Don't stop/start the host service (just reset the driver) - used by
|
|
redeploy-pf-vdisplay.ps1, which manages the service itself.
|
|
.PARAMETER Verify After recovery, run a punktfunk-probe loopback and report whether ADD works
|
|
again (best-effort; needs punktfunk-probe.exe on PATH or via -Probe).
|
|
.PARAMETER Probe Path to punktfunk-probe.exe for -Verify.
|
|
|
|
.EXAMPLE
|
|
powershell -ExecutionPolicy Bypass -File reset-pf-vdisplay.ps1
|
|
.EXAMPLE
|
|
powershell -ExecutionPolicy Bypass -File reset-pf-vdisplay.ps1 -Verify -Probe C:\t-goal1\debug\punktfunk-probe.exe
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$Service = 'PunktfunkHost',
|
|
[string]$AdapterName = 'Punktfunk Virtual Display',
|
|
[string]$GhostMatch = 'Punktfunk',
|
|
[switch]$KeepGhosts,
|
|
[switch]$NoHost,
|
|
[switch]$Verify,
|
|
[string]$Probe
|
|
)
|
|
$ErrorActionPreference = 'Continue'
|
|
|
|
function Get-PfAdapter {
|
|
Get-PnpDevice -Class Display -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.FriendlyName -match $AdapterName } | Select-Object -First 1
|
|
}
|
|
|
|
# 1) Stop the host so it isn't mid-IOCTL during the reset (it holds the control device).
|
|
$svc = Get-Service $Service -ErrorAction SilentlyContinue
|
|
$hostWasRunning = $svc -and $svc.Status -eq 'Running'
|
|
if (-not $NoHost -and $hostWasRunning) {
|
|
Write-Host "==> stopping $Service"
|
|
Stop-Service $Service -Force
|
|
Start-Sleep -Seconds 2
|
|
}
|
|
|
|
# 2) Reap the ghost (not-present) punktfunk virtual-monitor device nodes.
|
|
if (-not $KeepGhosts) {
|
|
$ghosts = Get-PnpDevice -Class Monitor -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.Status -ne 'OK' -and $_.FriendlyName -match $GhostMatch }
|
|
Write-Host "==> removing $($ghosts.Count) ghost virtual-monitor node(s)"
|
|
$removed = 0
|
|
foreach ($g in $ghosts) {
|
|
pnputil /remove-device $g.InstanceId *> $null
|
|
if ($LASTEXITCODE -eq 0) { $removed++ }
|
|
}
|
|
Write-Host " removed $removed"
|
|
}
|
|
|
|
# 3) Reload the IddCx adapter instance (disable + enable) to clear its monitor list.
|
|
$ad = Get-PfAdapter
|
|
if (-not $ad) {
|
|
Write-Warning "pf-vdisplay adapter '$AdapterName' not found (Class Display) - is the driver installed?"
|
|
}
|
|
else {
|
|
Write-Host "==> cycling adapter $($ad.InstanceId)"
|
|
Disable-PnpDevice -InstanceId $ad.InstanceId -Confirm:$false -ErrorAction Continue
|
|
Start-Sleep -Seconds 3
|
|
Enable-PnpDevice -InstanceId $ad.InstanceId -Confirm:$false -ErrorAction Continue
|
|
Start-Sleep -Seconds 3
|
|
$st = (Get-PnpDevice -InstanceId $ad.InstanceId -ErrorAction SilentlyContinue).Status
|
|
if ($st -ne 'OK') {
|
|
# One retry - a disabled root device occasionally needs a second enable to come back OK.
|
|
Enable-PnpDevice -InstanceId $ad.InstanceId -Confirm:$false -ErrorAction Continue
|
|
Start-Sleep -Seconds 2
|
|
$st = (Get-PnpDevice -InstanceId $ad.InstanceId -ErrorAction SilentlyContinue).Status
|
|
}
|
|
Write-Host " adapter status: $st"
|
|
}
|
|
|
|
# 4) Restart the host.
|
|
if (-not $NoHost -and $svc) {
|
|
Write-Host "==> starting $Service"
|
|
Start-Service $Service
|
|
Start-Sleep -Seconds 3
|
|
Write-Host " $Service status: $((Get-Service $Service -ErrorAction SilentlyContinue).Status)"
|
|
}
|
|
|
|
# 5) Optional: probe to confirm ADD recovers.
|
|
if ($Verify) {
|
|
if (-not $Probe) {
|
|
$Probe = (Get-Command punktfunk-probe.exe -ErrorAction SilentlyContinue).Source
|
|
}
|
|
if (-not $Probe -or -not (Test-Path $Probe)) {
|
|
Write-Warning "-Verify: punktfunk-probe.exe not found (pass -Probe <path>); skipping verification."
|
|
}
|
|
else {
|
|
$log = Join-Path $env:ProgramData 'punktfunk\logs\host.log'
|
|
Write-Host "==> verifying with $Probe"
|
|
& $Probe *> $null
|
|
Start-Sleep -Seconds 2
|
|
$last = Get-Content $log -Tail 80 -ErrorAction SilentlyContinue |
|
|
Select-String -Pattern 'pf-vdisplay created|Element nicht|0x80070490' | Select-Object -Last 1
|
|
if ($last -match 'created') { Write-Host " OK: ADD succeeded after reset." }
|
|
elseif ($last) { Write-Warning " ADD still failing after reset: $($last.Line.Trim())" }
|
|
else { Write-Warning " no ADD outcome found in the log; check $log." }
|
|
}
|
|
}
|
|
|
|
Write-Host "pf-vdisplay reset done."
|