John Smith
John Smith

Reputation: 51

pywin32 win32gui GetPixel fails predictably near 10,000th call

When running the following block of code, GetPixel starts throwing errors at around the 10,000th call every time. If I put the code in a try-except statement and all calls after the initial faliure will fail. The only way to recover is to terminate the script and run it again. I have tried putting a brief sleep between the calls. I have tried triggering garbage collection between the calls. I have tried running the script from the IDE and as a standalone process. Any ideas? Even running the script and reporting if/when it fails would help.

import gc
import os
import time
import win32api, win32con, win32gui
import sys
import win32com.client

terms = 0

def get_pixel_colour(i_x, i_y):
    i_desktop_window_id = win32gui.GetDesktopWindow()
    i_desktop_window_dc = win32gui.GetWindowDC(i_desktop_window_id)
    long_colour = win32gui.GetPixel(i_desktop_window_dc, i_x, i_y)
    i_colour = int(long_colour)
    return (i_colour & 0xff), ((i_colour >> 8) & 0xff), ((i_colour >> 16) & 0xff)


for x in range (0, 1000000):
    colour = get_pixel_colour(500, 500)
    terms = terms + 1
    print terms

The error that is returned is

error: (0, 'GetPixel', 'No error message is available')

Upvotes: 5

Views: 3030

Answers (1)

Mike Housky
Mike Housky

Reputation: 4069

I suspect that resources aren't getting released when the i_desktop_windw_dc object is garbage-collected. Maybe there's a releaseDC call needed, but I couldn't find docs or the source code after a short search on my system after installing pywin32. I do see a ReleaseDC function in win32gui, and I can only guess that you're supposed to use that to release the device context that you got with via win32gui.GetWindowDC.

This may be a workaround:

import gc
import os
import time
import win32api, win32con, win32gui
import sys
import win32com.client

terms = 0
class DesktopWindow(object):
    def __init__(self, *args, **kwargs):
        self.window_id = win32gui.GetDesktopWindow()
        self.window_dc = win32gui.GetWindowDC(self.window_id)
        pass
    def get_pixel_color(self, i_x, i_y):
        long_colour = win32gui.GetPixel(self.window_dc, i_x, i_y)
        i_colour = int(long_colour)
        return (i_colour & 0xff, (i_colour >> 8) & 0xff,
                (i_colour >> 16) & 0xff)

dtop = DesktopWindow()

for x in range (0, 1000000):
    colour = dtop.get_pixel_color(500, 500)
    terms = terms + 1
    if terms % 100 == 0:
        print terms

That's happily passing 200,000 get_pixel_color operations here, as I type.

Also, try win32gui.ReleaseDC(i_desktop_window_id, i_desktop_window_dc) at the end of your standalone function, and see if that helps. (This is what you're supposed to do in WinAPI coding...release the DC as soon as you're done with it.)

Note: Above fixed...you need the window handle (hWnd=window id) and the device context handle (hDC) in a call to ReleaseDC. See http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920%28v=vs.85%29.aspx

Upvotes: 3

Related Questions