Windows-Screenshot-Tool als WPF-Anwendung in Windows PowerShell 5.1 mit Tray-Integration, globalen Hotkeys, Editor und Verlauf. Verteilung als signiertes Paket → MSI durch das Softwareverteilteam.
PS 5.1 ist einziges Target. PowerShell 7 wird nicht unterstützt (Enterprise-Hosts haben es nicht garantiert; doppelter Support kostet nur Boilerplate). Erwägen wir später erneut, wenn
pwsh.exeflächendeckend ausgerollt ist. Kein PS-7-Sprachfeature nutzen: keine Ternary? :, keine Null-Conditional?./??, kein&&/||in der Pipeline.$IsWindowsexistiert in 5.1 nicht — nie referenzieren.
Lies vor jeder Implementierung
todo.md(offene Arbeitspakete) unddocs/Architektur.md(Module + Datenfluss). Was bereits erledigt ist, steht inluscreen-docs/docs/entwicklung/erledigt.md(inkl. Commit-Log). Datumsformat in Logs/Tabellen:YYYYMMDD-HHMM(z.B.20260515-1412).
| Layer | Pfad | Darf | Darf nicht |
|---|---|---|---|
| core | src/core/*.psm1 |
Logik, GDI+, P/Invoke, Konfig, Logging | PresentationFramework, XAML, Tray |
| ui | src/ui/*.psm1 |
XAML laden, Fenster, Hotkey-Hook, NotifyIcon | Direkte Domain-Logik (delegiert an core) |
| views | src/views/*.xaml |
reine XAML-Markup-Dateien | Code-Behind (Logik gehört in ui-Module) |
| main.ps1 | src/LucentScreen.ps1 |
Bootstrap + Application-Loop | Sonst nichts |
Regel: main.ps1 ist der einzige Ort, der core und ui zusammensteckt. Module untereinander dürfen Import-Module aufrufen, aber ui darf nicht von core umgekehrt importiert werden.
# 1. STA prüfen -- WPF und Clipboard funktionieren sonst nicht.
if ([Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') {
Start-Process powershell.exe -ArgumentList '-STA','-File',$PSCommandPath
exit
}
# 2. Single-Instance via Named-Mutex (kein File-Lock)
$mutex = [System.Threading.Mutex]::new($false, 'Global\LucentScreen.SingleInstance')
if (-not $mutex.WaitOne(0, $false)) { exit }
# 3. DPI-Awareness als allererste Zeile nach Logging
[LucentScreen.Native]::SetProcessDpiAwarenessContext(-4) # PER_MONITOR_AWARE_V2- Sprache: UI/Doku Deutsch, Code/Variablen/Logs intern Englisch, User-sichtbare Logs Deutsch.
- Keine Claude/AI-Marker in Commits, Code-Kommentaren, Doku.
- Keine globalen Variablen (
$global:). Innerhalb Modul:$script:-Scope. - Funktionen die fehlschlagen können, geben strukturiertes Hashtable zurück:
return @{ Success = $true; Status = 'OK'; Message = '…'; Path = $path } return @{ Success = $false; Status = 'Error'; Message = '…'; Path = $null }
- Config-Pfad:
%APPDATA%\LucentScreen\config.json— NIE im Programmordner (MSI-/Per-Machine-kompatibel). - WPF-Disposing:
NotifyIcon,Bitmap,Graphics,BitmapSource-Streams müssen.Dispose()bekommen. Faustregel: jedesNew-Objectmit IDisposable →try/finally. - Hotkey-Lifecycle: jedes
RegisterHotKeybraucht zwingendenUnregisterHotKey-Counterpart im Shutdown-Path. - PSSA-clean: Code muss
tools/Invoke-PSSA.ps1ohne Errors überstehen. Warnings tolerieren, dokumentieren. Konfig:PSScriptAnalyzerSettings.psd1im Repo-Root.
#Requires -Version 5.1
Set-StrictMode -Version Latest
function Get-Something {
param([string]$Param)
# implementation
}
Export-ModuleMember -Function Get-SomethingJede Funktion ist entweder exportiert oder fängt mit _ an (privat).
| Anti-Pattern | PS-5.1-Lösung |
|---|---|
$cond ? 'a' : 'b' |
if ($cond) { 'a' } else { 'b' } |
$x?.Property |
if ($null -ne $x) { $x.Property } |
$val ?? 'default' |
if ($null -eq $val) { 'default' } else { $val } |
cmd1 || cmd2 (Pipeline-Chain) |
klassisch: separater if-Block über $LASTEXITCODE |
$IsWindows lesen |
nicht verfügbar in 5.1 — entweder weglassen oder ($PSVersionTable.Platform -ne 'Unix') |
[Type]::new(args) für out-Parameter |
OK; Edge-Cases mit [ref] testen |
Invoke-RestMethod -SkipCertificateCheck |
nicht in 5.1 — [ServicePointManager]::ServerCertificateValidationCallback setzen |
System.Windows.Application ist ein Per-AppDomain-Singleton. PowerShell ISE hostet selbst eine WPF-Application im UI-Thread — der LucentScreen-Bootstrap würde aus dem Pipeline-Thread auf das fremde Objekt zugreifen und die ISE killen. Der Host-Guard in src/LucentScreen.ps1 fängt das ab.
Empfehlung für Entwicklung: VS Code mit PowerShell-Extension. Hintergrund: VS Code selbst ist Electron (kein WPF), und die PowerShell-Extension startet einen separaten powershell.exe-PSES-Prozess für den Integrated Console. Der Prozess hat beim Start kein WPF-Application → Host-Guard greift nicht, App läuft direkt in der Editor-Session, F5/F8 funktionieren beide.
ISE-Workflow (falls nötig):
- F5 (Run Script) funktioniert via Self-Relaunch — der Host-Guard erkennt
$psISEund startet einen frischenpowershell.exe -STA -File $PSCommandPath-Sub-Prozess. ISE bleibt offen, App läuft in eigener AppDomain (kein In-ISE-Debugging möglich). - F8 (Run Selection) funktioniert nur teilweise:
$PSCommandPathist bei F8 nicht gesetzt, daher fällt der Host-Guard auf MessageBox +returnzurück. Ab 0.3.11 wird die WinForms-MessageBox (statt WPF) genutzt undreturn(stattexit 0) — beides nötig, damit die ISE die fremde WPF-Application der ISE nicht von einem Pipeline-Thread aus anfasst und beim Skript-Ende nicht in einen Runspace-Teardown läuft. Praxis-Erfahrung (Mai 2026): mit WPF-MessageBox +exit 0hat die ISE trotz angezeigter Box wieder neugestartet. Für REPL-Tests einzelner Funktionen lieber VS Code benutzen.
VS-Code-Caveat: Integrated-Console-Session persistiert. Sobald die App im PSES-Prozess hochgefahren ist, lebt Application.Current weiter. Ein zweiter F5-Start würde am Application.new() mit „mehr als eine Instanz" scheitern — daher vor erneutem Test: Ctrl+Shift+P → „PowerShell: Restart Session" (oder das Restart-Icon im Integrated Console).
BeforeAll {
Import-Module "$PSScriptRoot/../src/core/<modul>.psm1" -Force
}
AfterEach {
Get-ChildItem $TestDrive -Recurse | Remove-Item -Recurse -Force -EA SilentlyContinue
}
# Mocking von Modul-internen Aufrufen IMMER mit -ModuleName
Mock -ModuleName <modul> Get-Foo { 'mock' }$TestDrive ist innerhalb Describe shared — pro It einzigartige Pfade benutzen.
p Parse-Check (AST aller *.ps1/*.psm1)
l PSScriptAnalyzer Lint (alle Sources)
L PSSA nur geänderte Dateien (-OnlyChangedSinceMain)
t Pester (alle Tests)
T Pester (einzelne Test-Datei)
a Audit (auditor-Agent: parse+pssa+pester+arch+doc-sync)
s App starten (-STA)
S App stoppen
d Zensical-Site bauen + im Standard-Browser öffnen
D Doku-Live-Server starten (http://127.0.0.1:8000)
h HTML-Single-Page Doku bauen (LucentScreen.docs.html)
Alle Werkzeuge schreiben Reports nach reports/<tool>/:
reports/pssa/pssa-report.{md,json}reports/pester/pester-report.md+pester-results.xmlreports/parse/parse-report.mdreports/audit/audit-yyyy-mm-dd.md
Snapshots vor Release: reports/<tool>/history/yyyy-mm-dd_HHmm/.
Siehe .claude/agents/*.md. Routing-Faustregel:
- Audit →
auditor(opus) - PS-Modul oder Pester →
powershell-specialist(sonnet) - XAML/WPF-Layout/HwndSource →
wpf-ui-specialist(sonnet) Add-Type/P/Invoke/GDI+ →csharp-specialist(sonnet)- Multi-Monitor/Bereichs-Capture →
capture-engine-specialist(sonnet) - MSI/Transfer-Bundle/Signing →
packaging-specialist(sonnet) - DE-User-Doku →
doc-writer(haiku)
- PS 7-Support neu evaluieren, sobald
pwsh.exeStandard auf den Ziel-Hosts ist (z.B. via MSI/MDM ausgerollt). Dann lassen sich Ternary/Null-Conditional/&&-Chains aktivieren, was Code etwas kürzer und besser lesbar macht. Bis dahin: 5.1 only, kein doppelter Support.