-1

I want to make a screenshot of an inactive window with win32gui.

from PIL import ImageGrab
import win32gui

hwnd = win32gui.FindWindow(None, 'Calculator')
win32gui.SetForegroundWindow(hwnd)
dimensions = win32gui.GetWindowRect(hwnd)

image = ImageGrab.grab(dimensions)
image.show()

But it is not working as expected. It grabs a screen region but it does not grab exactly where the window is. I have also tried resizing the calculator window but it doesn't help. My screen resolution is 1920*1080 if that is relevant.

Screenshot:

Calculator attempt.

Please help me if you have any solutions.

Mick
  • 811
  • 14
  • 31
  • Possible duplicate of [Using ImageGrab with bbox from pywin32's GetWindowRect](https://stackoverflow.com/questions/51786794/using-imagegrab-with-bbox-from-pywin32s-getwindowrect) – Mick Sep 13 '18 at 09:53

1 Answers1

2

Part of the problem here is the DPI settings described in Using ImageGrab with bbox from pywin32's GetWindowRect, but there will still be extra space around the rectangle returned by win32gui.GetClientRect(hwnd) thanks to a feature of Windows 10 described in GetWindowRect too small on Windows 7, which, from what I have read, started appearing with Windows 8 and Aero.

So, for completeness, here is a solution:

# imports
import win32gui, win32con, ctypes  
from PIL import ImageGrab
from ctypes import wintypes

# this takes care of the DPI settings (https://stackoverflow.com/questions/51786794/using-imagegrab-with-bbox-from-pywin32s-getwindowrect)
ctypes.windll.user32.SetProcessDPIAware()

# get window handle and dimensions 
hwnd = win32gui.FindWindow(None, 'Calculator')
dimensions = win32gui.GetWindowRect(hwnd)    

# this gets the window size, comparing it to `dimensions` will show a difference
winsize = win32gui.GetClientRect(hwnd)

# this sets window to front if it is not already
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST,0,0,0,0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST,0,0,0,0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST,0,0,0,0, win32con.SWP_SHOWWINDOW | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)

# grab screen region set in `dimensions`
image = ImageGrab.grab(dimensions)
image.show()

# we're going to use this to get window attributes
f=ctypes.windll.dwmapi.DwmGetWindowAttribute

# `rect` is for the window coordinates
rect = ctypes.wintypes.RECT()
DWMWA_EXTENDED_FRAME_BOUNDS = 9

# and then the coordinates of the window go into `rect`
f(ctypes.wintypes.HWND(hwnd),
  ctypes.wintypes.DWORD(DWMWA_EXTENDED_FRAME_BOUNDS),
  ctypes.byref(rect),
  ctypes.sizeof(rect)
  )

# if we want to work out the window size, for comparison this should be the same as `winsize`
size = (rect.right - rect.left, rect.bottom - rect.top)        

# put the window coordinates in a tuple like that returned earlier by GetWindowRect()
dimensions = (rect.left, rect.top, rect.right, rect.bottom)

# grab screen region set in the revised `dimensions`
image = ImageGrab.grab(dimensions)
image.show()

And then image should have the correct boundaries.

Mick
  • 811
  • 14
  • 31