20

After hours of googling I managed to "write" this:

import win32gui
from ctypes import windll

hwnd = win32gui.FindWindow(None, 'Steam')

hdc = win32gui.GetDC(hwnd)
hdcMem = win32gui.CreateCompatibleDC(hdc)
    
hbitmap = win32ui.CreateBitmap()
hbitmap = win32gui.CreateCompatibleBitmap(hdcMem, 500, 500)
    
win32gui.SelectObject(hdcMem, hbitmap)
    
windll.user32.PrintWindow(hwnd, hdcMem, 0)

Is this a correct way to do this and how would I save an image?

martineau
  • 119,623
  • 25
  • 170
  • 301
Maksim
  • 357
  • 1
  • 3
  • 11

1 Answers1

66

After lots of searching and trying various different methods, the following worked for me.

import win32gui
import win32ui
from ctypes import windll
import Image

hwnd = win32gui.FindWindow(None, 'Calculator')

# Change the line below depending on whether you want the whole window
# or just the client area. 
#left, top, right, bot = win32gui.GetClientRect(hwnd)
left, top, right, bot = win32gui.GetWindowRect(hwnd)
w = right - left
h = bot - top

hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC  = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()

saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)

saveDC.SelectObject(saveBitMap)

# Change the line below depending on whether you want the whole window
# or just the client area. 
#result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 1)
result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)
print result

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(hwnd, hwndDC)

if result == 1:
    #PrintWindow Succeeded
    im.save("test.png")
hazzey
  • 1,179
  • 25
  • 29
  • 4
    Nice Answer! I wasted last two hours trying to port the exactly same C++ code to Python only to find out you have already done it. Would you mind explaining in greater detail what each line is doing? – d34th4ck3r Jun 27 '17 at 09:08
  • 3
    It isn't working for me. I'm using it in Python 3.7x and I did made some adaptations, but in the end all I got is empty black screenshots of what supposed to be the calculator. Any help? – Mitrek Nov 05 '18 at 03:45
  • @Mitrek This was originally done in 2.7, so things certainly may have changed. – hazzey Nov 05 '18 at 04:06
  • 1
    Calculator has turned into a Universal Windows app - these aren't screenshottable with this API, try with notepad and it will work. – Stuart Axon Dec 07 '18 at 11:36
  • 2
    @hazzey Hello, I managed to get this program to display notepad properly but when using any other pogram, it shows the window borders buts inside of it is just black, any ideas? – Brandalf Jan 17 '19 at 15:29
  • 2
    As mentioned in another comment, this may be because the program that you are trying to use is a Universal Windows App. If you can get a subset of programs to work, that is likely the reason. – hazzey Jan 17 '19 at 15:52
  • 16
    Now in 2019 : Use `from PIL import Image` instead of `import Image` – Heetola Mar 27 '19 at 10:26
  • Doesn't work for me, OBS can record it but this code is black – Glass Cannon Apr 08 '20 at 16:59
  • Try to remove the alpha channel, as it might cause some pixels to be black. – imriqwe Apr 25 '20 at 11:34
  • 10
    @Brandalf in case you are getting black box, try different number in PrintWindow. In my case I found that `windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 3)` prints window contents. – Ignas2526 Nov 29 '20 at 08:48
  • 2
    @Brandalf ! Your solution was really very helpful. I changed the window number to 2, and it worked for me for Chrome window ; thanks a lot !!! – Nouman Ahsan Feb 02 '21 at 13:11
  • For those who use a high DPI display (and > 100% scaling size), use `windll.user32.SetProcessDPIAware()` before `win32gui.GetClientRect(hwnd)`/`win32gui.GetWindowRect(hwnd)` to get the correct weight/height. – weeix Sep 25 '21 at 03:58