Search code examples
pythonwinapiscreenshot

Screenshots taken with Pywin32 some times get a black imgaes, I think that handle ins't right


def window_full_shot(hwnd, gray=0):
    hwnd = get_hwnd(hwnd)[0]
    print(hwnd)
    print(win32gui.GetClassName(hwnd))
    l, t, r, b = win32gui.GetWindowRect(hwnd)
    h = b - t
    w = r - l
    hwindc = win32gui.GetWindowDC(hwnd)
    srcdc = win32ui.CreateDCFromHandle(hwindc)
    memdc = srcdc.CreateCompatibleDC()
    bmp = win32ui.CreateBitmap()
    bmp.CreateCompatibleBitmap(srcdc, w, h)
    memdc.SelectObject(bmp)
    memdc.BitBlt((0, 0), (w, h), srcdc, (0, 0), win32con.SRCCOPY)

    signedIntsArray = bmp.GetBitmapBits(True)
    img = np.fromstring(signedIntsArray, dtype='uint8')
    img.shape = (h, w, 4)
    print(type(img))
    srcdc.DeleteDC()
    memdc.DeleteDC()
    win32gui.ReleaseDC(hwnd, hwindc)
    win32gui.DeleteObject(bmp.GetHandle())
    if gray == 0:
        return cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
    else:
        return cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)

I use this code to shot two windows, one could got a right img, another it ins't work, just like that. the right code the bad code


Solution

  • Applications made with frameworks such as electron app, QT, WPF... will print black screen in response to GetDC or GetWindowDC.

    The only way around it is to make sure the target application is visible and take a screenshot of desktop at the specific coordinates where the target application is at.

    Windows GDI functions generally ignore alpha channel. But if you retrieve the screenshot in 32-bit, then GetDIBits will set the all alpha values set to 255 (at least in Windows 10).

    About code sample(C++), please refer: https://stackoverflow.com/a/53404526/11128312

    Python code:

    import win32gui
    import win32ui
    import win32con
    from ctypes import windll
    from PIL import Image
    import time
    import ctypes
    
    hwnd_target = win32gui.FindWindow(None, 'Calculator') # used for test 
    
    left, top, right, bot = win32gui.GetWindowRect(hwnd_target)
    w = right - left
    h = bot - top
    
    win32gui.SetForegroundWindow(hwnd_target)
    time.sleep(1.0)
    
    hdesktop = win32gui.GetDesktopWindow()
    hwndDC = win32gui.GetWindowDC(hdesktop)
    mfcDC  = win32ui.CreateDCFromHandle(hwndDC)
    saveDC = mfcDC.CreateCompatibleDC()
    
    saveBitMap = win32ui.CreateBitmap()
    saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
    
    saveDC.SelectObject(saveBitMap)
    
    result = saveDC.BitBlt((0, 0), (w, h), mfcDC, (left, top), win32con.SRCCOPY)
    
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)
    
    im = Image.frombuffer(
        'RGB',
        (bmpinfo['bmWidth'], bmpinfo['bmHeight']),
        bmpstr, 'raw', 'BGRX', 0, 1)
    
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(hdesktop, hwndDC)
    
    if result == None:
        #PrintWindow Succeeded
        im.save("test.png")