Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Element not found. (0x80070490) when using host objects #335

Closed
fvanheeswijk opened this issue Jul 14, 2020 · 9 comments
Closed

Element not found. (0x80070490) when using host objects #335

fvanheeswijk opened this issue Jul 14, 2020 · 9 comments
Labels
bug Something isn't working

Comments

@fvanheeswijk
Copy link

fvanheeswijk commented Jul 14, 2020

So this issue will be more difficult to debug as we so far have not been able to reproduce it.

When we call $WF.handler.systemIdentification() we get the following error:

VM6:1 Uncaught Error: Element not found. (0x80070490)
at RemoteMessenger.postSyncRequestMessage (:1:10471)
at Function.applyHostFunction (:1:22650)
at Object.apply (:1:15398)
at :1:15

However window.chrome.webview.hostObjects.sync.handler.systemIdentification() works correctly with no issues.

Furthermore, $WF.handler is defined as follows:

window.$Events = {
	'on': function (evs, sel, dat, han) {
		$(this).on (evs, sel, dat, han);
		return this;
	},

	'off': function (evs, sel, han) {
		$(this).off (evs, sel, han);
		return this;
	},

	'trigger': function (ev, par) {
		$(this).triggerHandler (ev, par);
		return this;
	}
};

window.$WF = $.extend ({}, $Events);

(function () {
	var external = null;

	try {
		// MS WebView2 has both window.external and this one; we want this one first
		external = window.chrome.webview.hostObjects.sync.handler;
	} catch (e) {
	}
	external = external || window.external;

	Object.defineProperties ($WF, {
		'handler': {
			get: function () {
				return external;
			},
			configurable: false
		}
	});
}) ();

systemIdentification is defined as follows:

public delegate string SystemIdentificationHandler();

[ComVisible(true)]
public class ExternalDocHostUICallback : DocHostUICallback
{
	public event SystemIdentificationHandler SystemIdentification;

	[ComVisible(true), DispId(1000)]
	public string systemIdentification ()
	{
		if (SystemIdentification == null)
			return "";

		return SystemIdentification();
	}
}

And later on we add an event to it, but that shouldn't be an issue.

We also get different error messages when we either call a different (non-existent) function or we pass an invalid number of parameters, so it should be the correct function/method we are calling.

We have tried to reproduce this issue but have not been successful in a test project, can you give us any hints on how to continue debugging this? We are particularly surprised by the fact that window.chrome.webview.hostObjects.sync.handler.systemIdentification() works but $WF.handler.systemIdentification() does not and this appears to be one of the only functions/methods that is not working, other ones that we are using do work.

AB#27803563

@pagoe-msft pagoe-msft added the bug Something isn't working label Jul 20, 2020
@pagoe-msft
Copy link

@fvanheeswijk,

Thanks for bringing this to our attention! I've filed a bug and we will investigate.

@champnic
Copy link
Member

@fvanheeswijk Has this error only hit once and typically the code works fine? Or does it fail in some environment, for example, and you are unable to reproduce the issue locally?

@fvanheeswijk
Copy link
Author

@champnic This issue reproduces 100% of the time in our actual application and also for multiple developers, the only thing we cannot get it to do is to reproduce within a test project.

@fvanheeswijk
Copy link
Author

I just found this topic on Stackoverflow: https://stackoverflow.com/a/62804535

Is this still the case? Our class is marked with ComVisible(true) but with nothing else, also I haven't found anything regarding those attributes in the documentation?

@champnic
Copy link
Member

champnic commented Aug 7, 2020

We have a task to improve our documentation here for .NET.

We looked into this issue a bit and we don't think it's related to your host object - rather the JS garbage collector at some point decides to clean up the host object which removes it on the host side as well, and so calling the host object function results in the element not found error. As noted, the workaround is to always use the full window.chrome.webview.hostObjects.sync.handler.systemIdentification()

We're going to keep digging and see if we can fix the GC issue with the host objects.

@cgeier
Copy link

cgeier commented Aug 8, 2020

Here's some documentation regarding COM that you may find useful:

@pagoe-msft
Copy link

Hi Everyone,

This was addressed in the 1.0.707-prerelease version.

@bruno-walter
Copy link

I'm still seeing this on 1.0.721-prerelease. Using the full window.chrome.webview.hostObjects.sync. to access my object resolves it so I'm pretty sure it's the same issue.

@cgeier
Copy link

cgeier commented Dec 18, 2020

