Migrating DotNetBrowser from version 1.x to 2.0

DotNetBrowser version 2.0 brings major improvements to both internal features and public API of the library. This guide shows how to make your application code written with DotNetBrowser version 1.x compatible with version 2.0.

Overview

Why migrate?

We recommend you to update your code to the latest version because all the new features, Chromium upgrades, support of new operating systems and .NET Framework versions, bug fixes, security patches, performance, and memory usage enhancements are applied on top of the latest version.

How long does it take?

From our experience, upgrade to a new major version may take from a couple of hours to a few days depending on the number of the features you use in your application. As usual, we strongly recommend to test your software after the upgrade in all the environments it supports.

Getting help

Please check out the FAQ section at the end of this guide. Maybe the answer to your question is already there.

In case you did not find the answer in this guide and you need assistance with migration, please contact us. We will be happy to help.

Key changes

API & architecture

In DotNetBrowser version 2.0, the architecture of the library has been improved. Now DotNetBrowser allows you to create two absolutely independent IBrowser instances and control their life cycle. Along with this internal architecture change, public API has been improved and extended with new features.

Refer to the Mapping section to find out how the main functionality of version 1.x maps to version 2.0.

System requirements

Starting with this version, DotNetBrowser no longer supports .NET Framework 4.0. Current system requirements are available here.

Assemblies

The structure of the library has changed. Now the library consists of the following assemblies:

  • DotNetBrowser.dll — data classes and interfaces;
  • DotNetBrowser.Core.dll — core implementation;
  • DotNetBrowser.Logging.dll — DotNetBrowser Logging API implementation;
  • DotNetBrowser.WinForms.dll — classes and interfaces for embedding into a WinForms application;
  • DotNetBrowser.Wpf.dll — classes and interfaces for embedding into a WPF app;
  • DotNetBrowser.Chromium.Win-x86.dll — Chromium 32-bit binaries for Windows;
  • DotNetBrowser.Chromium.Win-x64.dll — Chromium 64-bit binaries for Windows.

Basic concepts

The way to create and dispose various objects has changed in the new API.

To create an IEngine object use the EngineFactory.Create() static method:

Engine engine = EngineFactory.Create(options);

To create an immutable data object use the <Object>.Builder.Build() method:

EngineOptions engineOptions = new EngineOptions.Builder()
{
    Language = Language.EnglishUs
}
.Build();

Every object that should be disposed manually implements the IDisposable interface, for example, IBrowser. To dispose the object, call the IDisposable.Dispose() method:

browser.Dispose();

Some service objects depend on the other service objects. When you dispose a service object, all the service objects depending on it will be disposed automatically, so you do not need to dispose them manually. Refer to Architecture guide for more details about the key objects and their interaction principles.

Handlers

Handlers are used to process various callbacks from the Chromium engine and modify its behavior.

v1.x

In DotNetBrowser version 1.x, handlers are usually implemented as the properties of interface types, such as PrintHandler, PermissionHandler, LoadHandler, and others.

v2.0

In DotNetBrowser version 2.0, all the handlers are implemented as the properties of type IHandler<in T> or IHandler<in T, out TResult>, where T is the parameters type, TResult is the result type. The result returned from the handler will be used to notify the Chromium engine and affect its behavior.

To register and unregister a handler, use corresponding property setters and getters.

There are default implementations of the IHandler<in T, out TResult> interface:

  • Handler<T, TResult> class allows wrapping lambdas and method groups;
  • AsyncHandler<T, TResult> class allows wrapping async lambdas and method groups, or lambdas and method groups that return Task<TResult>.

In some cases, engine appears blocked until the handler returns the result. In case of using the AsyncHandler, engine appears blocked until the result is available in the returned task.

Thread safety

The library is not thread-safe, so please avoid working with the library from different threads at the same time.

Licensing

In DotNetBrowser version 2.0 the license format and the way how the library checks the license has been improved.

