pythonwinapicoordinatesscreenshotbitblt

CreateDCFromHandle select object TOP coordinate in Python


I need to capture a specific application window on Windows, even if it is not focused or in the foreground, WITHOUT the window's top header bar, which height I specify manually in 'application_window_topbar_size' variable to keep it simple.

monitor_info = GetMonitorInfo(MonitorFromPoint((0,0))) # get screen information
work_area = monitor_info.get("Work")
screen_width = work_area[2] # if taskbar is on the left
screen_height = work_area[3] # if taskbar is on the left

target_window_hwnd = win32gui.FindWindow(None, ("Math Analysis - Google Chrome"))
application_window_topbar_size = 200 # just roughly for testing

    hwndDC = win32gui.GetWindowDC(target_window_hwnd) 
    mfcDC  = win32ui.CreateDCFromHandle(hwndDC)    
    saveDC = mfcDC.CreateCompatibleDC()
    saveBitMap = win32ui.CreateBitmap()
    saveBitMap.CreateCompatibleBitmap(mfcDC, screen_width, screen_height)
    saveDC.SelectObject(saveBitMap)

#    saveDC.BitBlt((0, 0), (screen_width, screen_height), mfcDC, (0, application_window_topbar_size),win32con.SRCCOPY)
    
    result = windll.user32.PrintWindow(target_window_hwnd, saveDC.GetSafeHdc(), 3)
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)
    screen_image = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(target_window_hwnd, hwndDC)

The capture routine should works for windows handle "WM_PRINT" (thanks for the comment of IInspectable), BUT I am unable to figure it out how to set the TOP coordinate of the capture area. In other words, I would like to capture the target window without the top bar. Changing the height is one thing, as logically, if the capture rectangle coordinates start at 0,0 then it cuts only the bottom area of the window, not the top. I tried to use BitBlt() but I failed.

----------------- Alternative version below based on IInspectable comment

target_window_hwnd = win32gui.FindWindow(None, ("Math Analysis - Google Chrome"))
application_window_topbar_size = 200 # just roughly for testing

    hwndDC = win32gui.GetWindowDC(target_window_hwnd) 
    mfcDC  = win32ui.CreateDCFromHandle(hwndDC)    
    saveDC = mfcDC.CreateCompatibleDC()
    saveBitMap = win32ui.CreateBitmap()

    l, t, r, b = win32gui.GetClientRect(target_window_hwnd)
    screen_width = r - l
    screen_height = b -t

    saveBitMap.CreateCompatibleBitmap(mfcDC, screen_width, screen_height)
    saveDC.SelectObject(saveBitMap)   
    result = windll.user32.PrintWindow(target_window_hwnd, saveDC.GetSafeHdc(), 3)
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)
    screen_image = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(target_window_hwnd, hwndDC)

Still, altering top coordinate only effect height, not captured content (to be able to capture only the 'document area' of the window, excluding the address bar/toolbar).


Solution

  • After long search and trials, I realized that the most obvious solution is to CROP the screenshot after it made. This way, the unwanted part (in my case the top bar) can be easily get rid off.

    Cropping is easy with PIL Image:

    box = (left, top, right, bottom)
    screen_image.crop(box)