当我们为基于 .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