132 lines
3.6 KiB
Python
132 lines
3.6 KiB
Python
#!/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())
|