v1.x

In previous version, the license represented a teamdev.licenses text file. This text file contained the plain text of the license. To set up the license, you had to include this text file in your project as an EmbeddedResource or copy it to the working directory of the .NET application.

Project license was tied to a fully qualified type name (for example, Company.Product.MyClass). It could be any class of your application. The only requirement was that this class must be included in the classpath of your .NET app where you used DotNetBrowser.

v2.0

Now, the library requires a license key that represents a string with combination of letters and digits. You can add the license to your project through the API.

It is also possible to put this string to a text file named dotnetbrowser.license and include this text file in your project as an EmbeddedResource or copy it to the working directory of the .NET application.

The Project license is tied to a namespace now. The namespace is expected to be in the Company.Product.Namespace format. It should be a top-level namespace for the classes where you create an IEngine instance.

For example, if the Project license is tied to the Company.Product namespace, then you can create IEngine instances only in the classes located in this namespace or its inner namespaces, e. g. Company.Product.Namespace.

The IEngine was introduced in DotNetBrowser 2. We recommend that you check out the guide that describes the new architecture, how to create an IEngine and manage multiple IBrowsers lifecycle.

You can work with the created IEngine instance and make calls to the library’s API from the classes located in other namespaces without any restrictions.

Dropped functionality

Starting with this version DotNetBrowser no longer supports .NET Framework 4.

In the new version, the following functionality has been temporarily dropped:

  • Notifications API;
  • PasswordManager API;
  • Printing API;
  • HTML5 Application Cache API;
  • Displaying the BeforeUnload dialog when disposing IBrowser;
  • Drag&Drop IDataObject API;
  • Intercepting Drag&Drop events;
  • Intercepting and suppressing touch and gesture events.

We are going to provide the alternatives of the above-mentioned features in the next versions.

Mapping

In this section, we describe how the main functionality of DotNetBrowser version 1.x maps to version 2.0.

Engine

Creating Engine

1.x

The IEngine is not a part of the public API. It is created internally when the first Browser instance is created in the application. Only one IEngine can be created and used in the application.

2.0

The IEngine is a part of the public API now. You can create and use multiple Engine instances in the application. To create an Engine instance with the required options, use the code sample below:

IEngine engine = EngineFactory.Create(new EngineOptions.Builder
{
    // The language used on the default error pages and GUI.
    Language = Language.EnglishUs,
    // The absolute path to the directory where the data 
    // such as cache, cookies, history, GPU cache, local 
    // storage, visited links, web data, spell checking 
    // dictionary files, etc. is stored.
    UserDataDirectory = @"C:\Users\Me\DotNetBrowser"
}
.Build());

Each IEngine instance runs a separate native process where Chromium with the provided options is created and initialized. The native process communicates with the .NET process using the Inter-Process Communication (IPC) bridge. If the native process is unexpectedly terminated because of a crash, the .NET process will continue running.

You can use the following notification to find out when the IEngine is unexpectedly terminated, so that you can re-create the Engine and restore the IBrowser instances:

engine.Disposed += (s, e) => 
{
    long exitCode = e.ExitCode;
    if(exitCode != 0)
    {
        // Clear all the allocated resources and re-create the Engine.
    }
};

Closing Engine

1.x

The IEngine is disposed automatically when the last Browser instance is disposed in the application.

2.0

The IEngine should be disposed manually using the Dispose() method when it is no longer required:

engine.Dispose();

When the IEngine is disposed, all its IBrowser instances are disposed automatically. Any attempt to use an already disposed IEngine leads to an exception.

To find out when the IEngine is disposed, use the following event:

engine.Disposed += (s, e) => {};

Browser

Creating a browser

1.x

Browser browser = BrowserFactory.Create();

2.0

IBrowser browser = engine.CreateBrowser();

Creating a browser with options

1.x