@fvanheeswijk,@champnic I did some testing and I think that I may have found the issue when using the code posted above (which I'm re-posting below):

window.$Events = {
	'on': function (evs, sel, dat, han) {
		$(this).on (evs, sel, dat, han);
		return this;
	},

	'off': function (evs, sel, han) {
		$(this).off (evs, sel, han);
		return this;
	},

	'trigger': function (ev, par) {
		$(this).triggerHandler (ev, par);
		return this;
	}
};

window.$WF = $.extend ({}, $Events);

(function () {
	var external = null;

	try {
		// MS WebView2 has both window.external and this one; we want this one first
		external = window.chrome.webview.hostObjects.sync.handler;
	} catch (e) {
	}
	external = external || window.external;

	Object.defineProperties ($WF, {
		'handler': {
			get: function () {
				return external;
			},
			configurable: false
		}
	});
}) ();

The issue occurs at this statement: window.$WF = $.extend ({}, $Events); when adding the above code with AddScriptToExecuteOnDocumentCreatedAsync. The error is $ is not defined which can be seen if you change the code:

Original:

window.$WF = $.extend({}, $Events);

Change To:

try {
	window.$WF = $.extend({}, $Events);
}
catch (e) {
	window.chrome.webview.postMessage('Error: (msg 1b2): ' + e.message);
}

Result: $ is not defined

The most likely cause is trying to run the code above using AddScriptToExecuteOnDocumentCreatedAsync which must be called before the WebView2 Source property is set. Attempting to use AddScriptToExecuteOnDocumentCreatedAsync after the Source property has been set, has no effect. Attempting to use AddScriptToExecuteOnDocumentCreatedAsync in DOMContentLoaded (or later event handlers such as NavigationComplete) will have no effect--the event that causes the code specified in AddScriptToExecuteOnDocumentCreatedAsync to run has already occurred. Finally, $ won't be defined until DOMContentLoaded has occurred.

Here's a solution that seems to work: For the JavaScript don't use AddScriptToExecuteOnDocumentCreatedAsync, instead in the DOMContentLoaded event handler (or NavigationCompleted event handler), create a new instance of your class (ExternalDocHostUICallback), add the instance of ExternalDocHostUICallback to JavaScript using AddHostObjectToScript. Then run your JavaScript using ExecuteScriptAsync. The full code is below.

The code below can also be found in the .zip file at the end of this post. I've included the entire code below, for anyone reading this who may be reluctant to open a project from an unknown source.

Note: For JavaScript1.js need to set BuildAction property to Embedded Resource.

  • On VS menu, click View
  • Select Solution Explorer
  • On VS menu, click View
  • Select Properties Window
  • In Solution Explorer, click JavaScript1.js
  • In Properties Window, click area next to Build Action, set value to Embedded Resource

Create a JavaScript file named: JavaScript1.js

JavaScript1.js

window.chrome.webview.postMessage('msg 1a');

window.$Events = {
	'on': function (evs, sel, dat, han) {
		$(this).on(evs, sel, dat, han);
		return this;
	},

	'off': function (evs, sel, han) {
		$(this).off(evs, sel, han);
		return this;
	},

	'trigger': function (ev, par) {
		$(this).triggerHandler(ev, par);
		return this;
	}
};

window.chrome.webview.postMessage('msg 1b');

try {

	window.$WF = $.extend({}, $Events);

	window.chrome.webview.postMessage('msg 1b1');
}
catch (e) {
	window.chrome.webview.postMessage('Error: (msg 1b2): ' + e.message);
}


window.chrome.webview.postMessage('msg 1c');
(function () {

	window.chrome.webview.postMessage('msg 1d');
	var external = null;

	try {
		// MS WebView2 has both window.external and this one; we want this one first
		external = window.chrome.webview.hostObjects.sync.handler;

		window.chrome.webview.postMessage('msg 1e');
	} catch (e) {
		window.chrome.webview.postMessage('Error: ' + e.message);
	}

	window.chrome.webview.postMessage('msg 1f');
	external = external || window.external;

	window.chrome.webview.postMessage('msg 1g');

	Object.defineProperties($WF, {
		'handler': {
			get: function () {
				window.chrome.webview.postMessage('msg 1h');
				return external;
			},
			configurable: false
		}
	});

	window.chrome.webview.postMessage('msg 1i');
})();
window.chrome.webview.postMessage('msg 1j');

Create a class named: ExternalDocHostUICallback.cs

ExternalDocHostUICallback.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace WebView2HostObjectsTest
{
    [ComVisible(false)]
    public delegate string SystemIdentificationHandler();

    [ComVisible(false)]
    public delegate void SystemIdentificationValueUpdatedHandler(object sender, SystemIdentificationValueUpdatedEventArgs e);

    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IComEventsExternalDocHostUICallback
    {
        [DispId(10000001)]
        string SystemIdentification();

        [DispId(10000002)]
        void SystemIdentificationValueUpdated(object sender, SystemIdentificationValueUpdatedEventArgs e);

    }

    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IExternalDocHostUICallback
    {
        [DispId(00000001)]
        string systemIdentification();

        [DispId(00000002)]
        void SendUpdates(string data);
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IComEventsExternalDocHostUICallback))]
    public class ExternalDocHostUICallback : IExternalDocHostUICallback
    {
        public event SystemIdentificationHandler SystemIdentification;
        public event SystemIdentificationValueUpdatedHandler SystemIdentificationValueUpdated;

        public string systemIdentification()
        {
            if (SystemIdentification == null)
                return "";

            return SystemIdentification();
        }

        public void SendUpdates(string data)
        {
            //Important: it's necessary to check if there are any subscribers
            //before raising an event
            if (SystemIdentificationValueUpdated != null)
            {
                //create new instance
                SystemIdentificationValueUpdatedEventArgs valueArgs = new SystemIdentificationValueUpdatedEventArgs(data);

                //raise event
                SystemIdentificationValueUpdated(this, valueArgs);
            }
        }
    }

    [ComVisible(true)]
    public class SystemIdentificationValueUpdatedEventArgs : System.EventArgs
    {
        public string Data { get; set; } = string.Empty;
        public SystemIdentificationValueUpdatedEventArgs(string data)
        {
            this.Data = data;
        }
    }
}

