非活动窗口 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 = win32g...

问题描述:

经过几个小时的谷歌搜索,我设法“写”了这个:

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、、、heightplanes`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
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用