关于CefSharp的坎坷之路
项目背景:
公司的XX产品需要升级和以后支持多平台的使用。因为之前项目是由WPF实现的。目前以后想作为Html5来展示页面。
因为涉及到整体更改遇到的问题较多以及其他原因,所以只是内部内容区域先替换为Html5页面,所以需要嵌入Browser控件。
Browser控件的选型:
1.Winform中的WebBrowser
2.WPF中的WebBrowser
3.WebKit.Net
4.CefSharp
5.awesominum
6.OpenWebKitSharp
7. geckofx
经过初步查阅,WebKit.Net、OpenWebKitSharp 和geckofx 都是基于Winform的。CefSharp 在GitHub有源码,并且具备Winform和WPF的版本。awesominum可以允许把网页嵌入到 3D 画面或游戏中,支持unity3D。
经过尝试.Net中的Winform版本的WebBrowser,背景无法直接设置透明, 需要通过Windows Api进行处理(仅查阅,未实际进行处理和验证)。
首先把Winform的WebBrowser放到项目中进行了实验和处理,发现两个致命问题:一个是背景不透明,因为整个背景具有渐变和过渡效果。第二个是,页面切换具有滑动过渡效果。
Winform版的WebBrowser会悬浮最上层,不会渐变隐藏消失。 基于以上两点,针对Winform和基于Winform的其他暂时不在考虑。
(调研其他基于Winform的第三方控件没有具体查看是否内部已经处理这些问题,喜欢深入研究的同学或使用过的同学也可以告诉我啊)
目前 调研CefSharp和Awesominum。
CefSharp的源码Demo进行测试,目前是符合需求。Awesominum初步查看也符合需求。
特定需求:在某个Html5页面中 需要调用摄像头进行录像拍摄。
现象:在CefSharp的初步显示具备摄像头打开的页面时,无法打开摄像头。开始查找问题,最后发现 要给CefSharp的CommandLineArgs添加一些命令才可以显示处理,并且要启动WebServer服务。(后面会详细讲解此问题)
而在Awesominum中未找到可以添加命令行参数的方法,所以姑且放弃。最好还是先选用CefSharp。
目前,引用CefSharp,我是通过Nuget进行获取安装的。
关于Nuget的使用,大家可以自行搜索使用。Nuget还可以自己搭建公司专属的插件服务器和客户端调用。
其中需要注意的是:
1.使用正确的Package source资源地址进行搜索下载。
如下图,在搜搜CefSharp的时候,要使用nuget.org地址或All下载资源,这样才能搜索到CefSharp,Microsoft Visual Studio Offline Packages 则是针对微软的一些插件和应用包。
2.插件的安装和卸载都要要用Nuget进行处理
在使用过程中,有时候会遇到解决方案打开,然后操作生成项目时,提示正在恢复还原Nuget包,然后,然后,就一直然后了... 那叫个等的花都谢了。强制关闭,下次还是会有滴。还是放弃抵抗吧。
如果Nuget的包恢复和还原出来错活着不需要了,还是通过Nuget进行卸载,以免有残留。真是不使用不知道,一使用全乱套,啊哈哈~~~~
纯属个人体会,个中滋味,各自体会。
----------------以下为使用CefSharp过程中,个人遇到的一些问题和注意事项------------------------
一:引用CefSharp的编译需要指定目标平台X86或X64
在我们解决方案中,一般默认都是AnyCup的,所以在生成时会提示错误。
因为CefSharp的使用需要明确目标平台的。所以生成时,要指定是x86还是x64,因为我们项目需要 要改为X86. 有的会问,我已经修改目标平台为x86了,如下图 位置:1、2处。而编译还是生成不成功呢?这是为什么呢?为什么呢?
请不要在项目的属性上进行设置。要在整个解决方案的配置上进行设置 引用CefSharp的项目的Platform 平台。仔细看你会发现 解决方案的的项目的平台如下图 位置3处和 项目属性中是不一致的呢。所以切记。
二:CefSharp中的生成目录的问题
在我们项目中,CefSharp的项目是作为插件的方式嵌入主框架中的,所以生成目录到了根目录下的Plugins目录下。运行后,你会发现一直提示无法找到CefSharp.core.dll等相关的dll文件。 哈哈,找不到,找不到就对了。
CefSharp是到根目录下(默认是指Bin)的环境中寻找dll的,所以无法进行找到。暂时也未从源码案例中找到进行设置目录的方式(若其他忍知晓可以告知我哦)。
不过这样也不要紧,不就是在程序的运行环境中找不到吗?我们自己添加上不就行了。代码如下:
- var pluginsPath = Path.Combine(Environment.CurrentDirectory, "Plugins");
- var path = Environment.GetEnvironmentVariable("PATH") + ";" + pluginsPath;
- Environment.SetEnvironmentVariable("PATH", path, EnvironmentVariableTarget.Process);
在程序运行启动时,把对应的环境变量路径添加上Plugins的路径。运行起来,你会发现可以了。
三:关于CefSharp生成时产生的各种文件是否项目都必须用到的问题
比如,生成的文件中有扩展名为.pak的文件,pak文件是一种特殊的文件压缩格式。 比较常应用于游戏。 关于此类文件,在使用CefSharp也需要具备此类文件。
网上搜索:将locales及其下所有都设置为输出,里面有个en-US.pak文件,如没有,则应用程序会启动显示错误退出。再将devtools_resources.pak 设置为输出,否则调用devtools时将报错不能打开。(常见问题官网解释)
通过在CefSharp的源码中搜索也确实使用到了pak文件,所以此类文件也不可缺少。
四:关于CefSharp显示页面一直闪烁的问题。
具体查找过程就不详述,只能一把鼻涕一把泪的说 那也是一个煎熬的过程,不能说问题有多大,只能说很小的问题,没有找对方向。现在就把问题告诉大家。
经过和从GitHub下载的CefSharp3的源码进行对比,最终发现,我们未进行配置app.manifest文件的配置,大神们也许很多app.manifest文件的用途,而对于我这种不踩一两次坑,不长教训的,以前都会轻轻掠过这种东西。
app.manifest的文件可以通过添加文件来自动生成。也可以从源码中拷贝过来。然后在项目的属性=》应用(Application)中进行Resources=》Manifest配置上。下面描述app.manifest的描述的功能。
其中主要 代码片段入下:
- 1 <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
- 2 <application>
- 3 <!-- A list of the Windows versions that this application has been tested on and is
- 4 is designed to work with. Uncomment the appropriate elements and Windows will
- 5 automatically selected the most compatible environment. -->
- 6
- 7 <!-- Windows Vista -->
- 8 <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
- 9
- 10 <!-- Windows 7 -->
- 11 <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
- 12
- 13 <!-- Windows 8 -->
- 14 <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
- 15
- 16 <!-- Windows 8.1 -->
- 17 <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
- 18
- 19 <!-- Windows 10 -->
- 20 <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
- 21
- 22 </application>
- 23 </compatibility>
- 24
- 25 <!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
- 26 DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
- 27 to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
- 28 also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
- 29
- 30 <application xmlns="urn:schemas-microsoft-com:asm.v3">
- 31 <windowsSettings>
- 32 <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
- 33 </windowsSettings>
- 34 </application>
app.manifest的主要代码片段
自动添加app.manifest时会有对每个标签的描述信息,根据描述信息可以得知true其实是设置适应高高dpi对程序的影响。目前很多电脑都是高dpi的。
应该是 不同操作系统下,渲染机制是有区别的,所以为了更好的适应高dpi和不同系统的影响,所以最好不同的系统可以按照自己专属的环境进行处理,而supportedOs标签的设置解决了这类问题。
五:关于Windows8.1系统运行程序 页面依旧闪烁的原因。
在配置好app.manifest之后,以为可以源码的解决了页面闪烁的问题。而后,被告知Windows8.1的系统却依旧闪烁。这下又懵逼了~~懵逼~~了~~~!!!
后来又仔细阅读关于CefSharp中的代码和各种配置,在源码CefSharp3的命令行参数CefCommandLineArgs的配置中惊奇的发现了下面一段代码:
- 1 var osVersion = Environment.OSVersion;
- 2 //Disable GPU for Windows 7
- 3 if (osVersion.Version.Major == 6 && osVersion.Version.Minor == 1)
- 4 {
- 5 // Disable GPU in WPF and Offscreen examples until #1634 has been resolved
- 6 settings.CefCommandLineArgs.Add("disable-gpu", "1");
- 7 }
CefSharp禁用GPU的命令行参数
其中,Major和Minor分别指代系统的主版本(大版本)、次版本(小版本)版本号。其中指定了Windows7系统会禁用 GPU。,突发奇想,是否windows8.1也是因为这个问题?然后开始验证。
所以,经查阅,各系统的对应版本如下:
系统的主版本、次版本
- 1 Windows 10 -- 10.0*
- 2 Windows Server 2016 Technical Preview -- 10.0*
- 3 Windows 8.1 -- 6.3*
- 4 Windows Server 2012 R2 -- 6.3*
- 5 Windows 8 -- 6.2
- 6 Windows Server 2012 --6.2
- 7 Windows 7 -- 6.1
- 8 Windows Server 2008 R2 -- 6.1
- 9 Windows Server 2008 -- 6
- 10 Windows Vista -- 6
- 11 Windows Server 2003 R2 -- 5.2
- 12 Windows Server 2003 -- 5.2
- 13 Windows XP 64-Bit Edition -- 5.2
- 14 Windows XP -- 5.1
- 15 Windows 2000 -- 5
系统的主版本、次版本
如上图得知,若判断是否为Windows8.1系统,判断osVersion.Version.Major == 6 && osVersion.Version.Minor == 3 即可,
但是不知源码中 为何要判断windows7的禁用GPU,在windows7下取消禁用GPU的测试,发现页面并未闪烁。
但是为了安全起见,并且身边没有window8和其他的系统,所以决定,应用CefSharp的时候,配置CefCommandLineArgs进行了只判断osVersion.Version.Major == 6的处理,即windows8.1、windows8、windows7等都禁用了GPU。
暂且不知,这以后是否会成为历史遗留问题~~~~~嘻嘻~~。
六:关于 调试状态运行程序不报错,但是页面却一直未显示的问题
直接运行exe程序,提示找不到 CefSharp.BrowserSubprocess.exe和CefSharp.Core.dll等missing的问题。
在操作cefsharp项目并拷贝文件到输出目录过程中,会未对CefSharp.BrowserSubprocess.exe进行处理,导致CefSharp对应的运行环境下未有CefSharp.BrowserSubprocess.exe文件。
而CefSharp的浏览器是需要依赖此文件的,所以,才会出现页面显示不出画面的问题。CefSharp.BrowserSubprocess.exe文件是安装Nuget时自动下载下来的,但是在项目应用过程中,并不会拷贝生成到特定的输出目录下,
所以,想把此文件进行保存下载,上次到源码管理中,生成的时候也自动复制到输出目录,以避免丢失此文件而报错的情况。
处理方式:
在项目中添加了Files文件,并把CefSharp.BrowserSubprocess.exe 放入其中,设置属性为Content和Copy Always
设置项目生成成功后的脚本,拷贝到运行环境目录 即可。
七:关于使用CefSharp起用摄像头的问题
为什么要针对这个问题拿出来说一说你呢?可能会有人问:调用摄像头不是js操作的问题吗?跟CefSharp有什么关系。 请往下看eb( ̄▽ ̄)d
因项目要显示的网页中 有一个需要打开摄像头拍照的功能,js的代码如下:
- 1 var mediaStream = null, track = null;
- 2 var video;
- 3 var n = 0;
- 4 navigator.getMedia = (navigator.getUserMedia ||
- 5 navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
- 6 navigator.msGetUserMedia);
- 7 if (navigator.getMedia) {
- 8 navigator.getMedia(
- 9 {
- 10 video: true
- 11 },
- 12 // successCallback
- 13 function (stream) {
- 14 var s = window.URL.createObjectURL(stream);
- 15 video = document.getElementById('video');
- 16 video.src = window.URL.createObjectURL(stream);
- 17 mediaStream = stream;
- 18 track = stream.getTracks()[0];
- 19 $scope.photoBtnDiable = false; $scope.$apply();
- 20 },
- 21 // errorCallback
- 22 function (err) {
- 23 $scope.errorPhoto();
- 24 console.log("The following error occured:" + err);
- 25 });
- 26 } else {
- 27 $scope.errorPhoto();
- 28 }
- 29 //拍照
- 30 $scope.snap = function () {
- 31 var canvas1 = document.getElementById('canvas1');
- 32 var canvas2 = document.getElementById('canvas2');
- 33
- 34 var ctx1 = canvas1.getContext('2d');
- 35 var ctx2 = canvas2.getContext('2d');
- 36
- 37 ctx1.drawImage(video, 0, 0, canvas1.width, canvas1.height);
- 38 n++;
- 39 if (n % 2 == 0) {
- 40 ctx2.drawImage(video, 0, 0, canvas2.width, canvas2.height);
- 41 } else {
- 42 ctx1.drawImage(video, 0, 0, canvas1.width, canvas1.height);
- 43 }
- 44 //$uibModalInstance.close(canvas.toDataURL("image/png"));
- 45 };
- 46 //关闭摄像头
- 47 $scope.closeCamera = function () {
- 48 if (mediaStream != null) {
- 49 if (mediaStream.stop) {
- 50 mediaStream.stop();
- 51 }
- 52 $scope.videosrc = "";
- 53 }
- 54 if (track != null) {
- 55 if (track.stop) {
- 56 track.stop();
- 57 }
- 58 }
- 59 }
JS调用摄像头拍照代码
测试运行后发现在CefSharp使用,不起作用。因为我们访问页面一开始是通过file:///url的方式进行本地访问的。有经验的朋友会发现,浏览器很多(比如调动摄像头)的操作只支持安全源访问,并且有的浏览器会提示是否允许启动摄像头。
为了能使用摄像头所以需要启动web 服务,以http或者localhost的方式进行访问。因为,通过HttpListener搭建了个Web 服务,然后测CefSharp3的页面依旧不可以调用摄像头,经过通过其他浏览器调用显示没有问题。
这下又一头雾水,经过查阅资料,滚滚洪水中找到了那一颗螺丝钉,又位使用过CefSharp的一位 博主的博客 的说明中提到了 CefSharp要进行参数配置的如下:
在CefCommandLineArgs添加了enable-media-stream参数,意思是启用媒体流
- //主要是配置开启Media的命令参数,此配置可以允许摄像头打开摄像
- settings.CefCommandLineArgs.Add("enable-media-stream", "1");