PyWin32 cannot capture certain windows, resulting in black screen (Python, Windows)

Thanks to @wchill on Github for finding this solution (link)! Available on Windows, works with hardware acceleration and windows obscured by other windows.

import cv2
import numpy as np
from ctypes import windll
import win32gui
import win32ui


def capture_win_alt(hwnd=None, bmpFileName='hwnd.bmp', jpgFileName='hwnd.png', delete=False,
                    program_windowTitle='Swapface', program_className='Chrome_WidgetWin_1'):
    # Adapted from https://stackoverflow.com/questions/19695214/screenshot-of-inactive-window-printwindow-win32gui
    windll.user32.SetProcessDPIAware()
    if not hwnd:
        hwnd = win32gui.FindWindow(None, program_windowTitle)
        # hwnd = win32gui.FindWindowEx(0, 0, program_className, program_windowTitle)
    logger.info(f'hwnd: {hwnd}')
    if not hwnd:
        return None
    left, top, right, bottom = win32gui.GetClientRect(hwnd)
    w = right - left
    h = bottom - top
    hwnd_dc = win32gui.GetWindowDC(hwnd)
    mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
    save_dc = mfc_dc.CreateCompatibleDC()
    bitmap = win32ui.CreateBitmap()
    bitmap.CreateCompatibleBitmap(mfc_dc, w, h)
    save_dc.SelectObject(bitmap)
    # If Special K is running, this number is 3. If not, 1
    result = windll.user32.PrintWindow(hwnd, save_dc.GetSafeHdc(), 3)
    bmpinfo = bitmap.GetInfo()
    bmpstr = bitmap.GetBitmapBits(True)
    img = np.frombuffer(bmpstr, dtype=np.uint8).reshape((bmpinfo["bmHeight"], bmpinfo["bmWidth"], 4))
    img = np.ascontiguousarray(img)[..., :-1]  # make image C_CONTIGUOUS and drop alpha channel
    if not result:  # result should be 1
        win32gui.DeleteObject(bitmap.GetHandle())
        save_dc.DeleteDC()
        mfc_dc.DeleteDC()
        win32gui.ReleaseDC(hwnd, hwnd_dc)
        raise RuntimeError(f"Unable to acquire screenshot! Result: {result}")
    cv2.imwrite(jpgFileName, img)
    return img


def main():
    WINDOW_NAME = "Swapface"
    while cv2.waitKey(1) != ord('q'):
        screenshot = capture_win_alt(program_windowTitle=WINDOW_NAME)
        if screenshot is not None:
            print(screenshot.shape)


if __name__ == '__main__':
    main()

Guess you like

Origin blog.csdn.net/qq_41879696/article/details/132045700