【发布时间】:2019-05-11 14:48:16
【问题描述】:
我的程序正在截取其他应用程序窗口的屏幕截图以自动执行某些任务。这些窗口有时会隐藏在屏幕外或被其他窗口遮挡。
为了减少混乱,我从代码清单中删除了所有错误检查。我正在准备这两种类型的屏幕截图
// Get size of the target window.
RECT clientRect;
GetClientRect(hwnd, &clientRect);
int width = clientRect.right - clientRect.left;
int height = clientRect.bottom - clientRect.top;
// Create a memory device context.
HDC windowDC = GetDC(hwnd);
HDC memoryDC = CreateCompatibleDC(windowDC);
// Create a bitmap for rendering.
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = width;
bitmapInfo.bmiHeader.biHeight = -height;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = width * height * 4;
RGBQUAD* buffer;
HBITMAP bitmap = CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, (void**)&buffer, 0, 0);
HGDIOBJ previousObject = SelectOBject(memoryDC, bitmap);
然后我会用其中一个来截取实际的屏幕截图
PrintWindow(hwnd, memoryDC, PW_CLIENTONLY);
GdiFlush();
或
UpdateWindow(hwnd);
BitBlt(memoryDC, 0, 0, width, height, windowDC, 0, 0, SRCCOPY);
GdiFlush();
然后我将缓冲区内容复制到向量中
std::vector<RGBQUAD> pixels;
pixels.resize(width * height, { 0, 0, 0, 0 });
memcpy(&pixels[0], buffer, bitmapInfo.bmiHeader.biSizeImage);
最后我正在清理所有东西
ReleaseDC(hwnd, windowDC);
SelectObject(memoryDC, previousObject);
DeleteDC(memoryDC);
DeleteObject(bitmap);
我对上面的代码有三个问题:
- 我需要拨打
GdiFlush()吗?我阅读了文档并进行了一些 Google 研究,但我仍然不确定调用它是否有意义。 - 在
BitBlt()之前调用UpdateWindow()是否有影响?这是否有助于使设备上下文内容“更新”? - 我是否正确假设对于
PrintWindow(),所有工作都在目标应用程序内完成(增加目标应用程序的 CPU 使用率),而BitBlt()完全在调用线程内执行(从而增加 CPU 使用率我自己的应用程序)? - 在什么情况下上述函数可能会失败?我知道
BitBlt()仅在启用桌面组合 (DWM) 的情况下适用于隐藏窗口,但在极少数系统 (Windows 8/10) 上BitBlt()和PrintWindow()似乎对于它们工作的窗口失败在其他系统上就好了(即使启用了 DWM)。不过,我找不到任何模式来解释为什么。
感谢任何信息,谢谢。
【问题讨论】:
-
我认为这完全没问题,因为隐藏和/或遮挡的窗口不需要保留它们的内容。
-
你最终想要完成什么?截屏以自动化某些镜头听起来像是一种真的复杂的方式来模仿 UI 自动化。
-
GdiFlush 我认为是使用 GDI 写入打印机和绘图仪的保留。我不知道即使启用了 DWM,BitBlt 也会成功。 PrintWindow 发送一条 WM_PRINT 消息,因此目标应用程序不能崩溃并且必须实际处理它。如果您尝试与更高的完整性级别交谈,我希望这两种方法都会失败?窗户。
-
感谢您的 cmets。让我感到困惑的是,这在大多数系统上都能可靠地工作。它可能在一个系统上为一个应用程序工作,而在另一个系统上为同一应用程序(和应用程序版本)失败。这可能与安装了两个图形卡的系统有关吗?我也将不胜感激(2)和(3)的任何答案 - 谢谢。我还在 github 上找到了这条评论:github.com/mozilla/positron/blob/…
-
检查这些系统是否使用了软件光栅化器。无论如何,您真正想在这里完成什么?这个问题强烈暗示以错误的方式做事。除此之外,应用程序可以explicitly request, that taking a screenshot won't work。
标签: c++ winapi screenshot dwm bitblt