-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Frequently asked questions
Large parts of this document will be replaced with the new General Usage Guide - A must read!
This FAQ has ~ 10 items. It tries to showcase some cool CefSharp features and some of the most common gotcha's.
For more hints see the growing list of issues labeled as faq-able!
- 1. How do you call a Javascript method from .NET?
- 2. How do you call a Javascript method that return a result?
- 3. How do you expose a .NET class to Javascript?
- 4. "Could not load file or assembly 'CefSharp.Core.dll' or one of its dependencies.
- 5. Why does the Visual Studio WPF designer not work when I add a ChromiumWebBrowser to my app?
- 6. How do I include the Visual Studio C++ 2012/2013 redistributables on the target app?
- 7. How do I override Javascript
window.alert
actions and similar? - 8. Where are the CefSharp3 binaries?
- 9. Windows XP/2003 Support?
- 10. What files do I need to include when I redistribute an application that uses CefSharp?
- 11. Build process can't copy CEF files
- 12. Why does IndexedDB return a QuotaExceededError?
- 13. How do you handle a Javascript event in C#?
- 14. How can I implement WinForms drag & drop?
- 15. WPF blurry rendering
Simple code may look something like this:
var script = string.Format("document.body.style.background = '{0}'", colors[color_index++]);
if (color_index >= colors.Length)
{
color_index = 0;
}
browser.GetMainFrame().ExecuteJavaScriptAsync(script);
Unfortunately OnFrameLoadStart
is called before the DOM is loaded, so you'll need to use one of the follow: FrameLoadEnd/LoadingStateChanged/IRenderProcessMessageHandler.OnContextCreated
. Here are a few examples
browser.RenderProcessMessageHandler = new RenderProcessMessageHandler();
public class RenderProcessMessageHandler : IRenderProcessMessageHandler
{
// Wait for the underlying `Javascript Context` to be created, this is only called for the main frame.
// If the page has no javascript, no context will be created.
void IRenderProcessMessageHandler.OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame)
{
const string script = "document.addEventListener('DOMContentLoaded', function(){ alert('DomLoaded'); });";
frame.ExecuteJavaScriptAsync(script);
}
}
//Wait for the page to finish loading (all resources will have been loaded, rendering is likely still happening)
browser.LoadingStateChanged += (sender, args) =>
{
//Wait for the Page to finish loading
if (args.IsLoading == false)
{
browser.ExecuteJavaScriptAsync("alert('All Resources Have Loaded');");
}
}
//Wait for the MainFrame to finish loading
browser.FrameLoadEnd += (sender, args) =>
{
//Wait for the MainFrame to finish loading
if(args.Frame.IsMain)
{
args.Frame.ExecuteJavaScriptAsync("alert('MainFrame finished loading');");
}
};
Note: Scripts are executed at the frame level, ever page has at least one frame (MainFrame
). The IWebBrowser.ExecuteScriptAsync
extension method is left for backwards compatibility, you can use it as a shortcut to execute js
on the main frame.
If you need to evaluate code which returns a value, use the Task<JavascriptResponse> EvaluateScriptAsync(string script, TimeSpan? timeout)
method. Javascript code is executed asynchronously and as such uses the .Net Task
class to return a response, which contains error message, result and a success (bool) flag.
// Get Document Height
var task = frame.EvaluateScriptAsync("(function() { var body = document.body, html = document.documentElement; return Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight ); })();", null);
task.ContinueWith(t =>
{
if (!t.IsFaulted)
{
var response = t.Result;
EvaluateJavaScriptResult = response.Success ? (response.Result ?? "null") : response.Message;
}
}, TaskScheduler.FromCurrentSynchronizationContext());
For a more detailed example check out this Gist
Notes
- Scripts are executed at the frame level, ever page has at least one frame (
MainFrame
) - Only trivial values can be returned (like int, bool, string etc) - not a complex (user-defined) type which you have defined yourself. This is because there is no (easy) way to expose a random Javascript object to the .NET world, at least not today. However, one possible technique is to turn the Javascript object you wish to return to your .NET code into a JSON string with the Javascript JSON.toStringify() method and return that string to your .NET code. Then you can decode that string into a .NET object with something like JSON.net. See this MSDN link for more information. (https://msdn.microsoft.com/en-us/library/ie/cc836459(v=vs.94).aspx)
See General Usage Guide - How do I expose a .Net class
4. Why do I get an error about "Could not load file or assembly 'CefSharp.Core.dll' or one of its dependencies. The specified module could not be found." when trying to run my CefSharp-based application? It compiles successfully, but does not run? It runs on my developer machine, though throws an exception when I copy it to another computer?
This is a common error, typically one of the following
- VC++ 2013/2015 Redistributable Package is required in order to run CefSharp on non developer machines. See FAQ #6 below for more information. You can include the required dlls as part of your application.
- Not all dependencies are present in the executing folder.
CefSharp
includesunmanaged
dll's and resources, these are copied to the executing folder via two.props
file which are included in your project when you install theNuget
packages. See list of required files below, make sure the required files are present. - You packaged your application for distribution via an installer and it doesn't run on the target machine. Installers don't include the
unmanaged
resources by default, you'll need to add them manually. ForClickOnce
, see #1314 for some pointers and solutions other users have come up with. - "Generate serialization assembly" is set to "On" or "Auto" in Properties->Build tab. Setting this to "Off" seems to resolve the issue.
A list of required files can be found here: Output files description (Redistribution)
NOTE: This also applies if you get a FileNotFoundException
when initializing the WPF
control in XAML
.
NOTE 2: If compiling from source (not recommended, use the Nuget
packages) and you notice that you can no longer build in debug mode, but release builds work just fine you may need to repair your version of Visual Studio. This happens in rare cases where you will get the same exact message as a missing unmanaged .dll file as shown above.
NOTE Version 57.0.0
adds minimal designer support, see https://github.com/cefsharp/CefSharp/pull/1989 for details.
Older version do not support the WPF
designer this is unfortunate, and a consequence of the fact that CefSharp is written in C++ code which in turn references CEF DLL:s. That scenario isn't so well-supported by the Visual Studio designer. This forum thread explains it a bit further and also gives a possible workaround. I think (unfortunately) that the easiest workaround is to just accept the fact that it's not easy to support and just live with it, but there are also other approaches if you really feel you need it.
6. How do I include the Visual Studio C++ 2012/2013 redistributables on the target app?
CefSharp
requires the Microsoft VC++ Runtime
.
CefSharp Version | VC++ Version | .Net Version |
---|---|---|
64.0.0 and above | 2015 | 4.5.2 |
51.0.0 to 63.0.0 | 2013 | 4.5.2 |
45.0.0 to 49.0.0 | 2013 | 4.0.0 |
43.0.0 and below | 2012 | 4.0.0 |
For Microsoft's
official guide see Redistributing Visual C++ Files on MSDN
. To download visit Visual Studio C++ 2012/2013/2015 redistributables
A brief summary of your options for installing/including VC++
with your application are:
- Install the
Microsoft Visual C++ Redistributable Package
on every machine on which you wish to run yourCefSharp
based application. Once installed updates can then be managed viaWindows Update
. - You can either set the
Visual Studio C++
redistributables as pre-requisites of the installer (i.e. ClickOnce or WiX Toolset) - Copying over to your project the contents of this folder (Only present if you have the matching version of Visual Studio installed on your computer):
# For VC++ 2012 (x86)
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\redist\x86\Microsoft.VC110.CRT
# For VC++ 2012 (x64)
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\redist\x64\Microsoft.VC110.CRT
# For VC++ 2013 (x86)
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\x86\Microsoft.VC120.CRT
# For VC++ 2013 (x64)
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\x64\Microsoft.VC120.CRT
# For VC++ 2015 (x86)
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT
# For VC++ 2015 (x64)
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.CRT
With the 3rd approach you won't need to install the prerequisite Visual C++ 2012/2013/2015 Runtime Files
to your client. If you build your own from source what you deploy of course has to match your build environment. For the official Nuget
releases see the Releases Branches table for details.
For VC++ 2013
JetBrains
have a Microsoft.VC120.CRT.JetBrains package on Nuget.org
, appears to include the relevant files, you would still need to hook up some sort of post build task to copy them to your bin folder.
Note When building from source make sure you compile in Release
mode when deploying to machines that don't have Visual Studio
installed. Visual C++
uses a different set of run-time libraries for Debug and Release builds. The Debug
run-time libraries are only installed with Visual Studio
. If you use the official Nuget packages
they're already built in Release
mode and you can subsequently build your app in debug mode as only the Visual C++
projects need to be compiled in Release
mode.
The ChromiumWebBrowser
controls provide a property called JsDialogHandler
. This property can be set to a class of yours, which can be used to override the behavior of these dialogs.
CefSharp3 is released as Nuget
packages. Please see https://github.com/cefsharp/CefSharp/blob/master/README.md#nuget-packages for the latest stable and pre-release versions.
For a very simple example projects using the NuGet packages, see the CefSharp.MinimalExample repository. Clone it/download the source if you want a really small and simple example of how CefSharp3 can be used.
_Please Note: Platform Target
CefSharp
supports AnyCPU
for version 51.0.0
and above, see https://github.com/cefsharp/CefSharp/issues/1714 for details.
The simplest option to get up and running quickly is to either select x86
or x64
. See Steps to configure your solution here.
We no longer support Windows XP/2003
. The official Nuget
packages are not tested on Windows XP/2003
. You may have some luck using newer versions that target VC++ 2013
(There is a known issue with the VC++ 2012 packages
). (Remembering that the CefSharp.Example.*
projects are compiled using .Net 4.5
, so you'll have to re-target them and update code accordingly should you wish to use the provided examples).
Please don't create new issues related to Windows XP
. The following https://github.com/cefsharp/CefSharp/wiki/Windows-XP-No-Longer-Supported Wiki
page may be useful and can be edited by anyone with a GitHub
account, so feel free to contribute any information you think may prove useful.
See: Output files description (Redistribution)
Sometimes the build process can't copy CEF files and will retry many times before it finally fails. This happens when CefSharp.BrowserSubprocess.exe is still running even though the application was closed.
The solution is to manually kill the CefSharp.BrowserSubprocess.exe using i.e. Task Manager.
When working with IndexedDB you must set the CefSettings CachePath to a directory where the current user has write access. In most cases you can use code like this to create a cache directory and pass the setting to Cef during Initialize():
// On Win7 this will create a directory in AppData.
var cache = System.IO.Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), System.IO.Path.Combine("MyApplication","cache"));
if (!System.IO.Directory.Exists (cache))
System.IO.Directory.CreateDirectory (cache);
// Set the CachePath during initialization.
var settings = new CefSettings(){CachePath = cache};
Cef.Initialize (settings);
public class BoundObject
{
public void OnFrameLoadEnd (object sender, FrameLoadEndEventArgs e)
{
if(e.Frame.IsMain)
{
browser.ExecuteScriptAsync(@"
document.body.onmouseup = function()
{
bound.onSelected(window.getSelection().toString());
}
");
}
}
public void OnSelected(string selected)
{
MessageBox.Show("The user selected some text [" + selected + "]");
}
}
// ...
// After your ChromiumWebBrowser has been instantiated (for WPF directly after `InitializeComponent();` in the control constructor).
var obj = new BoundObject();
browser.RegisterJsObject("bound", obj);
browser.FrameLoadEnd += obj.OnFrameLoadEnd ;
Please note:
-
RegisterJsObject
should be called directly after you create an instance ofChromiumWebBrowser
-
"bound"
is the name of the object that will be created as a top-level object accessible to any JS scripts running on the webpage. You can use whatever you like, but ensure it does not conflict with an existing JS object, and that the names match in C# and JS. - This sample injects JS code into the webpage on-the-fly using the
FrameLoadEnd
event. If you have control of the webpage, you can simply add the JS code into the webpage. - We call the
OnSelected
C# method with a single string argument. You can use any number of arguments, or none, but they must all be primitive types. - Ensure you cast complex JS objects to strings or convert them to JSON when passing them.
- Since CefSharp will be searching for the method
OnSelected
by its name, ensure your obfuscator does NOT rename the method to something else, or it will not be executed.
One possible solution is outlined in https://github.com/cefsharp/CefSharp/issues/1593#issuecomment-304451518
NOTE: If you are struggling with blurry rendering, try setting UseLayoutRounding=true
on the parent of the ChromiumWebBrowser ... or even on an ancestor farther up the visual tree as this has helped others in the past.
From the V55 the default BitmapScalingMode
is HighQuality
, so the layout of the ChromiumWebBrowser
has to be aligned to the pixel, see #1931. The solution is:
- Either set
RenderOptions.BitmapScalingMode="NearestNeighbor"
. - Try
UseLayoutRounding="True"
to the proper parent container. Keep in mind it disables anti-aliasing for children, so set it toFalse
for the children of the container where you want to keep the anti-aliasing, see #166