BrowserContextParams parameters = new BrowserContextParams(@"C:\Users\Me\DotNetBrowser") 
{ 
    StorageType = StorageType.MEMORY 
};
BrowserContext context = new BrowserContext(parameters);
Browser browser = BrowserFactory.Create(context, BrowserType.LIGHTWEIGHT);

2.0

The BrowserContext functionality has been moved to IEngine. Thus, the same code should be replaced with the code sample below:

IEngine engine = EngineFactory.Create(new EngineOptions.Builder
{
    IncognitoEnabled = true,
    RenderingMode = RenderingMode.OffScreen,
    UserDataDirectory = @"C:\Users\Me\DotNetBrowser"
}
.Build());
IBrowser browser = engine.CreateBrowser();

Disposing a browser

1.x

browser.Dispose();

2.0

browser.Dispose();

Responsive/Unresponsive

1.x

browser.RenderResponsiveEvent += (s, e ) => {};
browser.RenderUnresponsiveEvent += (s, e) => {};

2.0

browser.BrowserBecameResponsive += (s, e ) => {};
browser.BrowserBecameUnresponsive += (s, e) => {};

BrowserView

In DotNetBrowser version 1.x, creating a BrowserView with its default constructor leads to initializing a bound Browser instance in the same thread. As a result, the main UI thread may appear blocked until the Browser instance is created and initialized, and this may take a significant amount of time.

In DotNetBrowser version 2.0, creating an IBrowserView implementation with its default constructor creates a UI control that is not initially bound to any browser. The connection with a specific IBrowser instance is established when it is necessary by calling InitializeFrom(IBrowser) extension method.

The IBrowserView implementations do not dispose the connected IBrowser or IEngine instances when the application is closed. As a result, the browser and engine will appear alive even after all the application windows are closed and prevent the application from terminating. To handle this case, dispose IBrowser or IEngine instances during your application shutdown.

Creating WinForms BrowserView

1.x

using DotNetBrowser.WinForms;
...
BrowserView view = new WinFormsBrowserView();

2.0

using DotNetBrowser.WinForms;
...
BrowserView view = new BrowserView();
view.InitializeFrom(browser);

Creating WPF BrowserView

1.x

using DotNetBrowser.WPF;
...
BrowserView view = new WPFBrowserView(browser);

2.0

using DotNetBrowser.Wpf;
...
BrowserView view = new BrowserView();
view.InitializeFrom(browser);

Frame

Working with frame

1.x

A web page may consist of the main frame including multiple sub-frames. To perform an operation with a frame, pass the frame’s identifier to the corresponding method:

// Get HTML of the main frame.
browser.GetHTML();

// Get HTML of the all frames.
foreach (long frameId in browser.GetFramesIds()) {
    string html = browser.GetHTML(frameId);
}

2.0

The IFrame is a part of the public API now. You can work with the frame through the IFrame instance:

// Get HTML of the main frame if it exists.
string html = browser.MainFrame?.Html;

// Get HTML of all the frames.
foreach (IFrame frame in browser.AllFrames) 
{
    string html = frame.Html;
}

Loading URL

1.x

browser.LoadURL("https://www.google.com");

2.0

browser.Navigation.LoadUrl("https://www.google.com");

Loading HTML

1.x

browser.LoadHTML("<html><body></body></html>");

2.0

browser.MainFrame?.LoadHtml("<html><body></body></html>"));

Start loading

1.x

browser.StartLoadingFrameEvent += (s, e) =>
{
    string url = e.ValidatedURL;
};

2.0

browser.Navigation.NavigationStarted += (s, e) => 
{
    string url = e.Url;
};

Finish loading

1.x

browser.FinishLoadingFrameEvent+= (s, e) =>
{
    string html = browser.GetHTML(e.FrameId);
};

2.0

browser.Navigation.FrameLoadFinished += (s, e) =>
{
    string html = e.Frame.Html;
};

Fail loading

1.x

