隐藏的 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);
我对上面的代码有三个问题:
- 我需要打电话给
GdiFlush()
吗?我阅读了文档并做了一些 Google 研究,但我仍然不确定调用它是否有意义。
- 在
BitBlt()
之前调用 UpdateWindow()
有什么不同吗?这有帮助吗,设备上下文内容是 "more up to date"?
- 我假设
PrintWindow()
的所有工作都是从目标应用程序内部完成的(增加目标应用程序的 CPU 使用率),而 BitBlt()
完全从执行在调用线程中(因此增加了我自己的应用程序的 CPU 使用率)?
- 在什么情况下上述功能可能失效?我知道如果启用了桌面合成 (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
我的程序正在截取其他应用程序 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);
我对上面的代码有三个问题:
- 我需要打电话给
GdiFlush()
吗?我阅读了文档并做了一些 Google 研究,但我仍然不确定调用它是否有意义。 - 在
BitBlt()
之前调用UpdateWindow()
有什么不同吗?这有帮助吗,设备上下文内容是 "more up to date"? - 我假设
PrintWindow()
的所有工作都是从目标应用程序内部完成的(增加目标应用程序的 CPU 使用率),而BitBlt()
完全从执行在调用线程中(因此增加了我自己的应用程序的 CPU 使用率)? - 在什么情况下上述功能可能失效?我知道如果启用了桌面合成 (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