Create a class named: HelperLoadResource.cs

HelperLoadResource.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Diagnostics;

namespace WebView2HostObjectsTest
{
    public static class HelperLoadResource
    {
        public static string ReadResource(string filename)
        {
            //use UTF8 encoding as the default encoding
            return ReadResource(filename, Encoding.UTF8);
        }

        public static string ReadResource(string filename, Encoding fileEncoding)
        {
            string fqResourceName = string.Empty;
            string result = string.Empty;

            //get executing assembly
            Assembly execAssembly = Assembly.GetExecutingAssembly();

            //get resource names
            string[] resourceNames = execAssembly.GetManifestResourceNames();

            if (resourceNames != null && resourceNames.Length > 0)
            {
                foreach (string rName in resourceNames)
                {
                    if (rName.EndsWith(filename))
                    {

                        //set value to 1st match
                        //if the same filename exists in different folders,
                        //the filename can be specified as <folder name>.<filename>
                        //or <namespace>.<folder name>.<filename>
                        fqResourceName = rName;

                        //exit loop
                        break;
                    }
                }

                //if not found, throw exception
                if (String.IsNullOrEmpty(fqResourceName))
                {
                    throw new Exception($"Resource '{filename}' not found.");
                }

                //get file text
                using (Stream s = execAssembly.GetManifestResourceStream(fqResourceName))
                {
                    using (StreamReader reader = new StreamReader(s, fileEncoding))
                    {
                        //get text
                        result = reader.ReadToEnd();
                    }
                }
            }

            return result;
        }

    }
}

Create a form named: FrmMain.cs

FrmMain.Designer.cs

