最近工作 中有个需求是抓取桌面截图, 这里的桌面是指点了“显示桌面”之后看到的桌面, 截图内容包括桌面背景和图标以及任务栏,如下图:
注意需求是:即使当前其他窗口盖在我们的桌面上,我们要求抓取到的内容也是这些窗口背后的桌面。
思考怎么样才能在程序运行时,动态抓取该截图?
大概想了些方法:
(1) 通过Desktop DC, 然后BitBlt
点评: 通过这种方法抓取到的截图是当前桌面的所有窗口,包括其他当前打开显示的窗口 , 这不是我们所期望的。
另外我们也不可能在需要截图时最小化所有窗口, 截完了再还原。
(2)通过注册表获取桌面壁纸保存的地方
点评:获取到该壁纸, 没有图标和任务栏, 也不是我们所期望的。
(3)通过DWM特性来获取
点评: Vista后的Desktop Window Manager(DWM)特性,可以让我们通过DwmRegisterThumbnail,DwmUpdateThumbnailProperties等API实时显示某个窗口的截图。可惜获取到的内容只能显示在我们预定义的窗口里, 但是我们没法获取里面的内容。另外这个特性只有Vista之后才支持, XP系统怎么办?
(4)通过Magnification来获取
点评:我们可以通过系统的放大镜技术, 过滤掉盖在桌面上的其他窗口(MagSetWindowFilterList ),然后通过MagSetImageScalingCallback 拦截内容。该技术的问题一是过滤窗口的个数限制, 另外也只能适用于Vista之后。
(5)通过API Hook技术
点评:通过API Hook技术我们可以动态拦截桌面窗口DC的绘画动作,这样就可以拦截或是合成窗口内容了。但是一来这样做比较复杂, 稳定性也很难保证,另外也有杀鸡用牛刀的感觉。
(6)通过PrintWindow技术
点评:PrintWindow API内部通过WM_PRINTCLIENT和WM_PRINT来获取窗口截图, 这个API在XP时代工作的不是很好,但是在Vista之后已经比较稳定了。
我们最后选择通过PrintWindow API来获取桌面截图, 大概过程如下:
a. 获取桌面窗口(Program Manager)的内容
b. 获取TaskBar窗口的位置和内容
c. 把TaskBar窗口内容和桌面内容合成, 贴在正确的位置
这里说一下要注意的一些问题:
a . 注意窗口的层次
Aero模式下, Program Manager窗口只包含背景图片, 图标列表在WorkerW窗口下:
Basic模式下窗口内容都在Program Manager窗口下:
b. 注意多显示器的情况, 多显示器时除了主显示器,其他桌面是没有任务栏的
c. 注意任务栏的位置, 任务栏是可以Dock到任何地方并且可以隐藏的,不要hard code在下面了。
d. XP下对ProgramManager窗口调用PrintWindow会有刷新问题。一直没有好的解决方案, 所以对XP勉强采用第一种DC的方式了。
因此, 我到现在还没有找到完整解决我们这个问题的方法, 不知道大家有没有好的思路?
附上我的测试代码:ScreenSnapshot.rar