仅针对某些应用程序使用winAPI BitBlt捕获窗口故障

时间:2013-12-16 00:45:48

标签: python windows winapi screenshot pywin32

我已经整理了一个简单的python脚本,它应该截取一个名称中包含特定字符串的窗口的截图。我使用的代码如下,


import win32gui, win32ui, win32con
import PIL.Image

def getWindowHandle(name):        
    windowList = []
    win32gui.EnumWindows(lambda hwnd, wndList:
                             wndList.append((win32gui.GetWindowText(hwnd), hwnd)),
                         windowList)

    for pair in windowList:
        if name in pair[0]:
            return pair[1]

    return None


class Window():
    def __init__(self, hwnd = None):
        if not hwnd: return

        l, t, r, b   = win32gui.GetClientRect(hwnd)
        sl, st, _, _ = win32gui.GetWindowRect(hwnd)
        cl, ct       = win32gui.ClientToScreen(hwnd, (l, t))

        self.size     = (r - l, b - t)
        self.position = (cl - sl, ct - st)

        hDC   = win32gui.GetWindowDC(hwnd)
        self.windowDC  = win32ui.CreateDCFromHandle(hDC)
        self.newDC = self.windowDC.CreateCompatibleDC()

        #win32gui.ReleaseDC(hwnd, hDC)

        self.bitmap = win32ui.CreateBitmap()
        self.bitmap.CreateCompatibleBitmap(self.windowDC, self.size[0], self.size[1])
        self.newDC.SelectObject(self.bitmap)

    def __del__(self):
        self.newDC.DeleteDC()
        self.windowDC.DeleteDC()
        del self.bitmap

    def screenshot(self, location = 'C:\\Users\\Grieverheart\\Desktop\\'):
        self.newDC.BitBlt((0, 0), self.size, self.windowDC, self.position, win32con.SRCCOPY)
        self.bitmap.Paint(self.newDC)

        bmpinfo = self.bitmap.GetInfo()
        bmpstr  = self.bitmap.GetBitmapBits(True)
        im = PIL.Image.frombuffer('RGB', self.size, bmpstr, 'raw', 'BGRX', 0, 1)
        try:
            im.save(location + 'test.png', 'PNG')
        except IOError:
            return


def main():
    handle = getWindowHandle("Blender")
    if not handle: return

    window = Window(handle)
    window.screenshot()

if __name__ == "__main__":
    main()

该脚本为我提供了一些应用程序的黑屏幕截图,例如Blender或DOSBox。

是否有人知道仅针对某些应用程序导致此问题的原因以及我如何解决该问题?

编辑:似乎我的问题可能与this帖子有关,但我不确定要解决我的问题需要做些什么。我还想补充一点,我也尝试过添加CAPTUREBLT标志而没有任何差异。

1 个答案:

答案 0 :(得分:2)

来自MSDN

  

如果源设备和目标设备上下文代表不同的设备,则BitBlt会返回错误。要在不同设备的DC之间传输数据,请通过调用GetDIBits将存储器位图转换为DIB。要将DIB显示给第二个设备,请调用SetDIBits或StretchDIBits。

这是什么意思?简而言之,混合DWM(即Aero)和非GDI应用程序(例如OpenGL)会使BiBlt变得不可预测/不可靠。

再次来自MSDN

  

Windows Vista中引入的桌面合成功能从根本上改变了应用程序在屏幕上显示像素的方式。启用桌面合成后,单个窗口不再像以前版本的Windows中那样直接绘制到屏幕或主显示设备。相反,他们的绘图被重定向到视频内存中的屏幕外表面,然后将其渲染成桌面图像并显示在显示屏上。

由于使用DWM进行blitting有know issues,您可以:

  1. 尝试使用替代技术;你有a nice list here

  2. 您可以使用DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);

  3. 禁用DWM(临时)

    尝试看看是否有效。

    AFAIK然而,可靠地获取使用3D渲染(DirectX或OpenGL)的应用程序内容的唯一方法是将自己注入进程并复制出位(请参阅this answer for DirectX或钩子) wglSwapBuffers并使用glReadPixels for OpenGL进行回读