# -*- coding: utf-8 -*- """Pruefstand: kommt bei der GEBAUTEN EXE wirklich ein Fenster? DER FALL, DER DAZU GEFUEHRT HAT (06.09.2026) -------------------------------------------- Nach dem Installieren lief das Programm - im Taskmanager stand es da, es verbrauchte Rechenzeit - und auf dem Bildschirm war NICHTS. Ursache: der Erprobungshinweis ging auf, bevor das Hauptfenster gezeichnet war. Der modale Dialog sperrte das Hauptfenster, und beide blieben unsichtbar. WARUM DAS KEIN ANDERER PRUEFSTAND FINDET ---------------------------------------- Alle uebrigen Pruefstaende fahren das Programm aus der QUELLE. Dort ist das Fenster sofort gezeichnet, der Fehler tritt nicht auf. Er gehoert zur gepackten EXE - also muss genau die gestartet werden. Und "der Prozess laeuft" beweist nichts: er lief ja. Geprueft wird deshalb, ob Windows ein Fenster dieses Programms als SICHTBAR fuehrt. ZWEI FALLEN, die hier schon zugeschnappt sind: * Eine PyInstaller-Onefile-EXE ist zweistufig. Der gestartete Prozess packt nur aus; das Fenster gehoert dem KIND. Wer nur die eigene PID betrachtet, sieht nie ein Fenster. * terminate() beendet nur den Auspacker. Das Kind laeuft weiter - und dessen Einzelinstanz-Sperre haelt danach jeden weiteren Start ab. Es muss die ganze Sippe beendet werden. """ import ctypes import os import subprocess import sys import time from ctypes import wintypes HIER = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HIER) import version # noqa: E402 from pruefhelfer import fehler, pruefe stamm, endung = os.path.splitext(version.EXE_NAME) EXE = os.path.join(HIER, "dist", f"{stamm}-{version.VERSION}{endung}") if not os.path.exists(EXE): print(f"UEBERSPRUNGEN | {os.path.basename(EXE)} ist nicht gebaut " f"(vorher: python bauen.py)") sys.exit(0) u = ctypes.windll.user32 # Mit --test: dann geht das Fenster auf dem ZWEITEN Monitor auf und nicht # mitten auf dem Hauptbildschirm, wo gerade jemand arbeitet. Seine Ansage am # 07.09.2026 - und dieser Pruefstand war der eine, der sie noch nicht befolgte, # weil er die gebaute EXE ganz normal startet. Sichtbar bleibt das Fenster, # und genau das wird hier ja geprueft. p = subprocess.Popen([EXE, "--test"]) print(f" (gestartet: {os.path.basename(EXE)}, PID {p.pid})") RUECKRUF = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM) gefunden = [] verwandte = {p.pid} def sippe(): alle = {p.pid} try: e = subprocess.run( ["powershell", "-NoProfile", "-Command", "Get-CimInstance Win32_Process | Where-Object " f"{{ $_.ParentProcessId -eq {p.pid} }} | ForEach-Object {{ $_.ProcessId }}"], capture_output=True, text=True, timeout=20) for zeile in e.stdout.split(): if zeile.strip().isdigit(): alle.add(int(zeile.strip())) except Exception: # noqa: BLE001 - dann bleibt es bei der eigenen PID pass return alle def je_fenster(hwnd, _): pid = wintypes.DWORD() u.GetWindowThreadProcessId(hwnd, ctypes.byref(pid)) if pid.value not in verwandte: return True laenge = u.GetWindowTextLengthW(hwnd) puffer = ctypes.create_unicode_buffer(laenge + 1) u.GetWindowTextW(hwnd, puffer, laenge + 1) r = wintypes.RECT() u.GetWindowRect(hwnd, ctypes.byref(r)) gefunden.append((puffer.value, bool(u.IsWindowVisible(hwnd)), r.right - r.left, r.bottom - r.top)) return True ende = time.time() + 60 sichtbar = False vorzeitig_weg = False while time.time() < ende and not sichtbar: if p.poll() is not None: vorzeitig_weg = True break verwandte = sippe() gefunden.clear() u.EnumWindows(RUECKRUF(je_fenster), 0) # Ein "Fenster" von wenigen Pixeln ist Verwaltung (IME, Notifier), kein # Programmfenster. Deshalb die Groesse mitpruefen. sichtbar = any(s and b > 300 and h > 300 for _, s, b, h in gefunden) time.sleep(0.5) for titel, s, b, h in gefunden: print(f" {titel!r:42s} sichtbar={s} {b}x{h}") pruefe(not vorzeitig_weg, f"das Programm bleibt am Leben (Rueckgabe {p.returncode})" if vorzeitig_weg else "das Programm bleibt am Leben") if vorzeitig_weg and p.returncode == 0: print(" (Rueckgabe 0 heisst meist: es laeuft schon eine Fassung. " "Erst beenden, dann noch einmal.)") pruefe(sichtbar, "ein sichtbares Programmfenster ist da") pruefe(any(str(t).startswith(stamm) and s for t, s, _, _ in gefunden), f"und es traegt den Programmnamen ({stamm})") subprocess.run(["taskkill", "/PID", str(p.pid), "/T", "/F"], capture_output=True) try: p.wait(10) except subprocess.TimeoutExpired: p.kill() if fehler: print(f"ROT | {len(fehler)} Punkt(e) nicht erfuellt") sys.exit(1) print("GRUEN | Die gebaute EXE zeigt ein Fenster")