在 Windows Vista、Windows 7 中截屏,应用区域外有透明区域

Posted

技术标签:

【中文标题】在 Windows Vista、Windows 7 中截屏,应用区域外有透明区域【英文标题】:Taking screenshots in Windows Vista, Windows 7, with transparent areas outside the app region 【发布时间】:2011-02-28 12:19:59 【问题描述】:

我正在尝试截取应用程序的屏幕截图,并且我想让矩形中不属于应用程序区域的部分透明。因此,例如在标准 Windows 应用程序上,我想让圆角透明。

我写了一个快速的测试应用程序,它可以在 XP(或关闭 aero 的 vista/windows 7)上运行:

    protected override void OnPaint(PaintEventArgs e)
    
        base.OnPaint(e);

        Graphics g = e.Graphics;           

        // Just find a window to test with
        IntPtr hwnd = FindWindowByCaption(IntPtr.Zero, "Calculator");

        WINDOWINFO info = new WINDOWINFO();
        info.cbSize = (uint)Marshal.SizeOf(info);
        GetWindowInfo(hwnd, ref info);


        Rectangle r = Rectangle.FromLTRB(info.rcWindow.Left, info.rcWindow.Top, info.rcWindow.Right, info.rcWindow.Bottom);
        IntPtr hrgn = CreateRectRgn(info.rcWindow.Left, info.rcWindow.Top, info.rcWindow.Right, info.rcWindow.Bottom);
        GetWindowRgn(hwnd, hrgn);

        // fill a rectangle which would be where I would probably 
        // write some mask color
        g.FillRectangle(Brushes.Red, r);

        // fill the region over the top, all I am trying to do here 
        // is show the contrast between the applications region and 
        // the rectangle that the region would be placed in
        Region region = Region.FromHrgn(hrgn);
        region.Translate(info.rcWindow.Left, info.rcWindow.Top);
        g.FillRegion(Brushes.Blue, region);
    

当我在 XP(或关闭 Aero 的 Vista/Windows 7)上运行这个测试应用程序时,我得到了类似的结果,这很棒,因为我可以从中找到一个 xor 掩码,以后可以与 BitBlt 一起使用。

删除了死掉的 Imageshack 链接 - 屏幕截图

这就是问题所在,在启用 Aero 的 Vista 或 Windows 7 上,窗口上不一定有区域,事实上在大多数情况下没有。谁能帮我想办法在这些平台上构建这样的面具?

以下是我已经尝试过的一些方法...

1.使用PrintWindow 函数:这不起作用,因为它会返回关闭 Aero 的窗口的屏幕截图,并且此窗口与打开 Aero 时返回的窗口的形状不同

2 使用Desktop Window Manager API 获取全尺寸缩略图:这不起作用,因为它直接绘制到屏幕上,据我所知,您无法直接获取屏幕截图这个api的。是的,我可以打开一个粉红色背景的窗口,显示缩略图,截屏然后隐藏这个临时窗口,但那是一种糟糕的用户体验和一个完整的黑客,我宁愿没有我的名字。

3.使用 Graphics.CopyFromScreen 或其他一些 pinvoke 变体: 这不起作用,因为我不能假设我需要从中获取信息的窗口位于屏幕 z 顺序的顶部。

现在,我能想到的最佳解决方案是在 Windows 7 和 Vista 上使用特殊情况 Aero,通过硬编码我绘制的一些图形路径来手动擦掉角落,但这个解决方案会很糟糕,因为任何执行自定义蒙皮的应用程序会打破这个。

你能想出另一个或更好的解决方案吗?

如果您在这里,感谢您花时间阅读这篇文章,感谢您提供的任何帮助或指导!

【问题讨论】:

有趣且经过深思熟虑的问题,希望得到更多关注。 您正在使用GetWindowRgn() 来获取窗口的区域。如果您使用 GetWindowDC() 代替(获取包括标题栏在内的窗口的 DC)然后使用 SelectObject()GetObject() 以这种方式检索区域会发生什么?有什么不同吗?还有GetClipRgn() 可能会返回不同的形状。 我没有尝试过这种方法,我会在这个周末尝试一下。 【参考方案1】:

删除了很糟糕但在 90 年代会很棒的想法

您说使用 DWM API 仅允许您直接捕获到屏幕...您可以在屏幕外创建一个窗口(例如,X = -100000px,Y = -100000px)但可见(甚至可能隐藏?)和把截图画出来吗?由于在使用 DWM 时每个窗口都有一个背景纹理,所以我认为即使目标不在屏幕上,它仍然可以很好地绘制。

另外,如果您想走 DirectX 路线并访问支持窗口的实际 DX 纹理,我发现了一些可能会有所帮助的线索(尤其是第一个链接):

