Importing pyautogui breaks monitor DPI detection with ctypes
GeorgeWashingtonABCDEFG opened this issue · 5 comments
import ctypes
# import pyautogui
dpix = ctypes.c_uint()
dpiy = ctypes.c_uint()
ctypes.windll.shcore.SetProcessDpiAwareness(2)
ctypes.windll.shcore.GetDpiForMonitor(1186359,0,ctypes.byref(dpix),ctypes.byref(dpiy))
print(dpix.value)
# output
144 # this is correct
import ctypes
import pyautogui
dpix = ctypes.c_uint()
dpiy = ctypes.c_uint()
ctypes.windll.shcore.SetProcessDpiAwareness(2)
ctypes.windll.shcore.GetDpiForMonitor(1186359,0,ctypes.byref(dpix),ctypes.byref(dpiy))
print(dpix.value)
# output
96 # this is wrong. The monitor's dpi is 144. Note the monitor is scaled in windows.
# Everything I do about detecting window size, location, etc is all broken by simply importing your module.
Here's something related:
"""
QtWarningMsg:
setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed:
COM error 0x5 (Access is denied.)
"""
import pyautogui # noqa # pylint:disable=unused-import
from PySide6.QtWidgets import QApplication
QApplication()
It outputs the warning
qt.qpa.windows: setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: COM error 0x5 (Access is denied.)
According to MS docs,
Possible errors are [..] ERROR_ACCESS_DENIED if the default API awareness mode for the process has already been set (via a previous API call or within the application manifest).
https://learn.microsoft.com/th-th/windows/win32/api/winuser/nf-winuser-setprocessdpiawarenesscontext
So it seems pyautogui
sets DPI awareness somewhere before. I find it in
pyautogui/pyautogui/_pyautogui_win.py
Lines 14 to 18 in ad7609c
I am considering monkey-patching ctypes
before importing pyautogui
to remove that call.
Here's a workaround:
"""Workaround."""
import ctypes
import pytest
from PySide6.QtWidgets import QApplication
with pytest.MonkeyPatch.context() as mp:
mp.setattr(ctypes.windll.user32, "SetProcessDPIAware", lambda: None)
import pyautogui # noqa # pylint:disable=unused-import
QApplication()
Much nicer workaround with the standard library:
"""Workaround."""
from unittest.mock import patch
from PySide6.QtWidgets import QApplication
with patch("ctypes.windll.user32.SetProcessDPIAware", autospec=True):
import pyautogui # noqa # pylint:disable=unused-import
QApplication()
Importantly, that Qt warning message will go away:
https://bugreports.qt.io/browse/PYSIDE-2105
This will solve my issue (and make my patch pointless), but it will not solve @GeorgeWashingtonABCDEFG's issue (for which my patch may still be useful).
In the end, pyautogui
should probably stop calling SetProcessDPIAware
unless necessary (and if so, call it as late as possible).
Related: 9cd04d3