【问题标题】:Retrieve Window Size without Windows Shadows检索没有 Windows 阴影的窗口大小
【发布时间】:2015-08-22 18:55:57
【问题描述】:

我正在尝试基于窗口句柄在 C# 中捕获桌面窗口。我正在使用 .NET 并使用 PInvoke 到 GetWindowRect() 来捕获窗口矩形。我的窗口选择和矩形捕获工作正常。

但是,捕获的窗口矩形不仅包括实际的窗口大小,还包括窗口周围的阴影等装饰物。当我尝试将窗口剪辑到位图时,位图包含区域和阴影。在 Windows 10 上,我得到透明阴影区域,包括活动窗口下方可能可见的任何内容:

我使用的代码很简单,使用 Win32 GetWindowRect() 通过 PInvoke 调用捕获窗口:

var rect = new Rect();
GetWindowRect(handle, ref rect);
var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
var result = new Bitmap(bounds.Width, bounds.Height);

using (var graphics = Graphics.FromImage(result))
{
    graphics.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
}

return result;

然后我捕获图像并将其分配到图片框中。

此外,窗口之间似乎存在一些差异 - 有些窗口有阴影,有些则没有。大多数都可以,但有些像 Visual Studio 和 Chrome 不可以,因此去除无关像素甚至不是一件简单的事情。

我尝试过使用 GetClientRect() ,但这让我得到的只是客户区,这不是我所追求的。我想得到的是带有边框但没有阴影的实际 Window 矩形。

有没有办法做到这一点?

【问题讨论】:

  • 您可能需要查看SystemInformation 以获得一致的边框大小,将其添加到客户区。
  • 您应该从窗口的 DC (GetWindowDC) 复制而不是从屏幕复制 - 即使它被另一个窗口覆盖,这也会得到窗口。另外,请参阅第三条评论on this page
  • 查看这个question 临时修改窗口创建参数以通过反转CS_DROPSHADOW 标志来移除阴影、捕获窗口并恢复蒙版。
  • @LucasTrzesniewski 我尝试使用 DC。还有其他问题 - 即我在一些所有者绘制的应用程序(如 Chrome)上获得了时髦的捕获行为。它仍然有边界。
  • 尝试DwmGetWindowAttribute DwmGetWindowAttribute functionDWMWA_EXTENDED_FRAME_BOUNDS 属性。

标签: c# windows winapi pinvoke


【解决方案1】:

您可以使用DwmGetWindowAttribute。请参阅以下示例。您可以将GetWindowRectangle 与任何句柄一起使用以获取其实际大小。

[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);

    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    [Flags]
    private enum DwmWindowAttribute : uint
    {
        DWMWA_NCRENDERING_ENABLED = 1,
        DWMWA_NCRENDERING_POLICY,
        DWMWA_TRANSITIONS_FORCEDISABLED,
        DWMWA_ALLOW_NCPAINT,
        DWMWA_CAPTION_BUTTON_BOUNDS,
        DWMWA_NONCLIENT_RTL_LAYOUT,
        DWMWA_FORCE_ICONIC_REPRESENTATION,
        DWMWA_FLIP3D_POLICY,
        DWMWA_EXTENDED_FRAME_BOUNDS,
        DWMWA_HAS_ICONIC_BITMAP,
        DWMWA_DISALLOW_PEEK,
        DWMWA_EXCLUDED_FROM_PEEK,
        DWMWA_CLOAK,
        DWMWA_CLOAKED,
        DWMWA_FREEZE_REPRESENTATION,
        DWMWA_LAST
    }

    public static RECT GetWindowRectangle(IntPtr hWnd)
    {
        RECT rect;

        int size = Marshal.SizeOf(typeof(RECT));
        DwmGetWindowAttribute(hWnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, size);

        return rect;
    }

【讨论】:

    【解决方案2】:

    我正在运行 Windows 10,并且在编写应用程序以将窗口捕捉到屏幕顶部或底部时遇到了同样的问题。我发现 DwmGetWindowAttribute() 可以工作。它返回的 RECT 值与 GetWindowRect() 略有不同...

    来自示例窗口的结果:

    GetWindowRect(): {X=88,Y=26,Width=871,Height=363}

    DwmGetWindowAttribute(): {X=95,Y=26,Width=857,Height=356}

    我的测试表明 GetWindowRect() 包含装饰,而 DwmGetWindowAttribute() 没有。

    如果您在带有装饰的窗口上从两种方法获得相同的结果,则可能是该特定窗口正在绘制自己的装饰,或者需要为该窗口设置一些其他属性或属性考虑到。

    【讨论】:

    • 这是您从 DwmGetWindowAttribute 检索到的扩展帧边界吗?
    • DwmGetWindowAttribute() 给了我实际的边框,而 GetWindowRect() 在边框周围包含半透明的“模糊”阴影。
    • DwmGetWindowAttribute(handle, DWMWINDOWATTRIBUTE.ExtendedFrameBounds, out var rect, Marshal.SizeOf<Rect>()); 在 Win10 上为我工作。
    • 参考chromium.googlesource.com/external/webrtc/+/master/modules/…,我这里有两个问题: 1. DwmGetWindowAttribute函数在win7或部分机器上可能会失效; 2.目标窗口最大化时,DwmGetWindowAttribute或GetCroppedWindowRect得到的区域还是有黑边
    猜你喜欢
    • 1970-01-01
    • 2020-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    • 2013-11-17
    相关资源
    最近更新 更多