e119aa50e9
Today's manual driver recovery (wedged under ADD/REMOVE churn → ERROR_NOT_FOUND) and the manual
host-stop/install/host-start dance around drivers/deploy-dev.ps1 are now two scripts:
* reset-pf-vdisplay.ps1 — recover a wedged driver: stop host → pnputil /remove-device the ghost
"Generic Monitor (punktfunk)" nodes → Disable+Enable the adapter
(Restart-PnpDevice doesn't exist on the box PS) → start host. No reboot
(the box boots to Proxmox). -Verify probes to confirm ADD recovered.
* redeploy-pf-vdisplay.ps1 — one-shot dev redeploy wrapping deploy-dev.ps1 with the host stop/start
(the running host holds the driver DLL) + a post-install adapter reload
(pnputil updates the store but the live device keeps the old binary).
Both standalone (don't touch deploy-dev.ps1). README gains a "Dev iteration on the test box" section.
Co-Authored-By: Claude Opus 4.8 (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."
|