fix(ctypes): share wintypes.MSG with platform_detector to avoid argtype races

WindowsShutdownGuard was binding user32.GetMessageW.argtypes with
POINTER(_MSG) (project-local struct), while PlatformDetector's display-
power monitor binds it with POINTER(wintypes.MSG). argtypes is a
mutable global on the cached WinDLL handle, so whichever module
imported last won, and the other module's byref() then tripped
Python 3.13's strict argtype check with
"expected LP_MSG instance instead of pointer to _MSG".

The two structs are byte-identical (same field types in the same
order, just pt vs pt_x/pt_y naming) and we never touch the pt field,
so aliasing _MSG to wintypes.MSG eliminates the conflict — both
modules now bind the same POINTER class, the writes become idempotent,
and the full test suite passes regardless of import order.

CI runs on Linux so this never fired in release builds, but it broke
the local Windows test run.
This commit is contained in:
2026-05-28 17:36:19 +03:00
parent 1f959932c1
commit 0d840adfca
+9 -10
View File
@@ -101,16 +101,15 @@ class _WNDCLASS(ctypes.Structure):
] ]
class _MSG(ctypes.Structure): # Use the stdlib wintypes.MSG (rather than a project-local _MSG) so the
_fields_ = [ # POINTER(MSG) type is shared with any other module that binds
("hwnd", wintypes.HWND), # user32.GetMessageW.argtypes — argtypes is a global on the cached DLL
("message", wintypes.UINT), # handle, and two modules binding it with different POINTER classes for
("wParam", wintypes.WPARAM), # the same C function fight each other (last writer wins, the other one's
("lParam", wintypes.LPARAM), # byref() then trips Python 3.13's strict argtype check). PlatformDetector's
("time", wintypes.DWORD), # display-power monitor binds with POINTER(wintypes.MSG); aligning here
("pt_x", wintypes.LONG), # means whichever loads last produces the same binding.
("pt_y", wintypes.LONG), _MSG = wintypes.MSG
]
def _bind_winapi() -> None: def _bind_winapi() -> None: