I'm attempting to enumerate the available display monitors via Windows API's EnumDisplayMonitors. However, I'm getting some very weird behavior that I can't figure out. Namely, the function runs correctly, but only when it's not inside of a function.
If I place it at the module level like so:
Module Code
def _monitorEnumProc(hMonitor, hdcMonitor, lprcMonitor, dwData):
print 'call result:', hMonitor, hdcMonitor, lprcMonitor, dwData
if __name__ == '__main__':
# Callback Factory
MonitorEnumProc = WINFUNCTYPE(
ctypes.c_bool,
ctypes.wintypes.HMONITOR,
ctypes.wintypes.HDC,
ctypes.POINTER(RECT),
ctypes.wintypes.LPARAM
)
# Make the callback function
enum_callback = MonitorEnumProc(_monitorEnumProc)
# Enumerate the windows
print 'return code: %d' % windll.user32.EnumDisplayMonitors(
None,
None,
enum_callback,
0
)
Everything runs as expected. It prints out the handles
and rects
for my two attached monitors.
Output:
>>> call result: 65537 None <__main__.LP_RECT object at 0x02250EE0> 0
>>> call result: 65539 None <__main__.LP_RECT object at 0x02250EE0> 0
[Finished in 0.1s]
All is well. And the EnumDisplayMonitors
function returns a non-zero value showing that everything went as planned.
Now, here's the problem, if I stick the exact same code into a function, things go screwy.
Function Code
def _monitorEnumProc(hMonitor, hdcMonitor, lprcMonitor, dwData):
print 'call result:', hMonitor, hdcMonitor, lprcMonitor, dwData
def enum_mons():
# Callback Factory
MonitorEnumProc = WINFUNCTYPE(
ctypes.c_bool,
ctypes.wintypes.HMONITOR,
ctypes.wintypes.HDC,
ctypes.POINTER(RECT),
ctypes.wintypes.LPARAM
)
# Make the callback function
enum_callback = MonitorEnumProc(_monitorEnumProc)
# Enumerate the windows
print 'return code: %d' % windll.user32.EnumDisplayMonitors(
None,
None,
enum_callback,
0
)
if __name__ == '__main__':
enum_mons()
So, exact same code, except now inside of a function.
Output
call result: 65537 None <__main__.LP_RECT object at 0x02250E90> 0
0
Rather than spitting out all the attached monitors and a success code, it spits out only one monitor and a 0, which according to the MSDN doc means the function failed.
Anyone know what would cause this?It's got me stumped!
When you use a function,
enum_callback
is getting deallocated when the Python frame forenum_mons
is garbage collected. So it's a race as to whether it still exists when Windows tries to call it for each monitor. Define the callback globally -- or use a class.Also your callback should return True to continue the enumeration.