#!/usr/bin/env python3 """Phase-5-Hilfe: AzA-Fenster und eigene AzA-Prozesse nach Shutdown pruefen.""" from __future__ import annotations import argparse import ctypes import subprocess import sys from ctypes import wintypes AZA_WINDOW_PREFIXES = ( "AzA Office", "AzA-Empfang", "AzA Empfang Chat", "AzA MiniChat", "AzA Kontakte", "AzA Add-on Beta", "Diktat", "AZA von Arzt zu Arzt", "AzA von Arzt zu Arzt", ) AZA_PROCESS_NAMES = ( "aza_desktop.exe", "aza_start_panel.exe", "AZA_EmpfangShell.exe", "AZA_KontaktPanel.exe", "aza_updater.exe", ) def _matches(title: str, prefixes: tuple[str, ...]) -> bool: return any(title == p or title.startswith(p) for p in prefixes if p) def count_aza_windows() -> list[tuple[int, int, str]]: if sys.platform != "win32": return [] user32 = ctypes.windll.user32 found: list[tuple[int, int, str]] = [] @ctypes.WINFUNCTYPE(ctypes.c_bool, wintypes.HWND, wintypes.LPARAM) def _cb(hwnd, _lp): visible = bool(user32.IsWindowVisible(hwnd) or user32.IsIconic(hwnd)) if not visible: return True buf = ctypes.create_unicode_buffer(320) user32.GetWindowTextW(hwnd, buf, 320) t = buf.value or "" if not _matches(t, AZA_WINDOW_PREFIXES): return True p = wintypes.DWORD() user32.GetWindowThreadProcessId(hwnd, ctypes.byref(p)) found.append((int(hwnd), int(p.value), t)) return True user32.EnumWindows(_cb, 0) return found def count_aza_processes() -> list[tuple[int, str]]: if sys.platform != "win32": return [] out: list[tuple[int, str]] = [] try: r = subprocess.run( [ "powershell", "-NoProfile", "-Command", "Get-Process | Where-Object { $_.ProcessName -match " "'^(aza_desktop|aza_start_panel|AZA_EmpfangShell|AZA_KontaktPanel|aza_updater)$' } " "| Select-Object Id,ProcessName | ConvertTo-Json -Compress", ], capture_output=True, text=True, timeout=15, ) raw = (r.stdout or "").strip() if not raw: return out import json data = json.loads(raw) if isinstance(data, dict): data = [data] for item in data or []: pid = int(item.get("Id") or 0) name = str(item.get("ProcessName") or "") if pid and name: out.append((pid, name.lower() + ".exe")) except Exception: pass return out def main() -> int: ap = argparse.ArgumentParser(description="AzA Shutdown Restpruefung Phase 5") ap.add_argument( "--check", action="store_true", help="Exit 1 wenn noch AzA-Fenster oder eigene AzA-EXEs laufen", ) args = ap.parse_args() if sys.platform != "win32": print("ERROR: nur Windows") return 2 wins = count_aza_windows() procs = count_aza_processes() print(f"aza_window_count={len(wins)}") print(f"aza_process_count={len(procs)}") for hwnd, pid, title in wins: print(f" window: HWND={hwnd} PID={pid} title={title!r}") for pid, name in procs: if name in AZA_PROCESS_NAMES: print(f" process: PID={pid} name={name}") if args.check: bad = bool(wins) or bool(procs) if bad: print("SHUTDOWN_RESIDUAL_FAIL") return 1 print("SHUTDOWN_RESIDUAL_OK") return 0 return 0 if __name__ == "__main__": raise SystemExit(main())