browser.FailLoadingFrameEvent  += (s, e) => 
{
    NetError errorCode = e.ErrorCode;
};

2.0

browser.Navigation.FrameLoadFailed += (s, e) => 
{
    NetError error = e.ErrorCode;
};

or

browser.Navigation.NavigationFinished += (s, e) => 
{
    NetError error = e.ErrorCode;
};

Network

Configuring User-Agent

1.x

BrowserPreferences.SetUserAgent("My User-Agent");

2.0

IEngine engine = EngineFactory.Create(new EngineOptions.Builder
{
    UserAgent = "My User-Agent"
}
.Build());

Configuring Accept-Language

1.x

browserContext.AcceptLanguage = "fr, en-gb;q=0.8, en;q=0.7";

2.0

engine.Network.AcceptLanguage = "fr, en-gb;q=0.8, en;q=0.7";

Configuring proxy

1.x

ProxyConfig proxyConfig = browserContext.ProxyConfig;
browserContext.ProxyConfig = new CustomProxyConfig("http-proxy-server:80");

2.0

engine.Proxy.Settings = new CustomProxySettings("http-proxy-server:80");

Redirecting URL request

1.x

class MyNetworkDelegate : DefaultNetworkDelegate
{
    public override void OnBeforeURLRequest(BeforeURLRequestParams parameters)
    {
        parameters.SetURL("https://www.google.com");
    }
}
...
browserContext.NetworkService.NetworkDelegate = new MyNetworkDelegate();

2.0

engine.Network.SendUrlRequestHandler =
    new Handler<SendUrlRequestParameters, SendUrlRequestResponse>((e) =>
    {
        return SendUrlRequestResponse.Override("https://www.google.com");
    });

Overriding HTTP headers

1.x

class MyNetworkDelegate : DefaultNetworkDelegate
{
    public override void OnBeforeSendHeaders(BeforeSendHeadersParams parameters)
    {
        HttpHeadersEx headers = params.HeadersEx;
        headers.SetHeader("User-Agent", "MyUserAgent");
        headers.SetHeader("Content-Type", "text/html");
    }
}
...
browserContext.NetworkService.NetworkDelegate = new MyNetworkDelegate();

2.0

engine.Network.SendHeadersHandler =
    new Handler<SendHeadersParameters, SendHeadersResponse>((e) =>
    {
        return SendHeadersResponse.OverrideHeaders(new[] {
            new HttpHeader("User-Agent", "MyUserAgent"),
            new HttpHeader("Content-Type", "text/html")
        });
    });

Intercepting URL requests

1.x

class MyProtocolHandler : IProtocolHandler
{
    public IUrlResponse Handle(IUrlRequest request)
    {
        string html = "<html><body><p>Hello there!</p></body></html>";
        IUrlResponse response = new UrlResponse(Encoding.UTF8.GetBytes(html));
        response.Headers.SetHeader("Content-Type", "text/html");
        return response;
    }
}
...
browser.Context.ProtocolService.Register("https", new MyProtocolHandler());

2.0

network.InterceptRequestHandler =
    new Handler<InterceptRequestParameters, InterceptRequestResponse>(p =>
    {
        if (!p.UrlRequest.Url.StartsWith("https"))
        {
            return InterceptRequestResponse.Proceed();
        }

        UrlRequestJobOptions options = new UrlRequestJobOptions
        {
            Headers = new List<HttpHeader>
            {
                new HttpHeader("Content-Type", "text/plain"),
                new HttpHeader("Content-Type", "charset=utf-8")
            }
        };
        UrlRequestJob job = network.CreateUrlRequestJob(p.UrlRequest, options);

        Task.Run(() =>
        {
            // The request processing is performed in a background thread
            // in order to avoid freezing the web page.
            string html = "<html><body><p>Hello there!</p></body></html>";
            job.Write(Encoding.UTF8.GetBytes(html));
            job.Complete();
        });
        
        return InterceptRequestResponse.Intercept(job);
    });

