隐藏的 PrintWindow 和 BitBlt windows

PrintWindow and BitBlt of hidden windows

我的程序正在截取其他应用程序 windows 的屏幕截图以自动执行它们的某些任务。那些 windows 有时会隐藏在屏幕外或被其他 windows 遮挡。

为了减少混乱,我从代码清单中删除了所有错误检查。我正在准备两种类型的屏幕截图

// 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);

我对上面的代码有三个问题:

  1. 我需要打电话给 GdiFlush() 吗?我阅读了文档并做了一些 Google 研究,但我仍然不确定调用它是否有意义。
  2. BitBlt() 之前调用 UpdateWindow() 有什么不同吗?这有帮助吗,设备上下文内容是 "more up to date"?
  3. 我假设 PrintWindow() 的所有工作都是从目标应用程序内部完成的(增加目标应用程序的 CPU 使用率),而 BitBlt() 完全从执行在调用线程中(因此增加了我自己的应用程序的 CPU 使用率)?
  4. 在什么情况下上述功能可能失效?我知道如果启用了桌面合成 (DWM),BitBlt() 仅适用于隐藏的 windows,但在非常小的一组系统上 (Windows 8/10) BitBlt()PrintWindow() 似乎在 windows 上失败了,它们在其他系统上工作得很好(即使启用了 DWM)。不过,我无法发现任何模式来说明原因。

感谢任何信息,谢谢。

两个多月后,我终于意识到这只发生在 2018 年底的 Windows 更新 1809 中。显然,Windows 改变了它处理该更新剪辑的方式,以便设备windows 中位于屏幕外的部分不再更新上下文内容。

回答个别问题:

1) GdiFlush 似乎没有什么不同,至少从目前我所知道的情况来看是这样。

2) 仍然不是 100% 确定,但我也认为这没有什么不同。

3) 还是不知道。

4) 参见上面的答案。

最后,经过几个小时的调查,我找到了解决该问题的方法: 在要成像的表单的 ACTIVATE 事件中调用以下命令就足够了(VB 编码中的示例):

Call SetWindowLong(me.hwnd, GWL_EXSTYLE, WS_EX_LAYERED)

而这个命令定义如下:

Private Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Private Const GWL_EXSTYLE = (-20)

Private Const WS_EX_LAYERED = &H80000

请试试这个!

此致, 小鹿斑比66