Pyautogui and pyscreeze crash with windll.user32.ReleaseDC failed

I'm trying to compare certain pixel values in my pyautogui script, but it crashes with following error message after either multiple successful runs, or sometimes just straight on the first call:

Traceback (most recent call last):
  File "F:\Koodit\Python\HeroWars NNet\Assets\autodataGet.py", line 219, in <module>
    battle = observeBattle()
  File "F:\Koodit\Python\HeroWars NNet\Assets\autodataGet.py", line 180, in observeBattle
    statii = getHeroBattlePixels()
  File "F:\Koodit\Python\HeroWars NNet\Assets\autodataGet.py", line 32, in getHeroBattlePixels
    colormatch = pyautogui.pixelMatchesColor(location[0], location[1], alive, tolerance=5)
  File "E:\Program Files\Python\lib\site-packages\pyscreeze\__init__.py", line 557, in pixelMatchesColor
    pix = pixel(x, y)
  File "E:\Program Files\Python\lib\site-packages\pyscreeze\__init__.py", line 582, in pixel
    return (r, g, b)
  File "E:\Program Files\Python\lib\contextlib.py", line 120, in __exit__
    next(self.gen)
  File "E:\Program Files\Python\lib\site-packages\pyscreeze\__init__.py", line 111, in __win32_openDC
    raise WindowsError("windll.user32.ReleaseDC failed : return 0")
OSError: windll.user32.ReleaseDC failed : return 0

My code (this is called multiple times, sometimes it crashes on first run, sometimes it runs nicely for around 100 calls before failing, also, my screen is 4K, so the resolutions get big):

def getSomePixelStatuses():
    someLocations= [
                        [1200, 990],
                        [1300, 990],
                        [1400, 990],
                        [1500, 990],
                        [1602, 990],
                        [1768, 990],
                        [1868, 990],
                        [1968, 990],
                        [2068, 990],
                        [2169, 990]
                        ]
    status = []
    someValue= (92, 13, 12)
    for location in someLocations:
        colormatch = pyautogui.pixelMatchesColor(location[0], location[1], someValue, tolerance=5)
        status.append(colormatch)
    return status

I have no idea how to mitigate this problem. It would seem that pyautogui uses pyscreeze to read pixel values on screen, and most probable candidate for the place where error occurs is the pyscreeze pixel function:

def pixel(x, y):
    """
    TODO
    """
    if sys.platform == 'win32':
        # On Windows, calling GetDC() and GetPixel() is twice as fast as using our screenshot() function.
        with __win32_openDC(0) as hdc: # handle will be released automatically
            color = windll.gdi32.GetPixel(hdc, x, y)
            if color < 0:
                raise WindowsError("windll.gdi32.GetPixel failed : return {}".format(color))
            # color is in the format 0xbbggrr https://msdn.microsoft.com/en-us/library/windows/desktop/dd183449(v=vs.85).aspx
            bbggrr = "{:0>6x}".format(color) # bbggrr => 'bbggrr' (hex)
            b, g, r = (int(bbggrr[i:i+2], 16) for i in range(0, 6, 2))
            return (r, g, b)
    else:
        # Need to select only the first three values of the color in
        # case the returned pixel has an alpha channel
        return RGB(*(screenshot().getpixel((x, y))[:3]))

I installed these libraries just yesterday, and I'm running python 3.8 on windows 10, and pyscreeze is version 0.1.25 so in theory everything should be up to date, but somehow something ends up crashing. Is there a way to mitigate this, either modifying my code, or even the library itself, or is my environment not suitable for this operation?


Solution 1:

Well I know it's not particularly helpful; but for me, this error was fixed simply by running my code on 3.7 instead of 3.8. There shouldn't be any changes you have to make to your code, however (unless you were using walrus!)

On Windows, this can be done with the -3.7 command line flag, as long as 3.7 is installed

Solution 2:

PyScreeze and PyAutoGUI maintainer here. This is an issue that has been fixed in PyScreeze 0.1.28, so you just need to update it by running pip install -U pyscreeze.

For more context, here's the GitHub issue where it was reported: https://github.com/asweigart/pyscreeze/pull/73

Solution 3:

It's a bug. You were on the right track, as the problem is indeed in this line of the pixel() function:

with __win32_openDC(0) as hdc

That function uses cyptes.windll which doesn't seem to do well with the negative values sometimes returned from windll.user32.GetDC(), which subsequently creates an exception when windll.user32.ReleaseDC() is called.

The folks at pillow helped track this down and propose a fix.

  • issue filed at pyautogui
  • issue filed at pillow which led to the solution
  • pending PR at pyscreeze to address