Authentication

Proxy, Basic, Digest, NTLM

1.x

class MyNetworkDelegate : DefaultNetworkDelegate
{
    public override bool OnAuthRequired(AuthRequiredParams parameters)
    {
        if(parameters.IsProxy)
        {
            params.Username = "user";
            params.Password = "password";
            return false;
        }
        return true; // Cancel authentication request
    }
}
...
browserContext.NetworkService.NetworkDelegate = new MyNetworkDelegate();

2.0

engine.Network.AuthenticateHandler =
    new Handler<AuthenticateParameters, AuthenticateResponse>((p) =>
    {
        if (p.IsProxy)
        {
            return AuthenticateResponse.Continue("user", "password");
        }
        else
        {
            return AuthenticateResponse.Cancel();
        }
    });

HTTPS client certificate

1.x

class MyDialogHandler : DialogHandler
{
    public CloseStatus OnSelectCertificate(CertificatesDialogParams parameters)
    {
        List<Certificate> certificates = params.Certificates;
        if (certificates.Count == 0) {
            return CloseStatus.CANCEL;
        } else {
            params.SelectedCertificate = certificates.LastOrDefault();
            return CloseStatus.OK;
        }
    }

    ...
}
...
browser.DialogHandler = new MyDialogHandler();

2.0

browser.SelectCertificateHandler
    = new Handler<SelectCertificateParameters, SelectCertificateResponse>(p =>
    {
        int count = p.Certificates.Count();
        return count == 0 ?
            SelectCertificateResponse.Cancel() :
            SelectCertificateResponse.Select(p.Certificates.Count() - 1);
    });

Plugins

Filtering plugins

1.x

class MyPluginFilter : PluginFilter
{
    public bool IsPluginAllowed(PluginInfo pluginInfo)
    {
        return true;
    }
}
...
browser.PluginManager.PluginFilter = new MyPluginFilter();

2.0

engine.Plugins.AllowPluginHandler = 
    new Handler<AllowPluginParameters, AllowPluginResponse>(p =>
    {
        return AllowPluginResponse.Allow();
    });

DOM

Accessing a document

1.x

DOMDocument document = browser.GetDocument();

2.0

IDocument document = browser.MainFrame?.Document;

DOM events

Working with events

1.x

element.AddEventListener(DOMEventType.OnClick, (s, e) =>
{
    DOMEventTarget eventTarget = e.Target;
    if (eventTarget != null) {
        ...
    }
}, false);

2.0

element.Events.Click += (s, e) =>
{
    IEventTarget eventTarget = e.Event.Target;
    if (eventTarget != null)
    {
        //...
    }
};

JavaScript

Calling JavaScript from .NET

1.x

string string = browser.ExecuteJavaScriptAndReturnValue("'Hello'")
        .AsString().Value;
double number = browser.ExecuteJavaScriptAndReturnValue("123")
        .AsNumber().Value;
bool boolean = browser.ExecuteJavaScriptAndReturnValue("true")
        .AsBoolean().Value;
JSObject window = browser.ExecuteJavaScriptAndReturnValue("window")
        .AsObject();

2.0

The JSValue class has been removed in DotNetBrowser 2.0. The type conversion is done automatically now. You can execute JavaScript both synchronously blocking the current thread execution or asynchronously:

IFrame mainFrame = browser.MainFrame;
if(mainFrame != null)
{
    //Execution with await
    string name = await mainFrame.ExecuteJavaScript<string>("'Hello'");
    double number = await mainFrame.ExecuteJavaScript<double>("123");
    bool boolean = await mainFrame.ExecuteJavaScript<bool>("true");
    IJsObject window = await mainFrame.ExecuteJavaScript<IJsObject>("window");

    //Synchronous execution that blocks the current thread
    name = mainFrame.ExecuteJavaScript<string>("'Hello'").Result;
    number = mainFrame.ExecuteJavaScript<double>("123").Result;
    boolean = mainFrame.ExecuteJavaScript<bool>("true").Result;
    window = mainFrame.ExecuteJavaScript<IJsObject>("window").Result;

    //Asynchronous execution with continuation
    mainFrame.ExecuteJavaScript<IDocument>("document").ContinueWith(t =>{
        string baseUri = t.Result.BaseUri;
    });
}

