1.目标
创建一个.NET Framework的WinForm可执行程序,测试使用Windows.Graphics.Capture API,对窗口句柄进行截取
将所有截取的步骤写在一个单独的类中,方便后续移到独立的链接库中
2.环境及框架
Visual Studio 2022 + Win10 64位 21H2版本 + .NET Framework 4.8框架
3.项目模板向导
4.NuGet引用
Microsoft.Windows.SDK.Contracts
引用后直接编译出错
修改csproj文件,在下述位置增加文本
<ItemGroup>
<PackageReference Include="Microsoft.Windows.SDK.Contracts">
<Version>10.0.26100.1742</Version>
</PackageReference>
</ItemGroup>
重新打开解决方案后先生成解决方案
生成后出现NuGet引用
然后可正常编译
5.添加Helper类
代码复制自
微软GITHUB ScreenCapture 代码
https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WPF/ScreenCapture
5.1 CaptureHelper.cs
该类的主要作用是根据窗体句柄或者显示器句柄生成GraphicsCaptureItem
5.2 Direct3D11Helper.cs
该类的主要作用是创建Direct3D设备,需要NuGet安装SharpDX.Direct3D11
5.3 MonitorEnumeratiohHelper和WindowEnumerationHelper
原主要项目中枚举窗体和显示用的类,相应的引入,可以判断窗体是否可以捕获
需要注意的是,对于MonitorEnumeratiohHelper中的Vector2这个类
需要NuGet安装System.Numerics包,如果不需要和显示器相关的可以直接不引用和Monitor相关代码也就不需要安装这个更新包
5.4 关于原例程ScreenCapture
原例程是使用DirectX来将获取的数据进行显示,本例程中主要目的是获取位图数据,因此所有和显示有关的代码不予引入
5.5 创建自己的类WGCHelper
Init方法
主要是初始化Direct相关类,Direct3D相关设备不应该多次初始化,因为其为COM操作,如果每次针对不同的窗口句柄初始化一次Direct3D设备,会导致相关的对象无法释放,内存占用不断增加,因此本类的Init必须在主进程初始化后立即进行,不能启停捕获一次执行一次
StartCapture方法
StopCapture方法
基本流程:
- 主进程实例化WGCHelper(创建Direct3D设备)
- 主进程通过FindWindow API得到需要捕获的窗体的句柄 – 下面的部分为WGCHelper类中的步骤
- 使用窗体句柄生成CaptureItem(这一步是COM操作,来源为微软的示例代码)
- 使用Direct设备和CaptureItem.Size创建framePool
- 注册OnFrameArrived处理函数为framePool的FrameArrived帧到达事件函数
- 使用framePool和CaptureItem创建captureSession
- 使用captureSession的StartCapture开始捕获
- 在framePool的OnFrameArrived里得到捕获的帧(处理窗体尺寸变化的情况-在窗体尺寸变化时重建framePool而不处理捕获帧)
- 处理捕获的帧 映射纹理成BGRA的32位BMP数据
- 构造回调数据,然后回调主进程传入的处理函数回调该数据
6 注意
- WGCHelper类的实例化应当在程序初始化时创建,而不能每启停捕获就创建,否则会导致SharpDX的COM对象无法释放造成内存泄露
- 对于在捕获过程中对窗口大小进行缩放进行了处理,若被捕获窗口在缩放过程中则不执行回调函数,使用lastSize记录最后一次的窗口大小,尺寸变化后重建帧池
- 对于需要捕获的窗口可以被其他窗口覆盖,但不能最小化,也不能处于虚拟桌面上,不然无法捕获
- 鼠标无法隐藏,即使窗体被别的窗体覆盖,鼠标也依然显示在对应的位置上
- 对于捕获窗口的外侧黄色醒目外框也是必须显示的,有隐藏的属性也是需要经由用户确认后才能隐藏
captureSession.IsCursorCaptureEnabled
captureSession.IsBorderRequired
对于4和5的有两项属性,但是需要由用户同意后能才修改,而且对于操作系统版本有要求
- 对于回调的窗口尺寸需要注意的是该尺寸在经由操作系统处理后会加上阴影部分,宽高都会变大
TryGetNextFrame()后的尺寸是不包含操作系统增加的阴影尺寸
CreateSharpDXTexture2D()创建纹理后的尺寸是增加了阴影的尺寸,在100%DPI缩放下宽度左右各7个像素的阴影,共14个点,高度增加7个像素的阴影,在下方
- 对于大分辨率大窗体数据量十分巨大,未测试其稳定性,比如一个3840×2160的全屏窗体,数据量为3840×2160×32÷8=63M 每帧,每秒60帧的话数据量巨大
7 关于Demo
在界面上和控制台输出的截取相关信息,未生成截获的缩略图
源代码包含完整工程项目以及编译后可执行文件下载链接:
https://download.youkuaiyun.com/download/coldwind811201/90518761