namespace WebView2HostObjectsTest
{
    partial class FrmMain
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.menuStrip1 = new System.Windows.Forms.MenuStrip();
            this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.monitorEventsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
            this.btnBack = new System.Windows.Forms.Button();
            this.btnForward = new System.Windows.Forms.Button();
            this.textBoxAddressBar = new System.Windows.Forms.TextBox();
            this.btnGo = new System.Windows.Forms.Button();
            this.btnRefresh = new System.Windows.Forms.Button();
            this.btnStop = new System.Windows.Forms.Button();
            this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel();
            this.panelLeftMenu = new System.Windows.Forms.Panel();
            this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel();
            this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel();
            this.webView21 = new Microsoft.Web.WebView2.WinForms.WebView2();
            this.richTextBoxOutput = new System.Windows.Forms.RichTextBox();
            this.menuStrip1.SuspendLayout();
            this.tableLayoutPanel1.SuspendLayout();
            this.tableLayoutPanel2.SuspendLayout();
            this.tableLayoutPanel3.SuspendLayout();
            this.panelLeftMenu.SuspendLayout();
            this.tableLayoutPanel5.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).BeginInit();
            this.SuspendLayout();
            // 
            // menuStrip1
            // 
            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.fileToolStripMenuItem,
            this.toolsToolStripMenuItem});
            this.menuStrip1.Location = new System.Drawing.Point(0, 0);
            this.menuStrip1.Name = "menuStrip1";
            this.menuStrip1.Size = new System.Drawing.Size(879, 24);
            this.menuStrip1.TabIndex = 0;
            this.menuStrip1.Text = "menuStrip1";
            // 
            // fileToolStripMenuItem
            // 
            this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.exitToolStripMenuItem});
            this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
            this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
            this.fileToolStripMenuItem.Text = "File";
            // 
            // exitToolStripMenuItem
            // 
            this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
            this.exitToolStripMenuItem.Size = new System.Drawing.Size(93, 22);
            this.exitToolStripMenuItem.Text = "&Exit";
            // 
            // toolsToolStripMenuItem
            // 
            this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.monitorEventsToolStripMenuItem});
            this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
            this.toolsToolStripMenuItem.Size = new System.Drawing.Size(46, 20);
            this.toolsToolStripMenuItem.Text = "Tools";
            // 
            // monitorEventsToolStripMenuItem
            // 
            this.monitorEventsToolStripMenuItem.Name = "monitorEventsToolStripMenuItem";
            this.monitorEventsToolStripMenuItem.Size = new System.Drawing.Size(154, 22);
            this.monitorEventsToolStripMenuItem.Text = "Monitor Events";
            this.monitorEventsToolStripMenuItem.Click += new System.EventHandler(this.monitorEventsToolStripMenuItem_Click);
            // 
            // statusStrip1
            // 
            this.statusStrip1.Location = new System.Drawing.Point(0, 554);
            this.statusStrip1.Name = "statusStrip1";
            this.statusStrip1.Size = new System.Drawing.Size(879, 22);
            this.statusStrip1.TabIndex = 1;
            this.statusStrip1.Text = "statusStrip1";
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.ColumnCount = 1;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 0);
            this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 0, 1);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 24);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.RowCount = 3;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 50F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5F));
            this.tableLayoutPanel1.Size = new System.Drawing.Size(879, 530);
            this.tableLayoutPanel1.TabIndex = 2;
            // 
            // tableLayoutPanel2
            // 
            this.tableLayoutPanel2.ColumnCount = 7;
            this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 75F));
            this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 75F));
            this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 75F));
            this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 75F));
            this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 75F));
            this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 5F));
            this.tableLayoutPanel2.Controls.Add(this.btnBack, 0, 0);
            this.tableLayoutPanel2.Controls.Add(this.btnForward, 1, 0);
            this.tableLayoutPanel2.Controls.Add(this.textBoxAddressBar, 2, 0);
            this.tableLayoutPanel2.Controls.Add(this.btnGo, 3, 0);
            this.tableLayoutPanel2.Controls.Add(this.btnRefresh, 4, 0);
            this.tableLayoutPanel2.Controls.Add(this.btnStop, 5, 0);
            this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 3);
            this.tableLayoutPanel2.Name = "tableLayoutPanel2";
            this.tableLayoutPanel2.RowCount = 1;
            this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel2.Size = new System.Drawing.Size(873, 44);
            this.tableLayoutPanel2.TabIndex = 1;
            // 
            // btnBack
            // 
            this.btnBack.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
            this.btnBack.Location = new System.Drawing.Point(3, 5);
            this.btnBack.Name = "btnBack";
            this.btnBack.Size = new System.Drawing.Size(69, 34);
            this.btnBack.TabIndex = 1;
            this.btnBack.Text = "Back";
            this.btnBack.UseVisualStyleBackColor = true;
            this.btnBack.Click += new System.EventHandler(this.btnBack_Click);
            // 
            // btnForward
            // 
            this.btnForward.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
            this.btnForward.Location = new System.Drawing.Point(78, 5);
            this.btnForward.Name = "btnForward";
            this.btnForward.Size = new System.Drawing.Size(69, 34);
            this.btnForward.TabIndex = 2;
            this.btnForward.Text = "Forward";
            this.btnForward.UseVisualStyleBackColor = true;
            this.btnForward.Click += new System.EventHandler(this.btnForward_Click);
            // 
            // textBoxAddressBar
            // 
            this.textBoxAddressBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
            this.textBoxAddressBar.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.textBoxAddressBar.Location = new System.Drawing.Point(153, 11);
            this.textBoxAddressBar.Name = "textBoxAddressBar";
            this.textBoxAddressBar.Size = new System.Drawing.Size(487, 22);
            this.textBoxAddressBar.TabIndex = 3;
            this.textBoxAddressBar.Text = "https://www.microsoft.com";
            this.textBoxAddressBar.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBoxAddressBar_KeyDown);
            // 
            // btnGo
            // 
            this.btnGo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
            this.btnGo.Location = new System.Drawing.Point(646, 5);
            this.btnGo.Name = "btnGo";
            this.btnGo.Size = new System.Drawing.Size(69, 34);
            this.btnGo.TabIndex = 4;
            this.btnGo.Text = "Go";
            this.btnGo.UseVisualStyleBackColor = true;
            this.btnGo.Click += new System.EventHandler(this.btnGo_Click);
            // 
            // btnRefresh
            // 
            this.btnRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
            this.btnRefresh.Location = new System.Drawing.Point(721, 5);
            this.btnRefresh.Name = "btnRefresh";
            this.btnRefresh.Size = new System.Drawing.Size(69, 34);
            this.btnRefresh.TabIndex = 5;
            this.btnRefresh.Text = "Refresh";
            this.btnRefresh.UseVisualStyleBackColor = true;
            this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
            // 
            // btnStop
            // 
            this.btnStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
            this.btnStop.Location = new System.Drawing.Point(796, 5);
            this.btnStop.Name = "btnStop";
            this.btnStop.Size = new System.Drawing.Size(69, 34);
            this.btnStop.TabIndex = 6;
            this.btnStop.Text = "Stop";
            this.btnStop.UseVisualStyleBackColor = true;
            this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
            // 
            // tableLayoutPanel3
            // 
            this.tableLayoutPanel3.ColumnCount = 2;
            this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 180F));
            this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel3.Controls.Add(this.panelLeftMenu, 0, 0);
            this.tableLayoutPanel3.Controls.Add(this.tableLayoutPanel5, 1, 0);
            this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 53);
            this.tableLayoutPanel3.Name = "tableLayoutPanel3";
            this.tableLayoutPanel3.RowCount = 1;
            this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel3.Size = new System.Drawing.Size(873, 469);
            this.tableLayoutPanel3.TabIndex = 2;
            // 
            // panelLeftMenu
            // 
            this.panelLeftMenu.AutoScroll = true;
            this.panelLeftMenu.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.panelLeftMenu.Controls.Add(this.tableLayoutPanel4);
            this.panelLeftMenu.Dock = System.Windows.Forms.DockStyle.Fill;
            this.panelLeftMenu.Location = new System.Drawing.Point(3, 3);
            this.panelLeftMenu.Name = "panelLeftMenu";
            this.panelLeftMenu.Size = new System.Drawing.Size(174, 463);
            this.panelLeftMenu.TabIndex = 0;
            // 
            // tableLayoutPanel4
            // 
            this.tableLayoutPanel4.AutoScroll = true;
            this.tableLayoutPanel4.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.tableLayoutPanel4.ColumnCount = 1;
            this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 0);
            this.tableLayoutPanel4.Name = "tableLayoutPanel4";
            this.tableLayoutPanel4.RowCount = 11;
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
            this.tableLayoutPanel4.Size = new System.Drawing.Size(174, 463);
            this.tableLayoutPanel4.TabIndex = 5;
            // 
            // tableLayoutPanel5
            // 
            this.tableLayoutPanel5.ColumnCount = 1;
            this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
            this.tableLayoutPanel5.Controls.Add(this.webView21, 0, 0);
            this.tableLayoutPanel5.Controls.Add(this.richTextBoxOutput, 0, 1);
            this.tableLayoutPanel5.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel5.Location = new System.Drawing.Point(183, 3);
            this.tableLayoutPanel5.Name = "tableLayoutPanel5";
            this.tableLayoutPanel5.RowCount = 2;
            this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 100F));
            this.tableLayoutPanel5.Size = new System.Drawing.Size(687, 463);
            this.tableLayoutPanel5.TabIndex = 1;
            // 
            // webView21
            // 
            this.webView21.CreationProperties = null;
            this.webView21.Dock = System.Windows.Forms.DockStyle.Fill;
            this.webView21.Location = new System.Drawing.Point(3, 3);
            this.webView21.Name = "webView21";
            this.webView21.Size = new System.Drawing.Size(681, 357);
            this.webView21.TabIndex = 0;
            this.webView21.ZoomFactor = 1D;
            this.webView21.CoreWebView2InitializationCompleted += new System.EventHandler<Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs>(this.webView21_CoreWebView2InitializationCompleted);
            this.webView21.NavigationStarting += new System.EventHandler<Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs>(this.webView21_NavigationStarting);
            this.webView21.NavigationCompleted += new System.EventHandler<Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs>(this.webView21_NavigationCompleted);
            this.webView21.WebMessageReceived += new System.EventHandler<Microsoft.Web.WebView2.Core.CoreWebView2WebMessageReceivedEventArgs>(this.webView21_WebMessageReceived);
            // 
            // richTextBoxOutput
            // 
            this.richTextBoxOutput.BackColor = System.Drawing.Color.Gainsboro;
            this.richTextBoxOutput.Dock = System.Windows.Forms.DockStyle.Fill;
            this.richTextBoxOutput.Location = new System.Drawing.Point(3, 366);
            this.richTextBoxOutput.Name = "richTextBoxOutput";
            this.richTextBoxOutput.ReadOnly = true;
            this.richTextBoxOutput.Size = new System.Drawing.Size(681, 94);
            this.richTextBoxOutput.TabIndex = 1;
            this.richTextBoxOutput.Text = "";
            // 
            // FrmMain
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(879, 576);
            this.Controls.Add(this.tableLayoutPanel1);
            this.Controls.Add(this.statusStrip1);
            this.Controls.Add(this.menuStrip1);
            this.MainMenuStrip = this.menuStrip1;
            this.Name = "FrmMain";
            this.Text = "WebView2 - GettingStarted - v2";
            this.Load += new System.EventHandler(this.FrmMain_Load);
            this.menuStrip1.ResumeLayout(false);
            this.menuStrip1.PerformLayout();
            this.tableLayoutPanel1.ResumeLayout(false);
            this.tableLayoutPanel2.ResumeLayout(false);
            this.tableLayoutPanel2.PerformLayout();
            this.tableLayoutPanel3.ResumeLayout(false);
            this.panelLeftMenu.ResumeLayout(false);
            this.tableLayoutPanel5.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.webView21)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.MenuStrip menuStrip1;
        private System.Windows.Forms.StatusStrip statusStrip1;
        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
        private System.Windows.Forms.Button btnBack;
        private System.Windows.Forms.Button btnForward;
        private System.Windows.Forms.TextBox textBoxAddressBar;
        private System.Windows.Forms.Button btnGo;
        private System.Windows.Forms.Button btnRefresh;
        private System.Windows.Forms.Button btnStop;
        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
        private System.Windows.Forms.Panel panelLeftMenu;
        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5;
        private Microsoft.Web.WebView2.WinForms.WebView2 webView21;
        private System.Windows.Forms.RichTextBox richTextBoxOutput;
        private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem monitorEventsToolStripMenuItem;
    }
}

