Using WebView (EdgeHTML) in Delphi / C++ Builder

Solution 1:

This answer is outdated but may be interesting to learn about the technical background. RAD Studio 10.4 Sydney now supports using the Edge browser out of the box. See my other answer.


The WebView control is offered via WinRT and does not depend on .net. You can use it from normal Win32 applications.

WinRT (Windows Runtime), now in Windows 10 rebranded as UWP (Universal Windows Platform), is something like the successor of COM.

Like COM it is heavily based on interfaces and the available interfaces are defined in type libraries. For WinRT, the type libraries are stored in *.WinMD files in the Windows system directory. The type library which contains the functionality we need to embed the Edge browser is Windows.Web.winmd.

Delphi does support using WinRT components and it comes with translations of some of the type libraries and some additional helper functions and classes to work with WinRT.

However, there is currently no tool to automatically translate WinMD files or IDL files derived from WinMD files to Delphi code. If you want to use WinRT functionality that does not ship with Delphi, you have to manually translate the type definitions to Delphi code.

WinRT heavily uses generic interfaces (interfaces with type parameters) that are not compatible with the way generic interfaces work in Delphi. This requires some manual adjustments when translating the type definitions.

If you install the Windows Platform SDK, you find IDL and C++ translations of the WinRT type libraries in a directory like Drive:\Windows Kits\10\Include\10.0.17134.0\winrt.

I have used these files as templates to create a very basic Proof of Concept Delphi project (for Delphi 10.2) which uses an embedded Edge browser. You can find the code below. In order to test this, just create a new VCL project, paste the code and connect the FormCreate, FormDestroy and FormResize events with the form.

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  System.Types,
  Winapi.Winrt,
  System.Win.WinRT,
  WinAPI.Foundation,
  WinAPI.Foundation.Types;

const
  SWebViewControlProcess = 'Windows.Web.UI.Interop.WebViewControlProcess';

type
  // Interface with functionality to interact with WebBrowser Control
  // https://docs.microsoft.com/en-us/uwp/api/windows.web.ui.iwebviewcontrol
  IWebViewControl = interface(IInspectable)
  ['{3F921316-BC70-4BDA-9136-C94370899FAB}']
    procedure Placeholder_SourceGet; safecall;
    procedure Placeholder_SourcePut; safecall;
    procedure Placeholder_DocumentTitle; safecall;
    procedure Placeholder_CanGoBack; safecall;
    procedure Placeholder_CanGoForward; safecall;
    procedure Placeholder_DefaultBackgroundColorPut; safecall;
    procedure Placeholder_DefaultBackgroundColorGet; safecall;
    procedure Placeholder_ContainsFullScreenElement; safecall;
    procedure Placeholder_Settings; safecall;
    procedure Placeholder_DeferredPermissionRequests; safecall;
    procedure Placeholder_GoForward; safecall;
    procedure Placeholder_GoBack; safecall;
    procedure Placeholder_Refresh; safecall;
    procedure Placeholder_Stop; safecall;
    procedure Navigate(source: IUriRuntimeClass); stdcall;
    procedure NavigateToString(text: HString); stdcall;
    // TODO: Declare further properties and functions of IWebViewControl
  end;

  IWebViewControlProcess = interface;

  // Declare  IWebViewControlSite
  IWebViewControlSite = interface(IInspectable)
  ['{133F47C6-12DC-4898-BD47-04967DE648BA}']
    function get_Process: IWebViewControlProcess; safecall;
    procedure put_Scale(value: Double); safecall;
    function get_Scale: Double; safecall;
    procedure put_Bounds(value: TRectF); safecall;
    function get_Bounds: TRectF; safecall;
    procedure put_IsVisible(value: Boolean); safecall;
    function get_IsVisible: Boolean; safecall;

    // TODO: Declare further properties and functions of IWebViewControlSite

    property Process: IWebViewControlProcess read get_Process;
    property Scale: Double read get_Scale write put_Scale;
    property Bounds: TRectF read get_Bounds write put_Bounds;
    property IsVisible: Boolean read get_IsVisible write put_IsVisible;
  end;

  // types for reacting to when the WebView has finished initialization
  IAsyncOperation_1__IWebViewControl = interface;

  IAsyncOperationCompletedHandler_1__IWebViewControl = interface(IUnknown)
  ['{d61963d6-806d-50a8-a81c-75d9356ad5d7}']
    procedure Invoke(asyncInfo: IAsyncOperation_1__IWebViewControl; asyncStatus: AsyncStatus); safecall;
  end;

  IAsyncOperation_1__IWebViewControl = interface(IInspectable)
  ['{ac3d28ac-8362-51c6-b2cc-16f3672758f1}']
    procedure put_Completed(handler: IAsyncOperationCompletedHandler_1__IWebViewControl); safecall;
    function get_Completed: IAsyncOperationCompletedHandler_1__IWebViewControl; safecall;
    function GetResults: IWebViewControl; safecall;
    property Completed: IAsyncOperationCompletedHandler_1__IWebViewControl read get_Completed write put_Completed;
  end;

  TWebViewControlCompleted = procedure(asyncInfo: IAsyncOperation_1__IWebViewControl; aasyncStatus: AsyncStatus) of object;

  TWebViewControlCompletedHandler = class(TInspectableObject,
      IAsyncOperationCompletedHandler_1__IWebViewControl
      )
  private
    FEvent: TWebViewControlCompleted;
  public
    procedure Invoke(asyncInfo: IAsyncOperation_1__IWebViewControl; aasyncStatus: AsyncStatus); safecall;
    constructor Create(AEvent: TWebViewControlCompleted);
  end;

   // The interface for interacting with the process hosting the web view control
   // https://docs.microsoft.com/en-us/uwp/api/windows.web.ui.interop.webviewcontrolprocess
  [WinRTClassNameAttribute(SWebViewControlProcess)]
  IWebViewControlProcess = interface(IInspectable)
  ['{02C723EC-98D6-424A-B63E-C6136C36A0F2}']
    function get_ProcessId: Cardinal; safecall;
    function get_EnterpriseId: HSTRING; safecall;
    function get_IsPrivateNetworkClientServerCapabilityEnabled: Boolean; safecall;
    function CreateWebViewControlAsync(hostWindowHandle: Int64; bounds: TRectF): IAsyncOperation_1__IWebViewControl; safecall;
    procedure Placeholder_GetWebViewControls; safecall;
    procedure Terminate; safecall;

    property ProcessId: Cardinal read get_ProcessId;
    property EnterpriseId: HSTRING read get_EnterpriseId;
    property IsPrivateNetworkClientServerCapabilityEnabled: Boolean read get_IsPrivateNetworkClientServerCapabilityEnabled;

    // TODO:
    //[eventadd] HRESULT ProcessExited([in] Windows.Foundation.TypedEventHandler<Windows.Web.UI.Interop.WebViewControlProcess*, IInspectable*>* handler, [out] [retval] EventRegistrationToken* token);
    //[eventremove] HRESULT ProcessExited([in] EventRegistrationToken token);
  end;

  // The CoClass to create an IWebViewControlProcess instance
  TWebViewControlProcess = class(TWinRTGenericImportI<IWebViewControlProcess>)
  end;

