Just posting a solution to the problem of capturing the whole virtual screen area, including all monitors with python and pywin32.
Because this doesn't work even with PIL's imagegrab module.
Code:
import win32gui, win32ui, win32con, win32api
hwnd = win32gui.GetDesktopWindow()
print hwnd
# you can use this to capture only a specific window
#l, t, r, b = win32gui.GetWindowRect(hwnd)
#w = r - l
#h = b - t
# get complete virtual screen including all monitors
SM_XVIRTUALSCREEN = 76
SM_YVIRTUALSCREEN = 77
SM_CXVIRTUALSCREEN = 78
SM_CYVIRTUALSCREEN = 79
w = vscreenwidth = win32api.GetSystemMetrics(SM_CXVIRTUALSCREEN)
h = vscreenheigth = win32api.GetSystemMetrics(SM_CYVIRTUALSCREEN)
l = vscreenx = win32api.GetSystemMetrics(SM_XVIRTUALSCREEN)
t = vscreeny = win32api.GetSystemMetrics(SM_YVIRTUALSCREEN)
r = l + w
b = t + h
print l, t, r, b, ' -> ', w, h
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
saveDC.SelectObject(saveBitMap)
saveDC.BitBlt((0, 0), (w, h), mfcDC, (l, t), win32con.SRCCOPY)
saveBitMap.SaveBitmapFile(saveDC, 'screencapture.bmp')
import Image
im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
#im.save('screencapture.jpg', format = 'jpeg', quality = 85)
im.save('screencapture.png', format = 'png')
r, b = context.GetSize()
# i have a second monitor left of my primary, so these value are negativ
l, t = (-1280, -256) # coulfn't find a wx function to get these
w, h = (r - l, b - t)
bitmap = wx.EmptyBitmap(w, h, -1)
memory = wx.MemoryDC()
memory.SelectObject(bitmap)
memory.Blit(0, 0, w, h, context, l, t)
memory.SelectObject(wx.NullBitmap)
#bitmap.SaveFile("screencapture.bmp", wx.BITMAP_TYPE_BMP)
#bitmap.SaveFile("screencapture.jpg", wx.BITMAP_TYPE_JPEG)
bitmap.SaveFile("screencapture.png", wx.BITMAP_TYPE_PNG)
First, many thanks to nobugus for this post, I had been hunting around for a way to do this for a while now, and this post was a huge boon!
It's worth noting though, that the code as is in the first post has a memory leak, and that if you wrap his code in a function take enough screenshots it will eventually crash with a windows error code 8. (On my system, this happens somewhere between 100 and 200 screenshots).
Adding the following after saving the screenshot (or doing whatever you like with it) seems to close the leak:
Code:
saveDC.DeleteDC()
win32gui.DeleteObject(saveBitMap.GetHandle())
I don't understand the details of why python/pywin32 garbage collection isn't handling this when the variables leave scope, but I thought I'd post this in case anyone else had the same problem. I experimented with leaving out just one line or the other, but both seem necessary. If anyone wants to explain to me why this works, I'm all ears!