FrmMain.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;

namespace WebView2HostObjectsTest
{
    public partial class FrmMain : Form
    {
        FrmWebView2Msg _frmWebview2Msg = null;
        ExternalDocHostUICallback _externalDocHostUICallback = null;
        public FrmMain()
        {
            InitializeComponent();
        }

        private async void FrmMain_Load(object sender, EventArgs e)
        {
            EnableMsgLoggingFrm();

            LogMsg("Form1_Load");

            LogMsg("MS Edge Version: " + CoreWebView2Environment.GetAvailableBrowserVersionString());


            //initialized CorewWebView2
            //await InitializeCoreWebView2Async();
            await InitializeCoreWebView2Async(webView21);

            //if desired to use 'AddScriptToExecuteOnDocumentCreatedAsync', it must be called prior
            //to the WebView2 Source property being set, otherwise the code won't be executed
            //ie: setting 'Source' causes the event to be raised that causes 
            //'AddScriptToExecuteOnDocumentCreatedAsync' to be run. Attempting to use
            //'AddScriptToExecuteOnDocumentCreatedAsync' in 'NavigationStarting' (or later event handlers)
            //will have no effect (the code won't be executed/run). After this point, use
            //'CoreWebView2.ExecuteScriptAsync' instead.


            //The code commented below won't work because of the JavaScript code being used.
            //particularly using '$', which won't be defined until CoreWebView2 'DOMContentLoaded'
            //has occurred.

            /*
             
            string jsCode = string.Empty;
            jsCode = HelperLoadResource.ReadResource("JavaScript1.js");

            LogMsg(jsCode + "\n");

            if (!String.IsNullOrEmpty(jsCode))
            {
                //create new instance
                _externalDocHostUICallback = new ExternalDocHostUICallback();

                //subscribe to event(s) (listen to events)
                _externalDocHostUICallback.SystemIdentificationValueUpdated += _externalDocHostUICallback_SystemIdentificationValueUpdated;

                //add host object
                webView21.CoreWebView2.AddHostObjectToScript("handler", _externalDocHostUICallback);

                await webView21.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(jsCode);
            }

            */

            //set Source
            webView21.Source = new Uri("http://www.microsoft.com");

        }

