Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request - Add AnyCPU Support #1714

Closed
amaitland opened this issue Jun 17, 2016 · 5 comments

Comments

@amaitland
Copy link
Member

commented Jun 17, 2016

Doing some research on another topic I believe I've come up with a method of making AnyCPU a reality without introducing maintainable code to the framework.

With 60a1e61 it's now possible to target AnyCPU, it's important to note that it won't just magically work out of the box like x86 and x64 will. Attempting to build using AnyCPU will throw an error directing you to this issue.

You must add <CefSharpAnyCpuSupport>true</CefSharpAnyCpuSupport> to the first <PropertyGroup> in your project (e.g. .csproj file) and pick one of the two options below.

Option 1

This option effectively makes your AnyCPU program always run as x86, this is the simplest option.

[STAThread]
public static void Main()
{
    var settings = new CefSettings();
    settings.BrowserSubprocessPath = @"x86\CefSharp.BrowserSubprocess.exe";

    Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: null);

    var browser = new BrowserForm();
    Application.Run(browser);
}

Example probingPath

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath="x86"/>
    </assemblyBinding>
</runtime>

Option 2

Add a dependency resolver, this is more complicated and needs to be hooked up before any calls to classes in the CefSharp.* namespaces. Here is one method of doing this, I'll add some helper classes in the future. It's important that LoadApp is not in-lined, so the calls to CefSharp are delayed long enough to hookup the Assembly Resolver

  • Add <CefSharpAnyCpuSupport>true</CefSharpAnyCpuSupport> to the first <PropertyGroup> in your project (e.g. .csproj file).
  • Implement the code below (modifying any setting that you require).
[STAThread]
public static void Main()
{
    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 browser = new BrowserForm();
    Application.Run(browser);
}

// 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;
}
@amaitland

This comment has been minimized.

Copy link
Member Author

commented Jun 20, 2016

Example WPF solution.

You must add <CefSharpAnyCpuSupport>true</CefSharpAnyCpuSupport> to the first <PropertyGroup> in your project (e.g. .csproj file).

public partial class App : Application
{
    public App()
    {
        //Add Custom assembly resolver
        AppDomain.CurrentDomain.AssemblyResolve += Resolver;

        //Any CefSharp references have to be in another method with NonInlining
        // attribute so the assembly rolver has time to do it's thing.
        InitializeCefSharp();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void InitializeCefSharp()
    {
        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);
    }

    // Will attempt to load missing assembly from either x86 or x64 subdir
    // Required by CefSharp to load the unmanaged dependencies when running using AnyCPU
    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;
    }
}
@amaitland

This comment has been minimized.

Copy link
Member Author

commented Jun 20, 2016

I've updated the original issue at the top here to include details about adding a CefSharpAnyCpuSupport property to your project file. This is only required for AnyCPU support and will through an error as part of your build process until it's been added. The error directs people to this issue.

@amaitland

This comment has been minimized.

Copy link
Member Author

commented Aug 26, 2016

See https://github.com/cefsharp/CefSharp.MinimalExample/tree/demo/anycpu for the original demo project that I used for testing. I currently have no plans on maintaining this branch, it's purely provided as a working example. See the commit log to see exactly what changes were made to get things working.

@cefsharp cefsharp locked and limited conversation to collaborators Sep 8, 2016

@amaitland

This comment has been minimized.

Copy link
Member Author

commented Apr 21, 2017

There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "CefSharp.Core", "x86".

The above warning in your Error List is safe to ignore. If you wish to disable this warning see http://thebuildingcoder.typepad.com/blog/2013/06/processor-architecture-mismatch-warning.html

@amaitland

This comment has been minimized.

Copy link
Member Author

commented Jul 13, 2018

You must add <CefSharpAnyCpuSupport>true</CefSharpAnyCpuSupport> to the first <PropertyGroup> in your project (e.g. .csproj file).

Option 3

I've not tested this option in any great detail, so use at you own risk. It requires the user to have Write Permission to the applications executing folder. No special resolves or methods of loading the dlls is required. The relevant files are copied to the executing folder and unused resources deleted

  • Copies CefSharp resources from the x86 or x64 folder depending on application bitness
  • Removes x86 and x64 folders

NOTE

  • If you upgrade/downgrade you will need to manually remove the libs from the executing folder (A simple Git Cleanup would suffice)
  • This option would be best used when deploying using an installer.
//Example WinForms Code (WPF  would be very similar)
[STAThread]
public static void Main()
{
	var executingFolder = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;

	//If libcef.dll doesn't exist in our executing folder then we'll copy the files
	//For this method the user must have write permissions to the executing folder.
	if (!File.Exists(Path.Combine(executingFolder, "libcef.dll")))
	{
		var libPath = Path.Combine(executingFolder, Environment.Is64BitProcess ? "x64" : "x86");

		CopyFilesRecursively(new DirectoryInfo(libPath), new DirectoryInfo(executingFolder));

		Directory.Delete("x86", recursive: true);
		Directory.Delete("x64", recursive: true);
	}

	LoadApp();
}

//https://stackoverflow.com/a/58779/4583726
private static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
	foreach (DirectoryInfo dir in source.GetDirectories())
	{
		CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
	}

	foreach (FileInfo file in source.GetFiles())
	{
		file.CopyTo(Path.Combine(target.FullName, file.Name));
	}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void LoadApp()
{
	//Perform dependency check to make sure all relevant resources are in our output directory.
	var settings = new CefSettings();

	Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: null);

	var browser = new BrowserForm();
	Application.Run(browser);
}
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
1 participant
You can’t perform that action at this time.