From e4d24a02daf983c9106e1e7aa6cb2f1b963464ee Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Thu, 28 May 2026 17:25:37 +0300 Subject: [PATCH] fix(ctypes): pin LPMSG across MSG-pump prototypes for Python 3.13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3.13's ctypes tightened argtypes checks and now rejects mismatched POINTER(MSG) cache entries — each call to POINTER(MSG) can return a class identity that doesn't match what byref() of an instance produces, raising "expected LP_MSG instance instead of pointer to _MSG" inside GetMessageW/TranslateMessage/DispatchMessageW. Capture POINTER(MSG) once into LPMSG and reuse the same class object across all three prototypes in both the WindowsShutdownGuard pump and the PlatformDetector display-power monitor. Restores the 4 failing win_shutdown tests. --- .../ledgrab/core/automations/platform_detector.py | 15 +++++++++++++++ server/src/ledgrab/utils/win_shutdown.py | 11 ++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/server/src/ledgrab/core/automations/platform_detector.py b/server/src/ledgrab/core/automations/platform_detector.py index c4f12ab..6f5eee4 100644 --- a/server/src/ledgrab/core/automations/platform_detector.py +++ b/server/src/ledgrab/core/automations/platform_detector.py @@ -84,6 +84,21 @@ class PlatformDetector: ] user32.DefWindowProcW.restype = ctypes.c_ssize_t + # Pin the MSG pointer type so byref(msg) matches the prototype + # (Python 3.13 ctypes rejects mismatched POINTER(MSG) caches). + LPMSG = ctypes.POINTER(ctypes.wintypes.MSG) + user32.GetMessageW.argtypes = [ + LPMSG, + ctypes.wintypes.HWND, + ctypes.c_uint, + ctypes.c_uint, + ] + user32.GetMessageW.restype = ctypes.c_int + user32.TranslateMessage.argtypes = [LPMSG] + user32.TranslateMessage.restype = ctypes.wintypes.BOOL + user32.DispatchMessageW.argtypes = [LPMSG] + user32.DispatchMessageW.restype = ctypes.c_ssize_t + def wnd_proc(hwnd, msg, wparam, lparam): if msg == WM_POWERBROADCAST and wparam == PBT_POWERSETTINGCHANGE: try: diff --git a/server/src/ledgrab/utils/win_shutdown.py b/server/src/ledgrab/utils/win_shutdown.py index d86935b..75def70 100644 --- a/server/src/ledgrab/utils/win_shutdown.py +++ b/server/src/ledgrab/utils/win_shutdown.py @@ -133,18 +133,23 @@ def _bind_winapi() -> None: ] user32.DefWindowProcW.restype = LRESULT + # Pin the MSG pointer type once and reuse the same class object on all + # three prototypes — Python 3.13 ctypes rejects mismatched POINTER(_MSG) + # caches across argtypes, even though they look identical. + LPMSG = ctypes.POINTER(_MSG) + user32.GetMessageW.argtypes = [ - ctypes.POINTER(_MSG), + LPMSG, wintypes.HWND, wintypes.UINT, wintypes.UINT, ] user32.GetMessageW.restype = wintypes.BOOL - user32.TranslateMessage.argtypes = [ctypes.POINTER(_MSG)] + user32.TranslateMessage.argtypes = [LPMSG] user32.TranslateMessage.restype = wintypes.BOOL - user32.DispatchMessageW.argtypes = [ctypes.POINTER(_MSG)] + user32.DispatchMessageW.argtypes = [LPMSG] user32.DispatchMessageW.restype = LRESULT user32.PostMessageW.argtypes = [