Search code examples
pythonwinapipywin32win32gui

How to properly use win32gui.ReleaseDC and what does it do (python, pywin32)?


I am new to this and I can't figure out which code is correct and fast? All options are working, but then I can’t understand the difference in behavior, please help me figure it out. How does win32gui.ReleaseDC work and is it necessary to use it if everything works without it? Whether to delete objects.

Examples of getting the color of a pixel:
Variant #1

def get_pixel_rgb_color(self, pos: Tuple[int, int]) -> Tuple[int, int, int]:
        rect = win32gui.GetWindowRect(self._hwnd)
        w = abs(rect[2] - rect[0])
        h = abs(rect[3] - rect[1])

        hwndDC = win32gui.GetWindowDC(self._hwnd)
        mfcDC = win32ui.CreateDCFromHandle(hwndDC)
        saveDC = mfcDC.CreateCompatibleDC()
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
        saveDC.SelectObject(saveBitMap)

        ret = win32gui.GetPixel(hwndDC, pos[0], pos[1])
        r, g, b = ret & 0xFF, (ret >> 8) & 0xFF, (ret >> 16) & 0xFF

        return r, g, b

Variant #2

def get_pixel_rgb_color(self, pos: Tuple[int, int]) -> Tuple[int, int, int]:
        hwndDC = win32gui.GetWindowDC(self._hwnd)

        ret = win32gui.GetPixel(hwndDC, pos[0], pos[1])
        r, g, b = ret & 0xFF, (ret >> 8) & 0xFF, (ret >> 16) & 0xFF

        return r, g, b

Examples of area screenshot:
Variant #1

def area_screenshot(self, start: Tuple[int, int], size: Tuple[int, int]) -> str:
        width, height = size[0], size[1]
        x_cord, y_cord = start[0], start[1]

        hwnd_dc = win32gui.GetWindowDC(self._hwnd)
        mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
        dc_obj = mfc_dc.CreateCompatibleDC()
        bmp = win32ui.CreateBitmap()
        bmp.CreateCompatibleBitmap(mfc_dc, width, height)

        dc_obj.SelectObject(bmp)
        dc_obj.BitBlt(
            (0, 0), (width, height), mfc_dc, (x_cord, y_cord), win32con.SRCCOPY
        )

        signed_ints_arr = bmp.GetBitmapBits(True)
        bmp_arr = np.fromstring(signed_ints_arr, dtype=np.uint8)
        bmp_info = bmp.GetInfo()
        pil_im = Image.frombuffer(
            "RGB",
            (bmp_info["bmWidth"], bmp_info["bmHeight"]),
            bmp_arr,
            "raw",
            "BGRX",
            0,
            1,
        )
        buff = BytesIO()
        pil_im.save(buff, format="JPEG")
        img_b64 = b64encode(buff.getvalue()).decode("utf-8")

        mfc_dc.DeleteDC()
        dc_obj.DeleteDC()
        win32gui.ReleaseDC(self._hwnd, hwnd_dc)
        win32gui.DeleteObject(bmp.GetHandle())

        return img_b64

Variant #2

def area_screenshot(self, start: Tuple[int, int], size: Tuple[int, int]) -> str:
        width, height = size[0], size[1]
        x_cord, y_cord = start[0], start[1]

        hwnd_dc = win32gui.GetWindowDC(self._hwnd)
        mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
        dc_obj = mfc_dc.CreateCompatibleDC()
        bmp = win32ui.CreateBitmap()
        bmp.CreateCompatibleBitmap(mfc_dc, width, height)

        dc_obj.SelectObject(bmp)
        dc_obj.BitBlt(
            (0, 0), (width, height), mfc_dc, (x_cord, y_cord), win32con.SRCCOPY
        )

        signed_ints_arr = bmp.GetBitmapBits(True)
        bmp_arr = np.fromstring(signed_ints_arr, dtype=np.uint8)
        bmp_info = bmp.GetInfo()
        pil_im = Image.frombuffer(
            "RGB",
            (bmp_info["bmWidth"], bmp_info["bmHeight"]),
            bmp_arr,
            "raw",
            "BGRX",
            0,
            1,
        )
        buff = BytesIO()
        pil_im.save(buff, format="JPEG")
        img_b64 = b64encode(buff.getvalue()).decode("utf-8")

        return img_b64

Solution

  • The official documentation states that ReleaseDC must be called after using GetWindowDC or GetDC.

    The application must call the ReleaseDC function for each call to the GetWindowDC function and for each call to the GetDC function that retrieves a common DC.

    An application cannot use the ReleaseDC function to release a DC that was created by calling the CreateDC function; instead, it must use the DeleteDC function. ReleaseDC must be called from the same thread that called GetDC.

    If you don't release it, your program will keep occupying this GDI resource, which will cause memory leaks.