当我们为基于 .NET Framework 的 WinForm 程序增加 CefSharp.WinForms 依赖后,可能会遇到以下报错信息:
CefSharp.Common is unable to proceeed as your current Platform is ‘AnyCPU’. To target AnyCPU please read https://github.com/cefsharp/CefSharp/issues/1714. Alternatively change your Platform to x86 or x64 and the relevant files will be copied automatically. For details on changing your projects Platform see https://docs.microsoft.com/en-gb/visualstudio/ide/how-to-configure-projects-to-target-platforms?view=vs-2017 CefSharpDemo
CefSharp.Common.targets
我们可以通过“配置管理器”为项目设置具体的 x86 或 x64 目标平台来消除该报错,同时我们也可以通过一些设置让我们的程序同时支持 x86 和 x64 目标平台。本篇将简述如何为依赖来 CefSharp.WinForms 的程序启用 AnyCPU 支持。英文版的操作步骤可参见:Feature Request – Add AnyCPU Support 。
为依赖 CefSharp 的程序增加 AnyCPU 支持
笔者新建了一个名为 CefSharpDemo 的 WinForm 项目,并通过 NuGet 引入 CefSharp.WinForms 组件:
首先,在项目上点击右键,选择“卸载项目”,在项目卸载成功后,再次点击右键,选择“编辑项目文件”。之后会看到以下类似的窗体:
在第一个 PropertyGroup
标签里增加 <CefSharpAnyCpuSupport>true</CefSharpAnyCpuSupport>
代码段:
修改完成之后,再次在项目文件上右击,选择“重新加载项目”。此番操作之后,项目的生成就不会报错了,可以发现在 bin 目录中多出了名为 x86 和 x64 的两个文件夹,分别对应 32 位和 64 位的应用程序。接下来我们需要在程序中根据运行环境,动态选择要加载的文件。
CefSharp 依赖文件的动态加载
我们可以通过 AppDomain.CurrentDomain.AssemblyResolve
事件将应用程序所需的组件注册进来。以下代码实现了该功能,并且根据是否 64 位进程选择具体加载哪个目录中的程序集:
using CefSharp; using CefSharp.WinForms; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows.Forms; namespace CefSharpDemo { static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); AppDomain.CurrentDomain.AssemblyResolve += Resolver; LoadApp(); } [MethodImpl(MethodImplOptions.NoInlining)] private static void LoadApp() { var settings = new CefSettings(); // Set BrowserSubProcessPath based on app bitness at runtime settings.BrowserSubprocessPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, Environment.Is64BitProcess ? "x64" : "x86", "CefSharp.BrowserSubprocess.exe"); // Make sure you set performDependencyCheck false Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: null); var frmMain = new FrmMain(); Application.Run(frmMain); } // Will attempt to load missing assembly from either x86 or x64 subdir private static Assembly Resolver(object sender, ResolveEventArgs args) { if (args.Name.StartsWith("CefSharp")) { string assemblyName = args.Name.Split(new[] { ',' }, 2)[0] + ".dll"; string archSpecificPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, Environment.Is64BitProcess ? "x64" : "x86", assemblyName); return File.Exists(archSpecificPath) ? Assembly.LoadFile(archSpecificPath) : null; } return null; } } }
至此,我们便实现了对 CefSharp 的 AnyCPU 支持。
CefSharp 应用支持 AnyCPU 的优缺点与解决方案
众所周知,相对于 32 位应用程序,64 位的应用拥有更快运行的运行速度,以及支持更多的内存。相对于分别对 x86 和 x64 分别发包的方式,AnyCPU 发包减少了运维成本,同时降低了客户下载程序包之后不能运行的风险。
但是 AnyCPU 的弊端也是很明显的:64 位的 libcef.dll
文件达到了 110兆,即便是本文所示的简单示例,全部文件的大小也达到了三百多兆:
要解决这个问题,我们可以在程序运行时动态去下载对应的依赖文件,该下载操作只要确保在调用 CefSharp 之前即可。
源代码
本文源代码:https://gitee.com/coderbusy/demo/tree/master/WinForm/CefSharpDemo