http://spazzarama.wordpress.com/2009/02/12/screen-capture-with-vista-dwm/ http://channel9.msdn.com/forums/TechOff/251261-Help-Getting-the-shared-window-texture-out-of-DWM-/ http://web.archive.org/web/20080625183653/http://www.aeroxp.org/board/index.php?showtopic=6286 http://www.youtube.com/watch?v=hRTgFTMnT_U

【讨论】:

试过了,不幸的是它不起作用。如上所述,当 Aero 开启时,窗口是不同的形状。 嘿,我查看了示例,但不幸的是,这种方法存在两个问题... 1. 用于获取 Direct3D 表面的 API 不受支持,因此我无法使用这些函数和 2 .(回到 1),它们似乎在 windows 7 和 Vista 之间发生了变化。所以显示的示例实际上并不适用于 Windows 7。不过,我很欣赏这些提示。我希望能直接使用 DirectX7 来做一些事情......【参考方案2】:

如果您正在寻找一个完成的应用程序,有7capture,它还捕获半透明,因此可以将图像保存为 PNG 格式以供以后合成。

编辑:

原始问题和 cmets 表明您希望在 Windows Vista/7 上生成一个区域,然后您可以使用该区域来屏蔽捕获的图像部分,就像 Windows XP 和非 Aero UI 一样。使用区域不会为您提供您正在寻找的结果,因为窗口轮廓不是作为区域计算的,而是作为具有可变透明度的图像 - RGBA。该图像中的 Alpha 通道是您的蒙版,但它不是像区域那样的开关蒙版,而是一个渐变蒙版,其值范围从完全包含像素到完全被屏蔽掉。

虽然它使用未记录的 API,但 http://spazzarama.wordpress.com/2009/02/12/screen-capture-with-vista-dwm/ 处的代码将捕获到 RGBA 缓冲区,然后您可以使用该缓冲区来渲染或保存完整的阴影和其他半透明效果的图像。

DwmCapture.cs

BackBufferFormat = Format.X8R8G8B8

BackBufferFormat = Format.A8R8G8B8

(X8->A8)

然后,您应该能够从捕获的缓冲区访问通常的 RGB 数据和透明度。然后可以将其保存为 PNG 或其他带有 alpha 通道的格式以进行合成。

【讨论】:

感谢提醒,我看到了那个应用程序,但我需要自己编写。 我开始认为这些人也作弊,并通过启发式而不是程序化的方法来做到这一点:) 也许,或者他们是无情的程序员,不介意使用未发布的 API。 DWM 确实计算了一个 RGBA 缓冲区以供以后合成,但似乎无法通过公共 API 提供此纹理。例如。 3d 任务开关使用这个。 我昨晚实际上尝试了这段代码,它适用于 vista 但由于某种原因不适用于 windows 7。 这是一个很好的答案,谢谢 - 我仍然知道比使用未记录的 API 更好,它们是有原因的。我在微软工作了很长时间,他们这样做通常是有充分理由的。【参考方案3】:
    使用 Graphics.CopyFromScreen 或其他一些 pinvoke 变体: 这不起作用,因为我不能 假设我需要的窗口 来自的信息位于顶部 屏幕上的 z 顺序。

如果你知道你需要哪个窗口的信息,你能把它带到前面,调用 Graphics.CopyFromScreen,然后重置它的 z-index 吗?我从经验中知道,当物品在后台时,Aero 会做一些奇怪的事情,以使它们的玻璃界面正常工作(部分渲染等)。这可能不是很好的用户体验;但是,这将是一种特殊情况,仅在 Aero 开启时使用。

【讨论】:

嘿内特,是的,我想这样做,但不幸的是,特殊情况很可能是默认情况,在这种情况下用户体验会很糟糕。另外,我相信 Graphics.CopyFromScreen 仍然没有给我区域,所以我可以圆角。【参考方案4】:

您可以查看AeroShot的源代码,如主页所述,它可以捕获圆角并具有Aero Glass透明效果并将其保存为PNG文件。它是用 C# 编写的。

【讨论】:

它可以工作,但它们会出现一些明显的屏幕闪烁。看起来他们先简单地绘制已知的背景颜色,然后再将其减去。创意。

以上是关于在 Windows Vista、Windows 7 中截屏,应用区域外有透明区域的主要内容,如果未能解决你的问题,请参考以下文章

如何使我的程序在 Windows Vista 和 Windows 7 中运行?

Vista 和 Windows 7 中的 OLEDB JET 错误,而不是 XP

为啥我的 Joomla 模板无法在 Windows 7 / Vista 的 IE9 中正确加载?

我如何在 Windows(xp、vista、7)欢迎屏幕或锁定屏幕(如 VNC 或 Dame Ware)中进行交互

获取 XP、Vista 和 7 的 windows 序列号

WH_JOURNALRECORD 的 SetWindowsHookEx 在 Vista/Windows 7 下失败