Calling .NET from JavaScript

1.x

In .NET code:

public class MyObject {
    public void foo(string text) {}
}
...
JSValue window = browser.ExecuteJavaScriptAndReturnValue("window");
if (window.IsObject()) {
    window.AsObject().SetProperty("myObject", new MyObject());
}

In JavaScript code:

window.myObject.foo("Hello");

2.0

In .NET code:

public class MyObject {
    public void foo(string text) {}
}
...
IJsObject window = frame.ExecuteJavaScript<IJsObject>("window").Result;
window.Properties["myObject"] = new MyObject();

In JavaScript code:

window.myObject.foo("Hello");

Injecting JavaScript

1.x

browser.ScriptContextCreated += (sender, args) =>
{
    JSValue window = browser.ExecuteJavaScriptAndReturnValue(args.Context.FrameId, @"window");
    window.AsObject().SetProperty("myObject", new MyObject());
};

2.0

browser.InjectJsHandler = new Handler<FrameParameters>((args) => 
{
    IJsObject window = args.Frame.ExecuteJavaScript<IJsObject>("window").Result;
    window.Properties["myObject"] = new MyObject();
});

Console events

1.x

browser.ConsoleMessageEvent += (s, args) =>
{
    string message = args.Message;
};

2.0

browser.ConsoleMessageReceived += (s, args) =>
{
    string message = args.Message;
};

Pop-ups

Suppressing pop-ups

1.x

class MyPopupHandler : PopupHandler
{
    public PopupContainer HandlePopup(PopupParams popupParams)
    {
        return null;
    }
}
browser.PopupHandler = new MyPopupHandler();

2.0

browser.CreatePopupHandler = new Handler<CreatePopupParameters, CreatePopupResponse>((p) =>
{
    return CreatePopupResponse.Suppress();
});

Opening pop-ups

1.x

class MyPopupContainer : PopupContainer
{
    public void InsertBrowser(Browser browser, System.Drawing.Rectangle initialBounds)
    {
        ...
    }
}
class MyPopupHandler : PopupHandler
{
    public PopupContainer HandlePopup(PopupParams popupParams)
    {
        return new MyPopupContainer();
    }
}
browser.PopupHandler = new MyPopupHandler();

2.0


browser.CreatePopupHandler = new Handler<CreatePopupParameters, CreatePopupResponse>((p) =>
{
    return CreatePopupResponse.Create();
});
browser.OpenPopupHandler = new Handler<OpenPopupParameters>((p) =>
{
    IBrowser popup = p.PopupBrowser;
    //...
});

Dialogs

JavaScript dialogs

1.x

class MyDialogHandler : DialogHandler
{
    public void OnAlert(DialogParams parameters) 
    {
    }

    public CloseStatus OnConfirmation(DialogParams parameters) 
    {
        return CloseStatus.CANCEL;
    }

    public CloseStatus OnPrompt(PromptDialogParams parameters) 
    {
        params.PromptText = "Text";
        return CloseStatus.OK;
    }
    ...
}
browser.DialogHandler = new MyDialogHandler();

2.0

browser.JsDialogs.AlertHandler = new Handler<AlertParameters>(p =>
    {
        //...
    });
browser.JsDialogs.ConfirmHandler =
    new Handler<ConfirmParameters, ConfirmResponse>(p => 
    {
        return ConfirmResponse.Cancel();
    });
browser.JsDialogs.PromptHandler =
    new Handler<PromptParameters, PromptResponse>(p =>
    {
        return PromptResponse.SubmitText("responseText");
    });

