非活动窗口 PrintWindow + win32gui 的屏幕截图
- 2025-04-10 09:45:00
- admin 原创
- 23
问题描述:
经过几个小时的谷歌搜索,我设法“写”了这个:
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)
这是正确的方法吗?我该如何保存图像?
解决方案 1:
经过大量搜索并尝试各种不同的方法后,以下方法对我有用。
import win32gui
import win32ui
from ctypes import windll
from PIL import Image
hwnd = win32gui.FindWindow(None, 'Calculator')
# Uncomment the following line if you use a high DPI display or >100% scaling size
# windll.user32.SetProcessDPIAware()
# 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")
解决方案 2:
这是我的更完整的脚本,它具有与 相同的效果ALT + PrtSc
。
它打印带有 1px 边框的整个窗口。(如果最大化则无边框)
我们可以像组合一样将其复制到剪贴板。
或者我们可以将其转换为 PIL 图像并保存。
import ctypes, win32con, win32gui
import win32clipboard as w32clip
from struct import pack, calcsize
from ctypes import windll, wintypes
from PIL import Image
user32,gdi32 = windll.user32,windll.gdi32
PW_RENDERFULLCONTENT = 2
def getWindowBMAP(hwnd,returnImage=False):
# get Window size and crop pos/size
L,T,R,B = win32gui.GetWindowRect(hwnd); W,H = R-L,B-T
x,y,w,h = (8,8,W-16,H-16) if user32.IsZoomed(hwnd) else (7,0,W-14,H-7)
# create dc's and bmp's
dc = user32.GetWindowDC(hwnd)
dc1,dc2 = gdi32.CreateCompatibleDC(dc),gdi32.CreateCompatibleDC(dc)
bmp1,bmp2 = gdi32.CreateCompatibleBitmap(dc,W,H),gdi32.CreateCompatibleBitmap(dc,w,h)
# render dc1 and dc2 (bmp1 and bmp2) (uncropped and cropped)
obj1,obj2 = gdi32.SelectObject(dc1,bmp1),gdi32.SelectObject(dc2,bmp2) # select bmp's into dc's
user32.PrintWindow(hwnd,dc1,PW_RENDERFULLCONTENT) # render window to dc1
gdi32.BitBlt(dc2,0,0,w,h,dc1,x,y,win32con.SRCCOPY) # copy dc1 (x,y,w,h) to dc2 (0,0,w,h)
gdi32.SelectObject(dc1,obj1); gdi32.SelectObject(dc2,obj2) # restore dc's default obj's
if returnImage: # create Image from bmp2
data = ctypes.create_string_buffer((w*4)*h)
bmi = ctypes.c_buffer(pack("IiiHHIIiiII",calcsize("IiiHHIIiiII"),w,-h,1,32,0,0,0,0,0,0))
gdi32.GetDIBits(dc2,bmp2,0,h,ctypes.byref(data),ctypes.byref(bmi),win32con.DIB_RGB_COLORS)
img = Image.frombuffer('RGB',(w,h),data,'raw','BGRX')
# clean up
gdi32.DeleteObject(bmp1) # delete bmp1 (uncropped)
gdi32.DeleteDC(dc1); gdi32.DeleteDC(dc2) # delete created dc's
user32.ReleaseDC(hwnd,dc) # release retrieved dc
return (bmp2,w,h,img) if returnImage else (bmp2,w,h)
def copyBitmap(hbmp): # copy HBITMAP to clipboard
w32clip.OpenClipboard(); w32clip.EmptyClipboard()
w32clip.SetClipboardData(w32clip.CF_BITMAP,hbmp); w32clip.CloseClipboard()
def copySnapshot(hwnd): # copy Window HBITMAP to clipboard
hbmp,w,h = getWindowBMAP(hwnd); copyBitmap(hbmp); gdi32.DeleteObject(hbmp)
def getSnapshot(hwnd): # get Window HBITMAP as Image
hbmp,w,h,img = getWindowBMAP(hwnd,True); gdi32.DeleteObject(hbmp); return img
hwnd = windll.kernel32.GetConsoleWindow() # Console
if not hwnd: hwnd = user32.FindWindowW("Notepad",None) # Notepad
if not hwnd: hwnd = user32.FindWindowW("Chrome_WidgetWin_1",None) # Chrome
if not hwnd: hwnd = user32.FindWindowW("CabinetWClass",None) # Windows Explorer
if not hwnd: print("Couldn't find Window")
elif user32.IsIconic(hwnd): print("Window is minimized")
else:
print(win32gui.GetWindowText(hwnd))
print(win32gui.GetClassName(hwnd))
copySnapshot(hwnd)
img = getSnapshot(hwnd)
img.save("snapshot.png")
#img.show()
print("Snapshot Saved!")
解释:
如果我们做了基本的PrintWindow
,它会呈现 Windows 7 样式的框架/边框,其左侧/右侧/底部的边框要厚 7 个像素。它也位于顶部,但仅在最大化时才如此。
我们可以使用隐藏标志PW_RENDERFULLCONTENT
来正确渲染,但额外的边框空间仍然存在并且保持空白。所以我们的图像被厚厚的黑色边框填充。
所以我们需要裁剪我们的 HBITMAP。
为此,我们可以创建一个新的位图并使用BitBlt
第一个位图的DC作为源。
从这里我们可以win32clipboard
像 一样复制 HBITMAP ALT + PrtSc
。
HBITMAP 到 PIL:
我们可以用来GetDIBits
获取大小数组中的原始像素数据w*h*4
。
它需要结构来BITMAPINFOHEADER
接收width
、、、height
。planes
`bitCount`
一种方法是:
class BITMAPINFOHEADER(ctypes.Structure):
_fields_ = [
("biSize", wintypes.DWORD),
("biWidth", ctypes.c_long),
("biHeight", ctypes.c_long),
("biPlanes", wintypes.WORD),
("biBitCount", wintypes.WORD),
("biCompression", wintypes.DWORD),
("biSizeImage", wintypes.DWORD),
("biXPelsPerMeter", ctypes.c_long),
("biYPelsPerMeter", ctypes.c_long),
("biClrUsed", wintypes.DWORD),
("biClrImportant", wintypes.DWORD)
]
def __init__(self,w,h,p=1,b=32):
self.biSize = ctypes.sizeof(BITMAPINFOHEADER)
self.biWidth,self.biHeight,self.biPlanes,self.biBitCount = w,h,p,b
bmi = BITMAPINFOHEADER(w,-h) # we use -h to flip the image also
但更紧凑的方式是:
bmi = ctypes.c_buffer(pack("IiiHHIIiiII",calcsize("IiiHHIIiiII"),w,-h,1,32,0,0,0,0,0,0))
我们用来struct
将值打包为特定的数据类型。
i = int (c_long)
I = unsigned int (DWORD)
H = unsigned short (WORD)
我们用它calcsize("IiiHHIIiiII")
来做相当于的事情ctypes.sizeof(BITMAPINFOHEADER)
。
以下是如何获取客户端的方法:
PW_CLIENTONLY,PW_RENDERFULLCONTENT = 1,2
def getClientBMAP(hwnd,returnImage=False):
# get Client size
L,T,R,B = win32gui.GetClientRect(hwnd); w,h = R-L,B-T
# create dc's and bmp's
dc = user32.GetWindowDC(hwnd)
dc1 = gdi32.CreateCompatibleDC(dc)
bmp1 = gdi32.CreateCompatibleBitmap(dc,w,h)
# render dc1 (bmp1)
obj1 = gdi32.SelectObject(dc1,bmp1) # select bmp into dc
user32.PrintWindow(hwnd,dc1,PW_CLIENTONLY|PW_RENDERFULLCONTENT) # render window to dc1
gdi32.SelectObject(dc1,obj1) # restore dc's default obj
if returnImage: # create Image from bmp1
data = ctypes.create_string_buffer((w*4)*h)
bmi = ctypes.c_buffer(pack("IiiHHIIiiII",calcsize("IiiHHIIiiII"),w,-h,1,32,0,0,0,0,0,0))
gdi32.GetDIBits(dc1,bmp1,0,h,ctypes.byref(data),ctypes.byref(bmi),win32con.DIB_RGB_COLORS)
img = Image.frombuffer('RGB',(w,h),data,'raw','BGRX')
# clean up
gdi32.DeleteDC(dc1) # delete created dc
user32.ReleaseDC(hwnd,dc) # release retrieved dc
return (bmp1,w,h,img) if returnImage else (bmp1,w,h)
def getClientSnapshot(hwnd): # get Client HBITMAP as Image
hbmp,w,h,img = getClientBMAP(hwnd,True); gdi32.DeleteObject(hbmp); return img
相关推荐
热门文章
项目管理软件有哪些?
热门标签
曾咪二维码
扫码咨询,免费领取项目管理大礼包!
云禅道AD