【问题标题】:Is there any way to omit a window from Desktop Duplication?有没有办法从桌面复制中省略一个窗口?
【发布时间】:2017-01-16 17:22:39
【问题描述】:

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

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

背景:我正在编写一个远程查看/支持应用程序,并希望允许远程用户在私密的情况下工作 - 在不干扰捕获的同时使用户的屏幕空白。

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

【问题讨论】:

    标签: dxgi desktop-duplication


    【解决方案1】:

    Desktop Duplication 将合成图像复制到视频输出,您的想法是让它不仅可以工作,不包括特定区域,而且还可以为相关窗口后面的窗口提供操作系统渲染/合成活动,即合成不是正常桌面操作所必需的。这种组合实际上并没有首先发生,桌面复制不提供服务来强制它或以其他方式在每个窗口的基础上分离图像数据。

    【讨论】:

    • 我知道使用 DXGI API 无法实现该行为,但是如果不使用旧的和已弃用的 GDI(BitBlt 等 ...)如何实现呢?此外,GDI 技巧不再适用于 Windows 10,更准确地说,适用于所有启用 DWM 的版本。 @RomanR。你能分享实现这一目标的替代方法吗?
    • 我不知道适合该任务的 API。新的 UWP Screen capture API 似乎正在这样做(它可以选择捕获单个应用程序),但我不确定是否可以使用公共较低级别的 API,例如在 DWM 的帮助下。此外,当我上次检查时,我对新的 API 性能不太满意。毕竟,所有不可见的部分都不必重新绘制,因此不必复制部分不可见的 UI。
    【解决方案2】:

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

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

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

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

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

    有一个很好的样本here

    这些 API 的最大好处是 它们也适用于当前未被其他窗口显示或覆盖的窗口。图像质量和帧率是 可以接受,您甚至可以获得窗口的全高清缩略图。 因此,您可以创建一个最顶层的全屏窗口并将其注册为接收另一个窗口的“帧”。结果是一个全屏窗口,它显示另一个窗口的内容。

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

    问题可以这样解决:

    1. 拦截 DwmUpdateThumbnailProperties 调用发送的消息(WM_...something),该调用也应将帧发送到目标窗口。
    2. 在窗口显示之前以任何格式和内存保存框架
    3. 向目标窗口发送不同的帧(用于覆盖屏幕的位图)
    4. 转到第 1 点

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

    【讨论】:

      【解决方案3】:

      我在网上搜索了一下,发现了Magnification API。 我发现可以使用 MagSetImageScalingCallback API 为放大线程注册回调。

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

      在我看来,“ImageScalingCallback”这个名称可能会导致对实际用法的误解。 无论如何,我终于意识到如何在我的应用程序中使用它:

      1) 放大镜窗口被创建并设置为 fullscreen-topmost。

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

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

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

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

      这些步骤可以在不丢失“捕获”功能的情况下进行迭代。 事实上,即使屏幕被黑色图像覆盖,这也不会阻止 Magnification API 捕获屏幕。

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

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

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

      【讨论】:

        【解决方案4】:

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

        另一个选项是捕获部分(或完全)隐藏到您选择的位图上下文的窗口,您可以使用 user32.dll PrintWindow 和鲜为人知的 PW_RENDERFULLCONTENT 标志。此标志仅在 Windows 8.1 或更高版本上可用,它甚至可以捕获使用合成 API 或直接使用 DirectX 呈现的窗口。下面是一个如何使用它的简单示例:

        Bitmap GetWindowBitmap(IntPtr hWnd) {
            RECT bounds;
            if (!GetWindowRect(hWnd, out bounds))
                throw new Win32Exception();
        
            var bmp = new Bitmap(bounds.right - bounds.left, bounds.bottom - bounds.top);
            using (var g = Graphics.FromImage(bmp))
            {
                IntPtr dc = IntPtr.Zero; 
                try
                {
                    dc = g.GetHdc();
                    bool success = PrintWindow(hWnd, dc, PrintWindowDrawingOptions.PW_RENDERFULLCONTENT /* 0x00000002 */);
                    if (!success)
                        throw new Win32Exception();
                }
                finally
                {
                    if (dc != IntPtr.Zero)
                        g.ReleaseHdc(dc);
                }
            }
            return bmp;
        }
        

        您可以使用该方法枚举桌面上的所有窗口,但它不会很快,因为 PrintWindow 要求每个窗口重绘到您提供的 hdc。即使是一个行为不端的窗口也会将其减慢数百或数千毫秒。

        【讨论】:

          猜你喜欢
          • 2011-06-12
          • 1970-01-01
          • 1970-01-01
          • 2013-01-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-18
          相关资源
          最近更新 更多