File dialogs

1.x

class FileChooserHandler : DialogHandler
{
    public CloseStatus OnFileChooser(FileChooserParams parameters) {
        FileChooserMode mode = parameters.Mode;
        if (mode == FileChooserMode.Open) {
            parameters.SelectedFiles = "file1.txt";
        }
        if (mode == FileChooserMode.OpenMultiple) {
            string[] selectedFiles = {"file1.txt", "file2.txt"};
            parameters.SelectedFiles = string.Join("|", selectedFiles);
        }
        return CloseStatus.OK;
    }
    ...
}
...
browser.DialogHandler = new FileChooserHandler();

2.0

browser.Dialogs.OpenFileHandler =
    new Handler<OpenFileParameters, OpenFileResponse>(p =>
    {
        return OpenFileResponse.SelectFile(Path.GetFullPath(p.DefaultFileName));
    });

browser.Dialogs.OpenMultipleFilesHandler =
    new Handler<OpenMultipleFilesParameters, OpenMultipleFilesResponse>(p =>
    {
        return OpenMultipleFilesResponse.SelectFiles(Path.GetFullPath("file1.txt"),
            Path.GetFullPath("file2.txt"));
    });

Color dialogs

1.x

class ColorDialogHandler : DialogHandler
{
    private Color color;

    public ColorDialogHandler()
    {
        color = Color.White;
    }
    
    public CloseStatus OnColorChooser(ColorChooserParams parameters)
    {        
        parameters.Color = color;
        return CloseStatus.OK;
    }
    ...
}
...
browser.DialogHandler = new ColorDialogHandler();

2.0

browser.Dialogs.SelectColorHandler =
    new Handler<SelectColorParameters, SelectColorResponse>(p =>
    {
        return SelectColorResponse.SelectColor(p.DefaultColor);
    });

SSL certificate dialogs

1.x

class SelectCertificateDialogHandler : DialogHandler
{
    private const string ClientCertFile = "<cert-file>.pfx";
    private const string ClientCertPassword = "<cert-password>";

    private Certificate certificate;

    public SelectCertificateDialogHandler()
    {
        certificate = new X509Certificate2(Path.GetFullPath(ClientCertFile),
                                           ClientCertPassword,
                                           X509KeyStorageFlags.Exportable);
    }
    
    public CloseStatus OnSelectCertificate(CertificatesDialogParams parameters)
    {        
        dialogParams.SelectedCertificate = certificate;
        selectCertificateEvent.Set();
        return CloseStatus.OK;
    }
    ...
}
...
browser.DialogHandler = new SelectCertificateDialogHandler();

2.0

string ClientCertFile = "<cert-file>.pfx";
string ClientCertPassword = "<cert-password>";
//...
X509Certificate2 certificate = new X509Certificate2(Path.GetFullPath(ClientCertFile),
    ClientCertPassword,
    X509KeyStorageFlags.Exportable);
Certificate cert = new Certificate(certificate);
browser.SelectCertificateHandler
    = new Handler<SelectCertificateParameters, SelectCertificateResponse>(p =>
    {
        return SelectCertificateResponse.Select(cert);
    });

Printing

Configuring printing

1.x

class MyPrintHandler : PrintHandler
{
    public PrintStatus OnPrint(PrintJob printJob)
    {
        PrintSettings printSettings = printJob.PrintSettings;
        printSettings.PrinterName = "Microsoft XPS Document Writer";
        printSettings.Landscape = true;
        printSettings.PrintBackgrounds = true;
        return PrintStatus.CONTINUE;
    }
}
...
browser.PrintHandler = new MyPrintHandler();

2.0

Functionality that allows configuring printing has been removed in DotNetBrowser version 2.0. Now the standard Print Preview dialog where you can provide the required settings is displayed:

browser.PrintHandler =
    new Handler<PrintParameters, PrintResponse>(p =>
    {
        return PrintResponse.ShowPrintPreview();
    });

