Game Foreground Fix
Windows-Hilfsprogramm, das ein Spielfenster automatisch in den Vordergrund bringt. Gedacht für Spiele wie Crimson Desert, die nach dem Start im Hintergrund landen oder von Steam, Taskleiste, Splashscreens oder Overlay-Fenstern verdeckt werden. Funktioniert mit Steam (automatische Prozesserkennung) und Sunshine/Moonlight (manueller Prozessname).
Inhaltsverzeichnis
- Problembeschreibung
- Lösungsansatz
- Architektur
- Voraussetzungen
- Installation
- Konfiguration
- Ablauf zur Laufzeit
- Dateireferenz
- Konfigurationsparameter
- Windows API — Technischer Hintergrund
- Manuelles Testen
- Debugging
- Bekannte Einschränkungen
Problembeschreibung
Beim Start eines Spiels über Steam oder Sunshine/Moonlight öffnet sich das Spielfenster im Hintergrund — die Steam-Oberfläche, die Windows-Taskleiste oder andere Fenster verdecken es. Der Spieler muss manuell per Alt+Tab zum Spiel wechseln.
Ursache: Windows vergibt den Vordergrundfokus an den Prozess, der ihn aktuell besitzt. Das Spielfenster erhält keinen automatischen Fokus, weil es von einem anderen Prozess (Steam-Launcher, Sunshine) erzeugt wurde.
Zusätzlich erzeugen manche Spiele beim Start mehrere Fenster — Splashscreens, Launcher oder titellose Overlay-Fenster — die den Fokus stören können.
Lösungsansatz
Ein Python-Script überwacht den Spielstart, wartet auf das Hauptfenster des Spiels und erzwingt den Vordergrundfokus über Windows-API-Aufrufe. Ein VBScript-Wrapper sorgt bei Steam dafür, dass weder ein Konsolenfenster noch andere störende Fenster erscheinen. Das Tool ist nicht auf ein bestimmtes Spiel festgelegt, sondern auf typische Startprobleme mit ähnlichem Fensterverhalten ausgelegt.
Architektur
Steam Launch Options / Sunshine Befehlsvorbereitung
│
▼
┌──────────┐ extrahiert Prozessname ┌────────────────────┐
│ launch.vbs├──── aus %command% ────────────►│ pythonw.exe │
│ (nur Steam)│ + startet unsichtbar │ foreground_fix.py │
│ │ │ --process GameName │
│ │ startet normal │ │
│ ├────────────────────────────────►│ (wartet auf Spiel) │
└──────────┘ %command% = Spiel.exe └────────┬───────────┘
│
Fenster gefunden
│
┌────────▼───────────┐
│ 1. Fenster erkennen│
│ 2. Größe abwarten │
│ 3. Spiel fokussieren│
│ 4. Wechsel beobachten│
│ 5. Script beendet │
└────────────────────┘
Zwei Dateien, klare Aufgabentrennung:
| Datei | Sprache | Aufgabe |
|---|---|---|
launch.vbs |
VBScript | Startrampe (nur Steam): extrahiert Prozessname aus %command%, startet das Python-Script unsichtbar und leitet den Spielstart weiter |
foreground_fix.py |
Python 3 | Kernlogik: Prozess-Monitoring, Fenstererkennung, Fensterwechsel-Erkennung, Fokus-Erzwingung, Logging |
Voraussetzungen
| Komponente | Version | Hinweis |
|---|---|---|
| Windows | 10 / 11 | Benötigt Win32 API (user32.dll) |
| Python | 3.10+ | py Launcher muss verfügbar sein |
| pip-Pakete | psutil, pywin32 |
Werden im .venv installiert |
| Steam | Beliebig | Startoptionen müssen konfigurierbar sein |
Installation
cd C:\Projekte\Steam_backgroud_fix
# Virtual Environment erstellen
py -m venv .venv
# Aktivieren
.venv\Scripts\activate
# Abhängigkeiten installieren
pip install psutil pywin32
Dateistruktur nach Installation
C:\Projekte\Steam_backgroud_fix\
├── .venv\ # Python Virtual Environment
├── foreground_fix.py # Hauptscript (Kernlogik)
├── foreground_fix.log # Logdatei (wird automatisch erstellt)
├── launch.vbs # VBScript-Wrapper für Steam
├── CLAUDE.md # Kontext für Claude Code
└── README.md # Diese Dokumentation
Konfiguration
Steam
- Steam öffnen → Bibliothek
- Rechtsklick auf das Spiel → Eigenschaften
- Unter Allgemein → Startoptionen eintragen:
wscript "C:\Projekte\Steam_backgroud_fix\launch.vbs" %command%
launch.vbs extrahiert den Prozessnamen automatisch aus %command% — keine manuelle Anpassung pro Spiel nötig. Die gleiche Startoption kann für beliebige Steam-Spiele mit vergleichbarem Fokus-Problem verwendet werden.
Erklärung:
wscript— Windows Script Host, führt VBScript ohne Konsolenfenster aus"...\launch.vbs"— Pfad zum VBScript-Wrapper%command%— Steam-Platzhalter, wird zur Laufzeit durch den vollständigen Pfad zur Spiel-Executable ersetzt (z.B."C:\SteamLibrary\...\Game.exe")
Sunshine / Moonlight
In Sunshine als Befehlsvorbereitung (Command Preparation) pro Spiel eintragen:
"C:\Projekte\Steam_backgroud_fix\.venv\Scripts\pythonw.exe" "C:\Projekte\Steam_backgroud_fix\foreground_fix.py" --process "Prozessname"
Den Prozessnamen im Task-Manager ablesen (ohne .exe). Beispiele:
| Spiel | Prozessname |
|---|---|
| Crimson Desert | CrimsonDesert |
| Wuthering Waves | Client-Win64-Shipping |
Wichtig: Der vollständige Pfad zu pythonw.exe im .venv ist nötig, damit die Abhängigkeiten (psutil, pywin32) gefunden werden. py oder python nutzt die globale Python-Installation ohne diese Pakete.
Ablauf zur Laufzeit
Zeitachse
─────────────────────────────────────────────────────────────────────
t=0s Steam/Sunshine startet das Script mit --process "GameName"
t=0.1s foreground_fix.py beginnt zu pollen (alle 0.5s)
→ Sucht nach Prozess "GameName"
t=~3s Spielprozess gestartet, ggf. Splashscreen erscheint
→ Script findet den Prozess (PID)
→ Sucht nach Fenster mit Breite >= 800px
→ Splashscreen ist kleiner → wird ignoriert
t=~10s Hauptfenster des Spiels erscheint (z.B. 2560×1440)
→ Script findet das Fenster (bevorzugt Fenster mit Titel)
→ Wartet 3 Sekunden (Initialisierung abwarten)
→ Wartet bis Fenstergröße stabil ist
t=~13s Phase 3: Fokus-Erzwingung
→ Spielfenster wird in den Vordergrund gebracht
→ Bei Misserfolg: bis zu 15 Wiederholungen (je 2s Pause)
→ Beobachtet ob sich das Fenster ändert (Splash → Hauptfenster)
→ Ignoriert titellose Overlay-Fenster
t=~15s Erfolg → foreground_fix.py beendet sich
→ Logdatei enthält den vollständigen Ablauf
Dateireferenz
launch.vbs
Set WshShell = CreateObject("WScript.Shell")
' Prozessname aus dem ersten Argument (Steam %command%) extrahieren
Dim exePath, fso, exeName
Set fso = CreateObject("Scripting.FileSystemObject")
exePath = WScript.Arguments(0)
exeName = fso.GetBaseName(exePath)
' Foreground-Fix unsichtbar starten mit extrahiertem Prozessnamen
WshShell.Run """...\pythonw.exe"" ""...\foreground_fix.py"" --process """ & exeName & """", 0, False
' Spiel starten: Steam-Argumente zusammensetzen
Dim gameCmd, i
gameCmd = ""
For i = 0 To WScript.Arguments.Count - 1
gameCmd = gameCmd & """" & WScript.Arguments(i) & """ "
Next
If gameCmd <> "" Then
WshShell.Run gameCmd, 1, False
End If
WshShell.Run Parameter:
| Parameter | Wert | Bedeutung |
|---|---|---|
| Command | "pythonw.exe" "foreground_fix.py" --process "GameName" |
Auszuführender Befehl |
| WindowStyle | 0 |
Fenster komplett versteckt |
| WaitOnReturn | False |
Nicht auf Beendigung warten (asynchron) |
Warum pythonw.exe statt python.exe?
python.exe ist eine Konsolenanwendung und erzeugt automatisch ein CMD-Fenster. pythonw.exe ist die "windowless"-Variante — identischer Python-Interpreter, aber ohne Konsole.
Warum VBScript statt Batch?
Eine .bat-Datei wird von cmd.exe ausgeführt, das immer ein Konsolenfenster öffnet — selbst wenn es sich sofort wieder schließt. VBScript über wscript.exe erzeugt zu keinem Zeitpunkt ein sichtbares Fenster.
foreground_fix.py
Funktionsübersicht
| Funktion | Beschreibung |
|---|---|
find_process_pids(name) |
Sucht laufende Prozesse nach Name (Teilstring, case-insensitiv), gibt PIDs zurück |
find_game_window(pids) |
Findet das Hauptfenster der gegebenen PIDs — bevorzugt Fenster mit Titel, dann nach Größe |
wait_for_stable_size(hwnd) |
Wartet bis die Fenstergröße sich stabilisiert hat (kein Resize mehr) |
is_foreground(hwnd) |
Prüft ob ein Fenster aktuell im Vordergrund ist |
bring_to_front(hwnd) |
Erzwingt den Vordergrundfokus für ein Fenster (Alt-Key + AttachThreadInput) |
main() |
Orchestriert den 3-Phasen-Ablauf |
find_game_window(pids) — Fensterauswahl
Nutzt win32gui.EnumWindows() um alle Top-Level-Fenster zu durchlaufen. Für jedes Fenster:
- Prüft Sichtbarkeit (
IsWindowVisible) - Ermittelt die zugehörige PID (
GetWindowThreadProcessId) - Misst die Fenstergröße (
GetWindowRect) - Filtert nach
MIN_WINDOW_WIDTH(Standard: 800px)
Bei mehreren Treffern wird das Fenster mit Titel bevorzugt, dann nach Fläche sortiert. Dadurch werden sowohl Splashscreens (zu klein) als auch titellose Overlay-Fenster (kein Titel) vom Hauptfenster unterschieden.
bring_to_front(hwnd) — Fokus-Erzwingung
Erzwingt den Vordergrundfokus trotz Windows-Einschränkungen:
try:
# Schritt 1: Alt-Taste simulieren (hebt SetForegroundWindow-Sperre auf)
keybd_event(VK_MENU, 0, 0, 0)
# Schritt 2: Thread-Input verbinden
AttachThreadInput(target_thread, foreground_thread, True)
# Schritt 3: Fenster anzeigen und fokussieren
ShowWindow(hwnd, SW_SHOW)
BringWindowToTop(hwnd)
SetForegroundWindow(hwnd)
SetFocus(hwnd)
SetActiveWindow(hwnd)
# Schritt 4: Thread-Input trennen
AttachThreadInput(target_thread, foreground_thread, False)
except Exception:
...
finally:
# Alt-Taste wird IMMER losgelassen, auch bei Fehlern
keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0)
Die Alt-Taste wird in einem finally-Block freigegeben, damit sie bei Fehlern (z.B. ungültiges Handle, fehlende Rechte) nicht systemweit gedrückt bleibt.
main() — Dreistufiger Ablauf
-
Phase 1 — Prozess-Suche: Pollt alle 0,5 Sekunden nach dem Prozessnamen. Bricht nach
--timeoutSekunden ab. -
Phase 2 — Fenster-Suche: Pollt alle 0,5 Sekunden nach einem sichtbaren Fenster mit ausreichender Größe. Nach Fund: 3 Sekunden Wartezeit für die vollständige Initialisierung.
-
Phase 3 — Fokus mit Fensterwechsel-Erkennung: Bis zu 15 Versuche den Spielfokus zu erzwingen, mit je 2 Sekunden Pause. Beobachtet dabei, ob sich das Fenster ändert (z.B. Splash → Hauptfenster) und fokussiert das neue Fenster. Titellose Overlay-Fenster werden ignoriert, wenn bereits ein Fenster mit Titel fokussiert wurde.
Konfigurationsparameter
Kommandozeilenargumente
| Argument | Pflicht | Standard | Beschreibung |
|---|---|---|---|
--process |
Ja | — | Teilstring des Prozessnamens (case-insensitiv, ohne .exe) |
--timeout |
Nein | 120 |
Maximale Wartezeit in Sekunden für Prozess- und Fensterfund |
Konstanten in foreground_fix.py
| Konstante | Wert | Beschreibung |
|---|---|---|
MIN_WINDOW_WIDTH |
800 |
Mindestbreite in Pixeln, um Splashscreens auszufiltern |
FOCUS_RETRIES |
15 |
Maximale Anzahl der Fokus-Versuche |
FOCUS_RETRY_DELAY |
2.0 |
Sekunden zwischen Fokus-Versuchen |
Hardcodierte Pfade in launch.vbs
| Pfad | Zweck |
|---|---|
C:\Projekte\Steam_backgroud_fix\.venv\Scripts\pythonw.exe |
Python-Interpreter (windowless) |
C:\Projekte\Steam_backgroud_fix\foreground_fix.py |
Hauptscript |
Bei einer Änderung des Installationsverzeichnisses müssen diese Pfade in launch.vbs angepasst werden.
Windows API — Technischer Hintergrund
Das SetForegroundWindow-Problem
Seit Windows 2000 verhindert das Betriebssystem, dass beliebige Prozesse den Vordergrundfokus "stehlen" können. SetForegroundWindow(hwnd) funktioniert nur, wenn:
- Der aufrufende Prozess der Vordergrundprozess ist, oder
- Der aufrufende Prozess von dem Vordergrundprozess gestartet wurde, oder
- Der letzte Eingabe-Event vom aufrufenden Prozess stammt
Andernfalls blinkt das Fenster lediglich in der Taskleiste.
Da foreground_fix.py als eigenständiger Prozess läuft (nicht vom Spiel gestartet), erfüllt es keine dieser Bedingungen.
Workaround-Strategie
Das Script kombiniert drei Techniken:
1. Alt-Key-Simulation (keybd_event)
Simuliert einen Tastendruck der Alt-Taste. Windows behandelt den aufrufenden Prozess danach so, als hätte der Benutzer eine Eingabe getätigt — dies hebt die SetForegroundWindow-Sperre auf.
2. Thread-Input-Verbindung (AttachThreadInput)
Verbindet die Eingabewarteschlangen zweier Threads. Danach teilen sich beide Threads den gleichen Eingabefokus-Zustand. Der Ziel-Thread (Spiel) kann nun den Fokus erhalten, als wäre er Teil des Vordergrund-Threads.
3. Mehrfache Fokus-Befehle
ShowWindow, BringWindowToTop, SetForegroundWindow, SetFocus und SetActiveWindow adressieren jeweils einen leicht anderen Aspekt des Fenster-Fokus unter Windows. Die Kombination stellt sicher, dass das Fenster sowohl visuell im Vordergrund ist als auch Tastatur- und Mauseingaben empfängt.
Splashscreen-Erkennung
Viele Spiele zeigen beim Start einen Splashscreen, bevor das Hauptfenster erscheint. Beide gehören zum selben Prozess.
Unterscheidungsmerkmal: Fenstergröße. Der Splashscreen ist typischerweise 300–600px breit. Das Spielfenster ist mindestens so groß wie die Bildschirmauflösung (z.B. 2560×1440). Der Schwellwert MIN_WINDOW_WIDTH = 800 trennt beide zuverlässig.
Zusätzlich erzeugen manche Spiele (z.B. Wuthering Waves) titellose Overlay-Fenster in voller Bildschirmgröße. Diese werden durch die Bevorzugung von Fenstern mit Titel in der Sortierung und durch die Fensterwechsel-Logik ignoriert.
Manuelles Testen
Script alleine testen (Spiel muss laufen)
cd C:\Projekte\Steam_backgroud_fix
.venv\Scripts\python foreground_fix.py --process "CrimsonDesert" --timeout 5
Die Laufzeit-Logs landen in foreground_fix.log. Bei Ausführung über pythonw.exe gibt es keine sichtbare Ausgabe; bei manuellen Starts mit python.exe können Argument- oder Interpreter-Fehler weiterhin in der Konsole erscheinen.
VBScript alleine testen
wscript "C:\Projekte\Steam_backgroud_fix\launch.vbs" "C:\dummy\TestGame.exe"
Sollte das Python-Script unsichtbar starten mit --process "TestGame". Prüfen ob pythonw.exe im Task-Manager erscheint.
Gesamtablauf testen
- Steam-Startoptionen wie oben konfigurieren
- Spiel über Steam starten
- Erwartung: Spielfenster erscheint nach dem Ladescreen im Vordergrund, keine Taskleiste sichtbar, keine CMD- oder Script-Fenster
foreground_fix.logprüfen für den detaillierten Ablauf
Debugging
Alle Informationen werden in foreground_fix.log protokolliert:
- Gefundene Kandidaten-Fenster (Handle, Titel, Größe, PID)
- Fensterwechsel und ignorierte titellose Fenster
- Fokus-Versuche mit Ergebnis (Erfolg/Misserfolg)
- Fehler mit vollständigem Traceback
Häufige Probleme
| Symptom | Ursache | Lösung |
|---|---|---|
| Spiel startet nicht | Steam-Startoptionen fehlerhaft | Prüfen ob %command% am Ende steht |
ModuleNotFoundError: psutil |
Globales Python statt venv | Vollständigen Pfad zu .venv\Scripts\pythonw.exe verwenden |
| Script findet Prozess nicht | Prozessname stimmt nicht | --process anpassen, Namen im Task-Manager prüfen |
| Fenster wird nicht fokussiert | Windows-Restriktion zu streng | Retry-Anzahl/Delay in Konstanten erhöhen |
| Alt-Taste bleibt hängen | Spiel mit Admin-Rechten, Script ohne | Script ebenfalls als Admin starten |
| CMD-Fenster sichtbar | python.exe statt pythonw.exe |
Pfad in launch.vbs bzw. Sunshine-Konfiguration prüfen |
| Verdunkeltes/blockiertes Spiel | Titelloses Overlay-Fenster | Log prüfen — sollte durch Fenster-mit-Titel-Bevorzugung gelöst sein |
Fenster-Inspektion per PowerShell
Um alle Fenster eines laufenden Spielprozesses zu inspizieren:
Get-Process -Name GameName | Select-Object Id, Name, MainWindowTitle, MainWindowHandle
Bekannte Einschränkungen
- Kein Admin erforderlich, aber manche Spiele starten mit erhöhten Rechten — in dem Fall muss das Script ebenfalls als Admin laufen
- Exklusiver Vollbildmodus: Spiele im exklusiven Vollbild (kein Borderless Windowed) können sich anders verhalten, da sie die komplette Bildschirmausgabe übernehmen
- Anti-Cheat: Einige Anti-Cheat-Systeme könnten die
keybd_event-Simulation oderAttachThreadInput-Aufrufe als verdächtig einstufen — bisher keine Probleme bekannt - Mehrere Monitore:
find_game_windowwählt das Fenster unabhängig davon, auf welchem Monitor es liegt - Nicht universell für jedes Spiel: Das Tool ist generisch für viele ähnliche Fälle, aber nicht garantiert für jeden Launcher, jedes Anti-Cheat-System oder jedes ungewöhnliche Fenster-/Startverhalten