type

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
    FProcess: IWebViewControlProcess;
    FBrowser: IWebViewControl;
    FBrowserSite: IWebViewControlSite;
    procedure WebViewCompleted(asyncInfo: IAsyncOperation_1__IWebViewControl; aasyncStatus: AsyncStatus);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  Rect: TRectF;
  AsyncOperation: IAsyncOperation_1__IWebViewControl;
    CompletedHandler: IAsyncOperationCompletedHandler_1__IWebViewControl;
begin
  CompletedHandler:=TWebViewControlCompletedHandler.Create(WebViewCompleted);

  // Size for browser
  Rect:= TRectF.Create(0, 0, ClientWidth, ClientHeight);

  // Create hosting process
  FProcess:= TWebViewControlProcess.Create();

  // Create WebView Control
  AsyncOperation:= FProcess.CreateWebViewControlAsync(self.Handle, Rect);

  // We will get notified when the control creation is finished
  AsyncOperation.Completed:= CompletedHandler;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // If there is a hosting process, then terminate it
  if Assigned(FProcess) then
  begin
    FProcess.Terminate;
  end;
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  if Assigned(FBrowserSite) then
  begin
    FBrowserSite.Bounds := TRectF.Create(0,0,ClientWidth, ClientHeight);
  end;
end;

procedure TForm1.WebViewCompleted(
  asyncInfo: IAsyncOperation_1__IWebViewControl;
  aasyncStatus: AsyncStatus);
var
  WinS: TWindowsString;
  Uri: IUriRuntimeClass;
begin
  // Initializing the WebView control was successful

  // Remember reference to control
  FBrowser:= asyncInfo.GetResults();
  FBrowserSite := FBrowser as IWebViewControlSite;

  // Load web page into control
  WinS:= TWindowsString.Create('http://www.whatismybrowser.com');
  Uri:= TUri.CreateUri(WinS);
  FBrowser.Navigate(Uri);
end;

{ TWebViewControlCompletedHandler }

constructor TWebViewControlCompletedHandler.Create(
  AEvent: TWebViewControlCompleted);
begin
  FEvent := AEvent;
end;

procedure TWebViewControlCompletedHandler.Invoke(
  asyncInfo: IAsyncOperation_1__IWebViewControl;
  aasyncStatus: AsyncStatus);
begin
  FEvent(asyncInfo, aasyncStatus);
end;

end.

enter image description here

Solution 2:

RAD Studio 10.4 Sydney comes with enhanced support for the new Chromium-based Edge browser by Microsoft.

There is both a new Control TEdgeBrowser that can be used to use the Edge browser engine directly as well as the possibility to allow the classic TWebBrowser control to use the new Edge rendering engine automatically when it is available via the TWebBrowser.SelectedEngine property.

Detailed explanation in this blog entry by Embarcadero:

  • Using TEdgeBrowser Component and Changes to the TWebBrowser Component