有没有办法从桌面复制中省略窗口?

时间:2017-01-16 17:22:39

标签: dxgi desktop-duplication

我希望能够显示一个窗口,其中包含向用户显示但桌面复制无法捕获的消息。这可能吗?

或者,有没有一种方法可以在向用户显示之前在桌面的顶部绘制? (理想情况下没有大规模停止GPU)

背景:我正在编写远程查看/支持应用程序,并且希望允许远程用户在隐私中工作 - 在不干扰捕获的情况下消隐用户的屏幕。

我想避免回到WM_PRINT和BitBlt的黑暗日子,但我不确定DXGI是否允许我想做什么。

4 个答案:

答案 0 :(得分:4)

桌面复制副本复制图像传递到视频输出,你的想法是让它工作不仅排除特定区域,而且还有窗口后面的窗口的操作系统渲染/组合活动,组成是对于正常的桌面操作不是必需的。这样的组合实际上并不是首先发生的,桌面复制不提供强制它或以其他方式基于每个窗口分离图像数据的服务。

答案 1 :(得分:1)

注意:此答案仅部分解决了问题。

我和@DanGroom想要实现的是捕获屏幕内容,同时屏幕继续显示覆盖屏幕内容的固定图像或位图。

如@RomanR所建议。我看了一下UWP屏幕捕获API,但是我意识到它们仅适用于UWP应用程序。我没有尝试过,但似乎它们基于DirectX,就像这里的桌面复制sample一样。我不了解DirectX,但据我了解,无法使用这些API监视特定窗口。您可以只获得整个屏幕的框架。如果屏幕上覆盖了一个全屏窗口,那么该窗口就是在每个帧上捕获的窗口(我添加了一段code,将一个帧转储到bmp中,这就是行为)。 因此,在我的情况下,这些API导致了一条死胡同。

我在DWM Thumbnail API DwmRegisterThumbnail ()等)上取得了一些成功。与屏幕捕获API相比,它们非常简单。

  1. 您注册了要监视的窗口,并设置了要接收监视窗口图像的目标窗口。
  2. 使用 DwmUpdateThumbnailProperties ()调用定期询问监视窗口的更新图像。

有一个很好的示例here

这些API的最大好处是它们还可以与当前未显示或未被其他窗口覆盖的窗口一起使用。图像质量和帧频为     可以接受,甚至可以获取窗口的FullHD缩略图。 因此,您只需创建一个最上面的全屏窗口并注册它即可接收另一个窗口的“框架”。结果是一个全屏窗口,其中显示了另一个窗口的内容。

由于将缩略图发送到窗口,这减少了使用BitBlt捕获接收另一个窗口的帧的窗口图像的问题。那是因为屏幕更新是直接发送到目标窗口的,显然您不能只接收缓冲区中的帧更新或类似内容。如果将目标窗口设为透明并尝试捕获帧,则会得到黑色位图。另外,您可能会陷入BitBlt捕获this之类的问题。

可以通过以下方式解决问题:

  1. 拦截DwmUpdateThumbnailProperties调用发送的消息(WM _... something),该消息还将帧发送到目标窗口。
  2. 在窗口上禁用帧之前,以任何格式和内存保存帧
  3. 将另一帧(用于覆盖屏幕的位图)发送到目标窗口
  4. 转到第1点

不幸的是,我不知道如何使用Windows API来实现。特别是对于第2点,如何在不捕获已经显示的图像的情况下获得窗口的框架?

答案 2 :(得分:1)

我在网络上挖了一点,然后碰到了Magnification API。 我发现可以使用 MagSetImageScalingCallback API为放大线程注册回调。

据我了解,每当需要向使用 MagSetWindowSource API注册的放大镜窗口中绘制新帧时,都会调用此回调。 原始屏幕位图和所有相关信息都传递给回调,其目的是转换在回调返回时将绘制到窗口的位图。

我认为名称“ Image 缩放回调”可能会导致对实际用法的误解。 无论如何,我终于意识到如何在我的应用程序中使用它:

1)创建放大镜窗口并将其设置为全屏顶部。

2)需要绘制第一帧时立即调用回调

3)将原始位图复制到另一个缓冲区

4)将原始位图内容替换为纯黑色的位图

5)回调返回,并将修改后的位图绘制到放大镜窗口

可以重复执行此步骤,而不会失去“捕获”功能。 实际上,即使屏幕上覆盖有黑色图像,也不会阻止Magnification API捕获屏幕。

这是因为注册为放大镜窗口的窗口从不包含在捕获中(即使是全屏窗口)

这正是我一直在寻找的行为。 我对CodeProject网站上的示例Screenshot using the Magnification library进行了少许修改,以实现此行为。包含在srcdata指针中的已捕获图像将转储到一组文件中,以证明该捕获正在工作并且每个图像都包含更新的捕获。

很遗憾,这些API已被弃用,并且尚未提供替代产品。

答案 3 :(得分:0)

一种选择是自己实施RDP协议,并利用Windows中已经可用的功能在启动远程会话时锁定本地会话。如果您想自己做,可以查看mstsclib,也可以签出mRemoteNG,它是功能齐全的C#远程桌面客户端,它也实现了该协议。

另一种选择是捕获部分(或全部)隐藏在所选位图上下文中的窗口,您可以使用user32.dll echo $DISPLAY和鲜为人知的PrintWindow标志。该标志仅在Windows 8.1或更高版本上可用,它甚至可以捕获用Composition API或直接用DirectX渲染的窗口。下面是一个如何使用它的简单示例:

PW_RENDERFULLCONTENT

您可以使用此方法枚举桌面上的所有窗口,但它不会很快,因为PrintWindow要求每个窗口重新绘制到您提供的hdc。甚至一个行为异常的窗口也可以使此速度降低数百或数千毫秒。

相关问题