From bfe6a7a2abe9516cf972a85bac11bea012283412 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Thu, 19 Feb 2026 22:30:22 +0300 Subject: [PATCH] Replace WMI process enumeration with Win32 EnumProcesses (350x faster) Use PROCESS_QUERY_LIMITED_INFORMATION + QueryFullProcessImageNameW instead of WMI Win32_Process. Reduces process enumeration from ~3s to ~8ms. All user-facing applications are detected; only protected system services are not visible (irrelevant for profile conditions). Co-Authored-By: Claude Opus 4.6 --- .../core/profiles/platform_detector.py | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/server/src/wled_controller/core/profiles/platform_detector.py b/server/src/wled_controller/core/profiles/platform_detector.py index 6079305..d5891a8 100644 --- a/server/src/wled_controller/core/profiles/platform_detector.py +++ b/server/src/wled_controller/core/profiles/platform_detector.py @@ -22,19 +22,53 @@ class PlatformDetector: """Detect running processes and the foreground window's process.""" def _get_running_processes_sync(self) -> Set[str]: - """Get set of lowercase process names (blocking, call via executor).""" + """Get set of lowercase process names via Win32 EnumProcesses. + + Uses PROCESS_QUERY_LIMITED_INFORMATION + QueryFullProcessImageNameW + which is ~300x faster than WMI (~8ms vs ~3s). System services + running under protected accounts are not visible, but all + user-facing applications are covered. + """ if not _IS_WINDOWS: return set() try: - import pythoncom - pythoncom.CoInitialize() - try: - import wmi - w = wmi.WMI() - return {p.Name.lower() for p in w.Win32_Process() if p.Name} - finally: - pythoncom.CoUninitialize() + psapi = ctypes.windll.psapi + kernel32 = ctypes.windll.kernel32 + + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + + # Enumerate all PIDs + pid_array = (ctypes.wintypes.DWORD * 2048)() + cb_needed = ctypes.wintypes.DWORD() + psapi.EnumProcesses( + ctypes.byref(pid_array), ctypes.sizeof(pid_array), + ctypes.byref(cb_needed), + ) + n_pids = cb_needed.value // ctypes.sizeof(ctypes.wintypes.DWORD) + + procs: Set[str] = set() + name_buf = ctypes.create_unicode_buffer(512) + + for i in range(n_pids): + pid = pid_array[i] + if pid == 0: + continue + handle = kernel32.OpenProcess( + PROCESS_QUERY_LIMITED_INFORMATION, False, pid, + ) + if not handle: + continue + try: + buf_size = ctypes.wintypes.DWORD(512) + if kernel32.QueryFullProcessImageNameW( + handle, 0, name_buf, ctypes.byref(buf_size), + ): + procs.add(os.path.basename(name_buf.value).lower()) + finally: + kernel32.CloseHandle(handle) + + return procs except Exception as e: logger.error(f"Failed to enumerate processes: {e}") return set()