        private void EnableMsgLoggingFrm()
        {
            if (_frmWebview2Msg == null || _frmWebview2Msg.IsDisposed)
            {
                //create new instance
                _frmWebview2Msg = new FrmWebView2Msg();

                //subscribe to event(s)
                _frmWebview2Msg.FormClosed += _frmWebview2Msg_FormClosed;

                _frmWebview2Msg.Show();
            }
            else
            {
                //make form visible
                _frmWebview2Msg.WindowState = FormWindowState.Normal;
                _frmWebview2Msg.Activate();
            }
        }

        private async Task InitializeCoreWebView2Async()
        {
            //initialize CorewWebView2
            await webView21.EnsureCoreWebView2Async();
        }

        public async Task InitializeCoreWebView2Async(WebView2 wv, string webCacheDir = "")
        {
            CoreWebView2EnvironmentOptions options = null;
            string tempWebCacheDir = string.Empty;
            CoreWebView2Environment webView2Environment = null;

            //set value
            tempWebCacheDir = webCacheDir;

            if (String.IsNullOrEmpty(tempWebCacheDir))
            {
                //get fully-qualified path to user's temp folder
                tempWebCacheDir = System.IO.Path.GetTempPath();

                tempWebCacheDir = System.IO.Path.Combine(tempWebCacheDir, System.Guid.NewGuid().ToString("N"));
            }

            //webView2Environment = await CoreWebView2Environment.CreateAsync(@"C:\Program Files (x86)\Microsoft\Edge Dev\Application\85.0.564.8", tempWebCacheDir, options);
            webView2Environment = await CoreWebView2Environment.CreateAsync(null, tempWebCacheDir, options);


            LogMsg("Info: before EnsureCoreWebView2Async");

            //wait for CoreWebView2 initialization
            await wv.EnsureCoreWebView2Async(webView2Environment);

            LogMsg("Info: after EnsureCoreWebView2Aync");

            LogMsg("Info: Cache data folder set to: " + tempWebCacheDir);
        }

