# Set-EventSaverScreensaver.ps1 # # Enable the EventSaver shopfloor screensaver for EVERY user on the box. Runs # under GE-Enforce (SYSTEM) every cycle. # # WHY THIS WAS REWRITTEN (2026-08-06) # The previous version wrote the timeout to exactly two places: HKU\.DEFAULT, # which only seeds profiles created AFTERWARDS, and the hive of one profile # whose folder had to be named literally 'Shopfloor'. On any PC where the # operator signs in as anything else, no HKCU was ever touched - the screensaver # then ran on whatever the profile already carried (a domain default, commonly # 120 seconds), while GE-Enforce reported success every cycle because from its # point of view it had done its job. # # That is what the "screensaver after 2 minutes instead of 9" reports were: the # manifest said 480 and the machines had never been told. # # Now: seed .DEFAULT for future profiles, then apply to EVERY loaded user hive. # Same approach Set-DisplayAlwaysOn.ps1 already uses for the kiosks. A user who # signs in between cycles is picked up on the next one. # # IDEMPOTENT + SILENT: hives already correct are skipped, so the common path # writes nothing. [CmdletBinding()] param( [string]$ScrPath = 'C:\Windows\System32\EventSaver.scr', [int] $TimeoutSeconds = 540, # Retired. Kept so an older manifest passing -TargetUser does not fail to # bind; it is deliberately ignored - targeting one named account is the bug # this rewrite removes. [string]$TargetUser = '', [string]$TaskName = 'EventSaver-Enable' ) $ErrorActionPreference = 'Continue' $logDir = 'C:\Logs\Shopfloor' if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } $log = Join-Path $logDir 'eventsaver.log' function Write-Log($m) { Add-Content -LiteralPath $log -Value ("{0} {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $m) } function Get-Val($deskKey, $name) { return (Get-ItemProperty -Path $deskKey -Name $name -ErrorAction SilentlyContinue).$name } # Already what we want? Idempotency gate - keeps the common path silent. function Saver-IsSet($deskKey) { if (-not (Test-Path $deskKey)) { return $false } if ((Get-Val $deskKey 'ScreenSaveActive') -ne '1') { return $false } if ((Get-Val $deskKey 'SCRNSAVE.EXE') -ne $ScrPath) { return $false } if ("$((Get-Val $deskKey 'ScreenSaveTimeOut'))" -ne "$TimeoutSeconds") { return $false } return $true } function Set-SaverValues($deskKey) { if (-not (Test-Path $deskKey)) { New-Item -Path $deskKey -Force | Out-Null } Set-ItemProperty -Path $deskKey -Name 'ScreenSaveActive' -Value '1' -Type String -Force Set-ItemProperty -Path $deskKey -Name 'SCRNSAVE.EXE' -Value $ScrPath -Type String -Force Set-ItemProperty -Path $deskKey -Name 'ScreenSaveTimeOut' -Value "$TimeoutSeconds" -Type String -Force Set-ItemProperty -Path $deskKey -Name 'ScreenSaverIsSecure' -Value '0' -Type String -Force } # --- 1. seed .DEFAULT so profiles created later start correct ----------------- $defKey = 'Registry::HKEY_USERS\.DEFAULT\Control Panel\Desktop' if (-not (Saver-IsSet $defKey)) { try { Set-SaverValues $defKey; Write-Log "seeded .DEFAULT ($TimeoutSeconds s)" } catch { Write-Log "ERROR seeding .DEFAULT: $_" } } # --- 2. apply to every loaded human hive -------------------------------------- # Skipped: the three service accounts (SYSTEM, LOCAL SERVICE, NETWORK SERVICE) # and the _Classes companions, which are not user desktops and would just add # noise. Everything else that is loaded belongs to somebody signed in now. $serviceSids = @('S-1-5-18', 'S-1-5-19', 'S-1-5-20') $applied = 0 $already = 0 try { $hives = Get-ChildItem 'Registry::HKEY_USERS' -ErrorAction Stop | ForEach-Object { $_.PSChildName } } catch { Write-Log "ERROR enumerating HKEY_USERS: $_" $hives = @() } foreach ($sid in $hives) { if ($sid -eq '.DEFAULT') { continue } # handled above if ($sid -like '*_Classes') { continue } if ($serviceSids -contains $sid) { continue } if ($sid -notlike 'S-1-5-21-*') { continue } # real domain/local users only $hiveKey = "Registry::HKEY_USERS\$sid\Control Panel\Desktop" if (Saver-IsSet $hiveKey) { $already++; continue } try { Set-SaverValues $hiveKey $applied++ Write-Log "applied to hive $sid ($TimeoutSeconds s)" } catch { Write-Log "ERROR writing hive ${sid}: $_" } } if ($applied -eq 0 -and $already -eq 0) { # Nobody signed in - normal during imaging or on an idle bay. .DEFAULT above # covers the next profile, and the next cycle after a logon covers the rest. Write-Log 'no user hives loaded; .DEFAULT seeded, will apply on a later cycle' } # --- 3. remove the old per-user fallback task --------------------------------- # The previous version registered an AtLogon task for the hardcoded 'Shopfloor' # account. On machines where nobody signs in as that, it sat queued forever and # never fired. Applying to loaded hives every cycle replaces it, so clear any # that are still registered. try { if (Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue) { Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue Write-Log "removed stale fallback task '$TaskName'" } } catch { } exit 0