Chromium Embedded Framework (CEF) is an open source project created in 2008 as a Web browser control operating on Google’s Chromium.
At the moment, it is quite a powerful tool for developing desktop applications, with a list of solutions that use this control can be found
here . But suffice it to say that well-known products like Evernote and Steam use it.
So what does this framework do?- CEF allows you to create your own protocol handlers, thus implementing your “private” encryption algorithm (yes
yes, unfortunate users of old Internet Explorer and corporate web solutions, down with ActiveX). This can also be used to load data from static program resources. - CEF allows you to make a wrapper on native functions in the object space of the Javascript virtual machine. Resource-intensive processing of large data arrays can be shifted to more stringent and faster programming languages.
- CEF allows you to handle navigation events, file downloads, and so on.
In general, everything that allows you to make your own browser like google chrome (just why?). But we will need it in order to create our own applications with HTML5 / CSS3 interface and hardware accelerated graphics.
And now about the sad
The
chromiumembedded library, referenced at the beginning of the article, is implemented in C ++. But what if your solution is already working on another, controlled programming language? Especially for us there are wrappers for Java, Delphi, Python and .NET. On the use of the library CefSharp for .NET and will be discussed.
')
Meet CefSharp
CefSharp is a wrapper library for chromiumembedded. However, its functionality is
somewhat inferior in its capabilities.
What is available to us:
- Creating an unlimited number of WebView class components
- Event handling on page loading, navigation events
- Own protocol handlers.
- Implementing js code at page time
- Creating global [native code] objects with static methods
What is missing:
- Normal event model. No, seriously, it does not look like a .NET library:
public Window(string Url, CefSharp.BrowserSettings settings = null) {
- Implementing various objects for different instances of the WebView class. You will have to accept the fact that anyone can call absolutely any method you have registered.
- Type conversion from CLR to JS and back. The crutch for a visual solution to this problem will be discussed further.
- You cannot associate the called method with a specific form where the WebView is located.
- Not really a minus, but the environment for compiling CefSharp should be Visual Studio 2008.
As a reward, you can note that you can start working with the library fairly quickly (provided that you have a compiled version), as well as the fact that the bar for understanding HTML + JS solutions is lower than that of WPF, and for those afraid to go to the months-long study of complex technology, you can simply use your favorite HTML.
First meeting
The three main things that are needed for work are the local protocol handler, the global object and the object that we will have to manage the framework.
Local Protocol Handler
A pair of classes is implemented: a factory (implements the CefSharp.ISchemeHandlerFactory interface) and, in fact, the handler itself (implementing the CefSharp.ISchemeHandler interface).
The first is clear:
public class LocalSchemeHandlerFactory : ISchemeHandlerFactory { public ISchemeHandler Create() { return new LocalSchemeHandler(); } }
The second will not be more difficult:
public class LocalSchemeHandler : ISchemeHandler {
In order to connect the js-file of the application, you can use the GetStream method or GetString method of the ResourceManager class. Of the benefits - the source code of your application will be located inside the .exe or .dll file. Of the minuses - when changing the js-code, you will have to re-compile the application each time.
Bridge object between .NET and JS
It is even simpler with it - it is a regular object containing methods and fields. One minus - for the whole project you will have one copy of each such class.
CEF Initialization
I decided to make the class a successor to ApplicationContext. For windowing, WinForms runs faster, and WPF is not necessary to pull
public class ApplicationController : ApplicationContext { protected Dictionary<string, object> registeredObjects; public ApplicationController(CefSharp.Settings settings = null) { registeredObjects = new Dictionary<string, object>(); string resources = Path.Combine(Directory.GetCurrentDirectory(), "cache"); if (Directory.Exists(resources)) Directory.CreateDirectory(resources); CefSharp.CEF.Initialize(settings ?? new CefSharp.Settings() { Locale = "ru", CachePath = resources }); CefSharp.CEF.RegisterScheme("local", new LocalSchemeHandlerFactory()); registerJsObject("Form", new WindowObject());
On this, in fact, everything. You can create a form, add a WebView component to it, and work as you please.
If you have read this far, then you are a patient person and I am grateful to you.But this is not enough for us
As I noted earlier, there are some drawbacks to CefSharp. For example, you cannot associate a WebView component with a form that contains it. For this, a kind of
cruel crutch was born, which I will present to the public.
In order not to litter the article with code patches, I will give some excerpts from the listing.
1 New Window class inherited from Form
- It contains a subclass of FormEnumerator, which assigns each window its own unique string identifier and stores references to all Window objects. You can get a form using the getWindowById method.
- Static JSInvoke method that receives a call from the browser environment and calls the form function
- CSInvoke method that calls the JS environment method from .NET
- Private method getFormRefrection, which creates a wrapper for the CLR form methods. The string is formed by StringBuilder based on the reflection data. It looks like this:
2 Common Call Bridge Object
It performs a call operation from JS to C #:
public class WindowObject { public string Invoke(string Id, string Method, string JSONData) { object x = JSON.JsonDecode(JSONData); if (x is ArrayList) { ArrayList y = (ArrayList)x; object[] args = new object[y.Count]; for (var i = 0; i < args.Length; i++) args[i] = y[i]; return JScripter.CreateString(Window.JSInvoke(Id, Method, args)); } else return JScripter.CreateString(Window.JSInvoke(Id, Method, new object[] { x })); } public void Close(string Id) { Window.FormEnumerator.getWindow(Id).Close(); } }
The attentive reader will notice something amiss: instead of normal objects, CefSharp allows you to exchange only simple types, such as int, double, bool, string. But in real life it is usually just the opposite. Therefore, this crutch uses data packing / unpacking in JSON. The solution is not ideal, a lot of time is wasted, but these are the limitations of the library.
Since DataContractJsonSerializer only works with certain types, it is problematic to use it. Therefore, a 100% managed parser was used in the project. Also a crutch.
The code can be found
here .