        private void LogMsg(string msg)
        {
            //string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
            string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("HH:mm:ss:fff"), msg);
            System.Diagnostics.Debug.WriteLine(logMsg);

            if (_frmWebview2Msg != null && !_frmWebview2Msg.IsDisposed)
            {
                _frmWebview2Msg.AddMessage(logMsg);
            }
        }

        
        private void textBoxAddressBar_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                webView21.Source = new Uri(textBoxAddressBar.Text);
            }
        }

        private void btnBack_Click(object sender, EventArgs e)
        {
            if (webView21 != null && webView21.CanGoBack)
            {
                webView21.GoBack();
            }
        }

        private void btnForward_Click(object sender, EventArgs e)
        {
            if (webView21 != null && webView21.CanGoForward)
            {
                webView21.GoForward();
            }
        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            webView21.Source = new Uri(textBoxAddressBar.Text);
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            if (webView21 != null)
            {
                webView21.Refresh();
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            if (webView21 != null)
            {
                webView21.Stop();
            }
        }



        private void monitorEventsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EnableMsgLoggingFrm();
        }

        private void _frmWebview2Msg_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (_frmWebview2Msg != null)
            {
                try
                {
                    //unsubscribe from event(s)
                    _frmWebview2Msg.FormClosed -= _frmWebview2Msg_FormClosed;
                }
                catch (Exception ex)
                {
                    LogMsg("Error: (_frmWebview2Msg_FormClosed) - " + ex.Message);
                }

                //set value
                _frmWebview2Msg = null;
            }

        }

        private void webView21_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
        {
            //CoreWebView2InitializationCompleted is available starting in WebView2 v1.0.707
            //prior to WebView2 v1.0.707, use CoreWebView2Ready
            LogMsg("webView21_CoreWebView2InitializationCompleted");

            //subscribe to events (add event handlers)
            webView21.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
            webView21.CoreWebView2.HistoryChanged += CoreWebView2_HistoryChanged;
        }

        private async void CoreWebView2_DOMContentLoaded(object sender, CoreWebView2DOMContentLoadedEventArgs e)
        {
            LogMsg("Info: CoreWebView2_DOMContentLoaded");

            if (_externalDocHostUICallback == null)
            {
                //create new instance
                _externalDocHostUICallback = new ExternalDocHostUICallback();

                //subscribe to event(s) (listen to events)
                _externalDocHostUICallback.SystemIdentificationValueUpdated += _externalDocHostUICallback_SystemIdentificationValueUpdated;

                //add host object
                webView21.CoreWebView2.AddHostObjectToScript("handler", _externalDocHostUICallback);

                string jsCode = string.Empty;
                jsCode = HelperLoadResource.ReadResource("JavaScript1.js");

                LogMsg(jsCode + "\n");

                if (!String.IsNullOrEmpty(jsCode))
                {
                    await webView21.CoreWebView2.ExecuteScriptAsync(jsCode);
                }
            }

        }

        private void CoreWebView2_HistoryChanged(object sender, object e)
        {
            btnBack.Enabled = webView21.CoreWebView2.CanGoBack;
            btnForward.Enabled = webView21.CoreWebView2.CanGoForward;
        }

        private async void webView21_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
        {
            LogMsg("webView21_NavigationCompleted");

            StringBuilder scriptToRun = new StringBuilder();
            scriptToRun.Append("function sendData(){ \n");
            scriptToRun.Append("  try{ \n");
            scriptToRun.Append("    $WF.handler.SendUpdates('SendUpdates  - webView21_NavigationCompleted - Success');\n");
            scriptToRun.Append("  } \n");
            scriptToRun.Append("  catch(err){ \n");
            scriptToRun.Append("    window.chrome.webview.postMessage('Error: ' + err.message); \n");
            scriptToRun.Append("  } \n\n");

            scriptToRun.Append("} \n");

            scriptToRun.AppendLine("sendData();\n");

            var result1 = await webView21.CoreWebView2.ExecuteScriptAsync(scriptToRun.ToString());

            if (_frmWebview2Msg  != null)
            {
                //make form visible
                _frmWebview2Msg.WindowState = FormWindowState.Normal;
                _frmWebview2Msg.Activate();
            }
        }

        private void _externalDocHostUICallback_SystemIdentificationValueUpdated(object sender, SystemIdentificationValueUpdatedEventArgs e)
        {
            LogMsg("Info: _externalDocHostUICallback_SystemIdentificationValueUpdated");
            LogMsg("      Data: " + e.Data);
        }

        private void webView21_NavigationStarting(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs e)
        {
            LogMsg("webView21_NavigationStarting: " + e.Uri.ToString());

            if (_externalDocHostUICallback != null)
            {
                try
                {
                    webView21.CoreWebView2.RemoveHostObjectFromScript("handler");
                }
                catch (System.Runtime.InteropServices.COMException ex)
                {
                    LogMsg("Error: (TestHostObject) - " + ex.Message);
                }
                catch (Exception ex)
                {
                    LogMsg("Error: (TestHostObject) - " + ex.Message);
                }

                //unsubscribe to event(s) (remove listener)
                _externalDocHostUICallback.SystemIdentificationValueUpdated -= _externalDocHostUICallback_SystemIdentificationValueUpdated;

                //set value
                _externalDocHostUICallback = null;
            }


            textBoxAddressBar.Text = e.Uri.ToString();
            textBoxAddressBar.Refresh();

            //set cursor to end of TextBox
            textBoxAddressBar.SelectionStart = textBoxAddressBar.TextLength;
            textBoxAddressBar.SelectionLength = 0;
        }

        private void webView21_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
        {
            LogMsg("webView21_WebMessageReceived");

            try
            {
                LogMsg("          MSG (JSON): " + e.WebMessageAsJson);
                LogMsg("          MSG (String): " + e.TryGetWebMessageAsString());
            }
            catch (Exception ex)
            {
                LogMsg("Error: (webView21_WebMessageReceived) - " + ex.Message);
            }
        }
    }
}


Here are 2 demo projects. One that doesn't work, and one that works. To use the demo projects ensure that PackageManagement is set to PackageReference prior to opening them.

  • On VS menu, select Tools
  • Select Options
  • Expand NuGet Package Manager
  • Click General
  • Under Package Management, set Default package management format: to PackageReference

WebView2HostObjectsTest - doesn't work.zip

WebView2HostObjectsTest - works.zip

Resources
How to read embedded resource text file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants