Using Microsoft Edge in a native Windows desktop app – part 2

This article has been updated for the new version of WebView2 that requires Microsoft Edge 82.0.488.0 or newer.

In the second part of this series, we will see how to use the WebView2 control in a C++ Windows desktop application. We will use a single document interface MFC application that features a toolbar where you can specify an address to navigate to and buttons to navigate back and forward as well as reloading the current page or stopping navigation.

Articles in this series:

The API overview

The WebView2 SDK contains the following APIs:

The environment is a container that runs a specific version of the Edge browser, with optional custom browser arguments, and a user data folder.

In order to create a web view control you must do the following:

  • Call CreateCoreWebView2EnvironmentWithOptions() to create the web view environment.
  • When the environment is available, use the ICoreWebView2Environment interface to create the web view and its controller by calling CreateCoreWebView2Controller.
  • When the web view controller is available, use the ICoreWebView2Controller interface to retrieve a pointer to the webview, ICoreWebView2* so you can add and remove event handlers. Also, you can retrieve a pointer to the ICoreWebView2Settings interface to modify web view features.

The demo app

To see how the WebView2 control works, we will use a very simple MFC application with SDI support. The WebView2 control will be created and display within the view. The application contains a toolbar with buttons to navigate back and forward, to stop or reload a page, and a control to provide the URL. Navigation to the URL starts when you press the ENTER key. With this minimum functionality, the application mimics a browser.

The most important classes here are the following:

  • CMainFrame that represents the main window of the application, that contains the menu, toolbar, and status bar. This is where the toolbar events are handled and processed.
  • CMfcEdgeDemoView is the view in the SDI architecture. It is a window that contains and displays on top of itself the WebView2 control, implemented by the class CWebBrowser that we will see below. The class overrides OnInitialUpdate() to create the web view and DestroyWindow() to destroy it. It also handles the WM_SIZE window message to resize the web view control.

You can check the attached demo projects to look at the source code details.

Creating the WebView2 control

The WebView2 control will be managed by the CWebBrowser class. This class is derived from CWnd and has the following interface:

The Create() method is an overload from CWnd. However, you can only use this if you want to initiate the creation of the web view and then forget about it. If you need to do something after the web view was created then you need to properly utilize the asynchronous API of the WebView2. The method CreateAsync() is initiating the creation of the web view and registers a callback that will be invoked when the web view creation is completed.

There are three steps here:

  1. Create a parent (host) window. The purpose of this window is have a message queue that we will use to process callbacks. When an event occurs, we post a message to the queue. The window procedure will process the message and invoke the appropriate callback. In this example, we have defined the CallbackType enumeration that provides two types of callbacks: one for completing the navigation, and one for completing the creation of the view.
  2. Register a callback function to be invoked when the web view has been created.
  3. Initialize the web view.

To initialize the web view we must call the CreateCoreWebView2EnvironmentWithOptions() method with the following arguments:

  • The path to the installation folder of Edge. If this is null, the component should automatically locate the installation of Edge and use that. In practice, providing null does not work well, and the component is not able to detect the browser.
  • The patch to the user data folder. If this is null, a subfolder in the current folder will be created. Beware that if your application is installed in Program Files, it will not be able to create it. Invoking this method will result in an access denied error (0x80070005 which is a HRESULT value for ERROR_ACCESS_DENIED). Therefore, make sure you provide a user folder to a writable location.
  • Optional environment options (as ICoreWebView2EnvironmentOptions*) to change the behavior of the web view.
  • A handler for the result of the asynchronous operation, that will be invoked if the environment was successfully created.

If this function fails, it returns an error code. For instance, 0x80070002 (HRESULT for ERROR_FILE_NOT_FOUND) means that the browser was not found. This function can be implemented as follows:

When the creation of the environment completes successfully, the provided callback is invoked. The first argument of the handler is a HRESULT, and the second is a pointer to the ICoreWebView2Environment interface that defines the environment. This pointer can be used to create the web view by calling CreateCoreWebView2Controller(). This method has two parameters: the handle of the parent window and a callback that will be invoked when the web view creation completes. The implementation of this function is as follows:

The callback OnCreateWebViewControllerCompleted is invoked with two arguments: a HRESULT value that indicates the success of the operation and a pointer to the ICoreWebView2Controller interfaces that defines the controller for the web view. This pointer can be used to get a pointer to the ICoreWebView2 interface. This, in turn, can be used to add and remove event handlers and invoke various methods such as navigation. The implementation is as follows:

We will look at handling events in the next installment. What you can see here is that when the creation completes we invoke the callback the user passed when initiating the asynchronous creation of the web view. However, the invocation is not done directly. Instead, a message is posted to the web view’s parent window message queue. When this message is processed, the callback is actually invoked.

Having a pointer to the ICoreWebView2 and ICoreWebView2Controller interfaces, we can also implement the other methods from the public interface of the CWebBrowser class.

We will discuss the details about events and navigation in the next post.

What is left to show here is how the CWebBrowser is used from the SDI’s view, which you can see below:

Notice that when calling CreateAsync(), we pass a lambda that, when invoked, triggers navigation to the https://bing.com web page.

Finding the Edge location

In my experience with the CreateCoreWebView2EnvironmentWithOptions(), passing null for the browser location did not work well and it was unable to find the browser installation, regardless of the version I was using (whether it was the Beta or the RTM version).

The Edge browser is installed under C:\Program Files (x86)\Microsoft\Edge\Application. This is the case even though Edge is a 64-bit application. The reason its installation path is under Program Files (x86) and not under Program Files (as it is expected for 64-bit application) is probably historical. Chrome does the same because it was easier for scenarios when users migrated from 32-bit to the 64-bit version of the browser.

However, the folder you are expected to provide to CreateCoreWebView2EnvironmentWithOptions() is not C:\Program Files (x86)\Microsoft\Edge\Application but a subfolder that has the same name as the version of the browser. In the picture above, the version (and folder name) is 79.0.309.71.

The current version of this implementation works only with Edge Beta, which has a different installation path, c:\Program Files (x86)\Microsoft\Edge Beta\.

To programmatically detect the path of the Edge installation you can do the following:

  • Search in Windows Registry. Installation location and version information is available under SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge.
  • Search on disk in the default installation location for a folder name of the form 79.0.309.71.

In the attached source code, you will find the following implementation for this:

A few more words…

In the code above, there were references to a CHECK_FAILURE macro, as well as the function ShowFailure(). This function displays a message to the user containing information about an error. There is also a function CheckFailure(), called from the CHECK_FAILURE macro that logs an error message and then terminates the process. These functions have been adapted from the sample code provided with the WebView2 SDK.

Try the app

You can download, build, and try the sample app for this series from here: MfcEdgeDemo.zip (119 downloads) .

Stay tuned for the next part of the series.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.