Suppressing printing

1.x

class MyPrintHandler : PrintHandler
{
    public PrintStatus OnPrint(PrintJob printJob)
    {
        return PrintStatus.CANCEL;
    }
}
...
browser.PrintHandler = new MyPrintHandler();

2.0

browser.PrintHandler =
    new Handler<PrintParameters, PrintResponse>(p =>
    {
        return PrintResponse.Cancel();
    });

Cache

Clearing HTTP cache

1.x

browser.CacheStorage.ClearCache(() => 
{
    // HTTP Disk Cache has been cleared.
});

2.0

await engine.HttpCache.ClearDiskCache();
// HTTP Disk Cache has been cleared.

or

engine.HttpCache.ClearDiskCache().Wait();
// HTTP Disk Cache has been cleared.

Cookies

Accessing cookies

1.x

List<Cookie> cookies = browser.CookieStorage.GetAllCookies();

2.0

IEnumerable<Cookie> cookies = await engine.CookieStore.GetAllCookies();

or

IEnumerable<Cookie> cookies = engine.CookieStore.GetAllCookies().Result;

Render process termination

Each browser instance is running on a separate native process where the web page is rendered.

Sometimes this process can exit unexpectedly because of the plugin crash. The render process termination event can be used to receive notifications about unexpected render process termination.

1.x

browser.RenderGoneEvent += (s, e) =>
{
    TerminationStatus status = e.TerminationStatus;
};

2.0

browser.RenderProcessTerminated += (s, e) =>
{
    TerminationStatus status = e.TerminationStatus;
};

Chromium

Switches

The library does not support all possible Chromium switches. It allows configuring Chromium with the switches, but we do not guarantee that the passed switches will work correctly or work at all. We recommend to check this functionality before using.

1.x

BrowserPreferences.SetChromiumSwitches(
        "--<switch_name>",
        "--<switch_name>=<switch_value>"
);

2.0

IEngine engine = EngineFactory.Create(new EngineOptions.Builder
{
    ChromiumSwitches = { "--<switch-name>", "--<switch-name>=<switch-value>" }
}
.Build());

API keys

1.x

BrowserPreferences.SetChromiumVariable(
        "GOOGLE_API_KEY", "My API Key");
BrowserPreferences.SetChromiumVariable(
        "GOOGLE_DEFAULT_CLIENT_ID", "My Client ID");
BrowserPreferences.SetChromiumVariable(
        "GOOGLE_DEFAULT_CLIENT_SECRET", "My Client Secret");

2.0

IEngine engine = EngineFactory.Create(new EngineOptions.Builder
{
    GoogleApiKey = "<api-key>",
    GoogleDefaultClientId = "<client-id>",
    GoogleDefaultClientSecret = "<client-secret>"
}
.Build());

Logging

In the new version, we improved the logging API. Read more about how to configure logging in the Troubleshooting guide.

FAQ

How long will DotNetBrowser version 1.x be supported?

DotNetBrowser version 1.x will be supported for some period, however, this support includes only critical bug and security fixes we can apply. All new features, Chromium upgrades, support of the new operating systems and .NET implementations, different enhancements, will be applied on top of the latest (mainstream) version.

Can I upgrade to DotNetBrowser 2 for free?

If you already own a commercial license for DotNetBrowser 1 with an active Subscription, then you can receive a commercial license key for DotNetBrowser 2 for free. Contact our Sales Team if you have any questions regarding this.

I have a case not covered by this guide. What should I do?

We recommend checking our Guides section. DotNetBrowser version 1.0 and version 2.0 have almost identical functionality. So, if this migration guide does not describe an alternative to the DotNetBrowser version 1.0 functionality you use, you can find description of the similar functionality in our guides where the documents are grouped by feature areas.

If you do not see description of the required functionality in the guides, submit a ticket.

Go Top