你的位置:首页 > ASP.net教程

[ASP.net教程]关于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 "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 higher26    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 "urn:schemas-microsoft-com:asm.v3">31   <windowsSettings>32    <dpiAware "http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>33   </windowsSettings>34  </application>

app.manifest的主要代码片段 

 自动添加app.manifest时会有对每个标签的描述信息,根据描述信息可以得知<dpiAware>true</dpiAware>其实是设置适应高高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 resolved6         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 -- 610 Windows Vista -- 611 Windows Server 2003 R2 -- 5.212 Windows Server 2003 -- 5.213 Windows XP 64-Bit Edition -- 5.214 Windows XP -- 5.115 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: true11       },12       // successCallback13       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       // errorCallback22       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");

    忆往昔岁月,已然犹记此些。

 

结后语:

     第一次认真编写博客,排版等还有待完善。希望各位博友多鼓励,多指教。

     我近期设立了一个小目标:(比如 先赚他一个亿,哈哈) 多发博文和博友互动,整理思路,弥补不足,多学习开源,参与开源。忘自己以后会越来越牛~~~~~~~