C++ - 如何进行屏幕截图,但不包括某些窗口

11

背景: 我有一款软件可以在互联网上进行屏幕共享,其中一个用户充当演示者,其他用户则是观众/参与者。

除了演示窗口外,演示者还有一组不共享的窗口显示在屏幕上(启动共享/停止共享等按钮栏、Skype 窗口等)。

演示者可以从屏幕共享软件的设置中配置,使这些不共享的窗口变为不可见状态(即它们不会出现在发送给参与者的屏幕共享中,但它们后面的窗口内容将出现在截屏中)。

截屏的帧速率约为每秒 10 帧或更快。

问题: 如何以编程方式捕获屏幕,但排除这些不共享窗口?

备注:

  • 由于帧速率较高,我无法对这些窗口进行最小化/最大化/透明化等操作,因为这样窗口会闪烁。该应用程序是使用 Win32 C++ 编写的。
  • 我可以使用分层窗口,但由于 Windows 7 桌面组合功能,这不能直接使用(在 Windows 8 中,你不能再使用 DwmEnableComposition 临时和以编程方式禁用组合)
  • 我可以为 Windows XP/2000/7 等使用分层窗口方法,而对于 Windows 8,则采用不同的方法(如果有),但我更喜欢一个适用于所有系统的单个进程。
  • 我也可以尝试通过捕获单个图像(桌面、需要捕获的窗口)并利用它们的 z-索引来创建最终图像来“组合”截屏,但由于所需的帧速率值,这个过程太慢了。

屏幕截图通常捕获“窗口上方的屏幕区域”,而不是窗口本身绘制的内容。您可以尝试捕获所有可见的顶级窗口并重新组合它们。 - Deanna
这里有人知道WebEx或GootoMeeting等服务是如何实现相同功能的吗? - rohitvk
@SucataMihnea,你有找到任何解决方案吗?我也在寻找同样的答案。 - Vishnu
@Vishnu 目前还没有解决方案(至少没有不需要组合图像的解决方案,这对于实时流传输来说太慢了)。正在研究 Magnification API,但这也有缺点。 - Sucata Mihnea
如果您仍在寻找,请参考媒体这个项目。该项目包含一个名为ScreenCapture的类,在这个类中,您可以设置屏幕坐标以捕获所有屏幕中您想要捕获的部分。所有屏幕都被视为一个大图像,您只需要设置您希望开始和结束的坐标即可。 - D.Zadravec
显示剩余7条评论
5个回答

1
在Windows中,即使是桌面也被视为一个窗口,并拥有自己的HWND。然而,似乎不容易只复制“壁纸”本身。
所以我基本上看到了两种方法。 1. 复制整个桌面,例如BitBlt(GetWindowDC(GetDesktopWindow()),...)
或者
2. 使用GetWindow并以反向方向遍历窗口列表,从刚刚可以确定其HWND的Desktop-Window开始,像这样: //在黑色DC上绘制 hwnd = GetDesktopWindow() while(hwnd = GetWindow(hwnd,GW_HWNDPREV)) { //此窗口未共享? 继续 //否则将其位块传输到我们的dc中 }
希望我能给您一些灵感 :-) 如果有人知道如何仅复制桌面而不包括其子窗口,请告诉我。

我在4个月前尝试过这种方法。这种方法会导致一些桌面元素(如开始菜单和工具栏)无法被捕捉到。它们将以黑色打印在DC上,并且没有透明度被创建。 - Vishnu

0

我知道这个问题很老了,但我遇到了同样的问题,非常非常难以找到任何关于此的信息。

自从Windows 10版本2004(构建10.0.19041)以来,SetWindowDisplayAffinityAPI已经扩展,包括一个名为WDA_EXCLUDEFROMCAPTURE(0x00000011)的标志。这将从使用BitBlt捕获的图像中移除窗口。

该窗口仅在监视器上显示。在其他地方,窗口根本不会出现。 使用此亲和力的一种用途是用于显示视频录制控件的窗口,以便控件不包含在捕获中。

引入于Windows 10版本2004。有关先前Windows版本的兼容性的说明,请参见备注。

对于2004年之前的版本,它将使用现有的WDA_MONITOR标志。

我已经使用桌面截图进行了测试,但如果您使用窗口DC,我不确定会发生什么。

所以我想可能的解决方案是:

// get window handle
hWnd = (...)

BOOL result = SetWindowDisplayAffinity(m_hWnd, WDA_EXCLUDEFROMCAPTURE);

// do bitblt stuff


0

您可以使用放大镜API。

在放大镜API中有一个函数,允许您从目标窗口(放大镜渲染的窗口)中排除特定的窗口。

您可以将此窗口设置为全屏并使其透明,然后使用PrintWindow函数。

该函数:https://learn.microsoft.com/en-us/windows/desktop/api/magnification/nf-magnification-magsetwindowfilterlist

示例项目:

https://www.codeproject.com/Articles/607288/Screenshot-using-the-Magnification-library

https://code.msdn.microsoft.com/windowsdesktop/Magnification-API-Sample-14269fd2


0

也许你可以使用放大镜API,即使微软说“MagImageScalingCallback函数在Windows 7及更高版本中已被弃用,并且不应在新应用程序中使用。没有替代功能。”,但它仍然适用于Windows 10;

这是该API的概述:https://learn.microsoft.com/en-us/previous-versions/windows/desktop/magapi/magapi-intro

微软的示例代码在这里:https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Magnification

如果您想获取屏幕截图的RGB数据,可以使用此API MagSetImageScalingCallback来设置放大镜窗口的回调函数,每次使用放大镜窗口的MagSetWindowSourceInvalidRect时,都会调用此回调函数MagImageScalingCallback,因此您可以在此处获取屏幕截图的RGB数据。


-1

我认为将捕获内容限制在一个大窗口内会更简单。否则,您将需要从屏幕截图中剪切一些窗口。


谢谢,但由于这是一个屏幕共享应用程序(再次强调,像JoinMe和GoToMeeting这样的软件已经可以做到这一点),强制用户只选择一个窗口并不友好(也不符合商业智慧):) - Sucata Mihnea
4
无论如何,我的问题更多地是技术方面的——有人知道我可以使用哪些C/C++指令在Windows 7和/或8中捕获屏幕,而不会渲染一些可见窗口吗?我正在寻找与分层窗口等效的内容(请参见以下MSDN链接,了解使用分层窗口的示例)。 - Sucata Mihnea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接