Open champnic opened 3 years ago
I've created an item on our backlog to track this, thanks for calling it out! We're working with the .NET team on the right path forwards.
Using ClassInterfaceType.AutoDual
is not required. ClassInterfaceType.None
can be used. If ClassInterfaceType.None
is used, one needs to create interface(s) for each class. The following code also demonstrates using an event to send data from JavaScript to C#. I believe that the maximum depth currently supported by hostObjects is 3.
For the following code, ensure that you've subscribed to the WebView2 control's WebMessageReceived
event.
C#: (WebView2 v.1.0.721)
Set WebView2 control settings
//set WebView2 control settings
webView21.CoreWebView2.Settings.AreHostObjectsAllowed = true;
webView21.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = true;
webView21.CoreWebView2.Settings.IsScriptEnabled = true;
webView21.CoreWebView2.Settings.IsWebMessageEnabled = true;
Create a class named: EmployeeInfoValueUpdatedArgs
EmployeeInfoValueUpdatedArgs.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace WebView2UsingHostObject
{
[ComVisible(false)]
public delegate void EventHandlerEmployeeInfoValueUpdated(object sender, EmployeeInfoValueUpdatedArgs e);
[ComVisible(true)]
public class EmployeeInfoValueUpdatedArgs : System.EventArgs
{
public string Data { get; private set; } = string.Empty;
public EmployeeInfoValueUpdatedArgs(string data)
{
this.Data = data;
}
}
}
Create a class named: Employee.cs
Employee.cs
Employee.cs
Add the following using statements:
using System.Runtime.InteropServices;
using Newtonsoft.Json;
To allow use with event(s), add the following interface (in Employee.cs):
IComEventsEmployee
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IComEventsEmployee
{
void ValueUpdatedEmployee(object sender, EmployeeInfoValueUpdatedArgs e);
}
Create an interface for Employee (in Employee.cs):
IEmployee
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IEmployee
{
//If a method / property doesn't need to be COM visible
//(available in Javascript), don't include it in the interface
string EmployeeId { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
void AddPhone(string phoneNumberType, string phoneNumber);
string GetPhone();
string GetPhoneAsJson();
string GetPhoneAsJsonNewtonsoft();
void SendUpdates(string data);
}
Add code to the Employee class (in Employee.cs):
Employee
Note: Employee
implements IEmployee
, but IComEventsEmployee
is only specified in [ComSourceInterfaces(typeof(IComEventsEmployee))]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IComEventsEmployee))]
public class Employee : IEmployee
{
//variable name as declared in the interface (IComEventsEmployee)
//event interested parties can register with to know
//when value is updated.
public event EventHandlerEmployeeInfoValueUpdated ValueUpdatedEmployee;
public string EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<PhoneInfo> Phone { get; set; } = new List<PhoneInfo>();
public void AddPhone(string phoneNumberType, string phoneNumber)
{
Phone.Add(new PhoneInfo(phoneNumberType, phoneNumber));
}
public string GetPhone()
{
StringBuilder sb = new StringBuilder();
if (Phone != null && Phone.Count > 0)
{
foreach (PhoneInfo p in Phone)
{
sb.AppendFormat("PhoneNumberType: {0}, PhoneNumber: {1}", p.PhoneNumberType, p.PhoneNumber);
}
}
return sb.ToString();
}
public string GetPhoneAsJson()
{
string jsonStr = string.Empty;
string jsonInnerStr = string.Empty;
if (Phone != null && Phone.Count > 0)
{
jsonStr = String.Format("[");
foreach (PhoneInfo p in Phone)
{
if (!String.IsNullOrEmpty(jsonInnerStr))
{
jsonInnerStr += ",";
}
jsonInnerStr += String.Format("{{\"PhoneNumberType\":\"{0}\",\"PhoneNumber\":\"{1}\"}}", p.PhoneNumberType, p.PhoneNumber);
}
jsonStr += jsonInnerStr;
jsonStr += String.Format("]");
}
return jsonStr;
}
public string GetPhoneAsJsonNewtonsoft()
{
return JsonConvert.SerializeObject(Phone);
}
public void SendUpdates(string data)
{
//Important: don't raise the event unless there are subscribers
if (ValueUpdatedEmployee != null)
{
//create a new instance
//using the constructor to set the value
EmployeeInfoValueUpdatedArgs valueArgs = new EmployeeInfoValueUpdatedArgs(data);
//raise event 'ValueUpdatedEmployee'
ValueUpdatedEmployee(this, valueArgs);
}//if
}
}
Create a class named: PhoneInfo.cs
PhoneInfo.cs
PhoneInfo.cs
Add the following using statements:
using System.Runtime.InteropServices;
using Newtonsoft.Json;
To allow use with event(s), add the following interface (in PhoneInfo.cs):
IComEventsPhoneInfo
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IComEventsPhoneInfo
{
//Todo: add event - optional
}
Create an interface for Employee (in PhoneInfo.cs):
IPhoneInfo
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IPhoneInfo
{
//If a method / property doesn't need to be COM visible
//(available in Javascript), don't include it in the interface
string PhoneNumberType { get; set; }
string PhoneNumber { get; set; }
}
Add code to the PhoneInfo class (in PhoneInfo.cs):
PhoneInfo
Note: Employee implements IPhoneInfo, but IComEventsPhoneInfo
is only specified in [ComSourceInterfaces(typeof(IComEventsPhoneInfo))]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IComEventsPhoneInfo))]
public class PhoneInfo : IPhoneInfo
{
//Todo: add event - optional
public string PhoneNumberType { get; set; }
public string PhoneNumber { get; set; }
public PhoneInfo()
{
}
public PhoneInfo(string phoneNumberType, string phoneNumber)
{
this.PhoneNumberType = phoneNumberType;
this.PhoneNumber = phoneNumber;
}
}
To Use
Example:
private async Task TestHostObjectEmployeeInfo()
{
StringBuilder scriptToRun = new StringBuilder();
//create new instance
Employee employee = new Employee();
//add host object
webView21.CoreWebView2.AddHostObjectToScript("employee", employee);
//subscribe to event(s) (listen to events)
employee.ValueUpdatedEmployee += Employee_ValueUpdatedEmployee;
scriptToRun = new StringBuilder();
//#1
scriptToRun.Append("function sendData(){ \n");
scriptToRun.Append(" try{ \n");
scriptToRun.Append(" chrome.webview.hostObjects.sync.employee.FirstName = 'John';\n");
scriptToRun.Append(" let result = chrome.webview.hostObjects.sync.employee.FirstName; \n");
scriptToRun.Append(" return result; \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());
//#2
scriptToRun = new StringBuilder();
scriptToRun.Append("function sendData(){ \n");
scriptToRun.Append(" try{ \n");
scriptToRun.Append(" chrome.webview.hostObjects.sync.employee.AddPhone('home','800-555-1234'); \n");
scriptToRun.Append(" let result =
chrome.webview.hostObjects.sync.employee.GetPhoneAsJsonNewtonsoft(); \n");
scriptToRun.Append(" window.chrome.webview.postMessage(result); \n");
scriptToRun.Append(" return result; \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 result2 = await webView21.CoreWebView2.ExecuteScriptAsync(scriptToRun.ToString());
//#3
scriptToRun = new StringBuilder();
scriptToRun.Append("function sendData(){ \n");
scriptToRun.Append(" try{ \n");
scriptToRun.Append(" chrome.webview.hostObjects.sync.employee.FirstName = 'Tom';\n");
scriptToRun.Append(" let result = chrome.webview.hostObjects.sync.employee.FirstName; \n");
scriptToRun.Append(" chrome.webview.hostObjects.sync.employee.SendUpdates(result + ' hello from JS'); \n");
scriptToRun.Append(" return result; \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");
//LogMsg(scriptToRun.ToString());
var result3 = await webView21.CoreWebView2.ExecuteScriptAsync(scriptToRun.ToString());
try
{
webView21.CoreWebView2.RemoveHostObjectFromScript("employee");
}
catch (System.Runtime.InteropServices.COMException ex)
{
System.Diagnostics.Debug.WriteLine("Error: (COMException) - " + ex.Message);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
//unsubscribe to event(s) (remove listener)
employee.ValueUpdatedEmployee -= Employee_ValueUpdatedEmployee;
employee = null;
}
Employee_ValueUpdatedEmployee
private void Employee_ValueUpdatedEmployee(object sender, EmployeeInfoValueUpdatedArgs e)
{
System.Diagnostics.Debug.WriteLine("Employee_ValueUpdatedEmployee event raised");
System.Diagnostics.Debug.WriteLine("Data: " + e.Data);
}
Some other things that can be specified are Guid
, DispId
, and ProgId
.
Note: Dispin is only specified in the interface(s). I believe each value should be unique among the interfaces used by a particular class. The values should also be sequential.
According to this post:
DispId: Uniquely identifies methods and properties of an interface/class to the COM Interop system. ... The ID’s that you assign in DispID need to be sequential (1, 2, 3, etc.).
Guid: Each COM visible component (Classes, Interfaces, etc.) within your assembly is identified within the Interop system through a Guid.
ComInterfaceType:
ClassInterfaceType (lets COM know whether your class supports Early Binding, Late Binding, or both.)
Note: To be available in VB6/VBA, the class may need to be registered using RegAsm.exe.
Also see:
IComEventsEmployee
[ComVisible(true)]
[Guid("your guid 1 here - containing only letters, numbers, and dashes")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IComEventsEmployee
{
[DispId(0x00000001)]
void ValueUpdatedEmployee(object sender, EmployeeInfoValueUpdatedArgs e);
}
IEmployee
[ComVisible(true)]
[Guid("your guid 2 here - containing only letters, numbers, and dashes")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IEmployee
{
[DispId(0x10000001)]
string EmployeeId { get; set; }
[DispId(0x10000002)]
string FirstName { get; set; }
...
}
Employee
[ComVisible(true)]
[Guid("your guid 3 here - containing only letters, numbers, and dashes")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IComEventsEmployee))]
[ProgId("MyNamespace.Employee")]
public class Employee : IEmployee
{
public event EventHandlerEmployeeInfoValueUpdated ValueUpdatedEmployee;
public string EmployeeId { get; set; }
public string FirstName { get; set; }
...
}
Regasm.exe: When deploying your dll to other machines, use RegAsm.exe. It exports a type library (.tlb file) and registers it for COM. It’s okay if you overwrite an existing type library for the same assembly. The RegAsm.exe utility replaces the traditional RegSvr32.exe registration utility.
RegAsm.exe can be found in: %SystemRoot%\Microsoft.NET\Framework64\<version> or (%SystemRoot%\Microsoft.NET\Framework\<version>)
See
Here's a version for VB .NET that uses ClassInterfaceType.None
instead of the ClassInterfaceType.AutoDual
. If ClassInterfaceType.None is used, one needs to create interface(s) for each class. The following code also demonstrates using an event to send data from JavaScript to VB .NET. (maximum depth currently supported by hostObjects is 3).
For the following code, ensure that you've subscribed to the WebView2 control's WebMessageReceived event. The instructions below are for VS 2019. However, you can also use VS 2017.
VB .NET (WebView2 v1.0.721)
Set WebView2 control settings
'set WebView2 control settings
WebView21.CoreWebView2.Settings.AreHostObjectsAllowed = True
WebView21.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = True
WebView21.CoreWebView2.Settings.IsScriptEnabled = True
WebView21.CoreWebView2.Settings.IsWebMessageEnabled = True
Create a class named: EmployeeInfoValueUpdatedArgs
EmployeeInfoValueUpdatedArgs
Option Strict Off
Option Explicit On
Imports System.Runtime.InteropServices
Imports Newtonsoft.Json
<ComVisible(False)>
Public Delegate Sub EventHandlerEmployeeInfoValueUpdated(sender As Object, e As EmployeeInfoValueUpdatedArgs)
Public Class EmployeeInfoValueUpdatedArgs
Public Data As String
Public Sub New()
End Sub
Public Sub New(userData As String)
Data = userData
End Sub
End Class
Create a class named: EmployeeInfo.vb
EmployeeInfo.vb
Add the following statements:
Option Strict Off
Option Explicit On
Imports System.Text
Imports System.Runtime.InteropServices
Imports Newtonsoft.Json
To allow use with event(s), add the following interface (in EmployeeInfo.vb):
<ComVisible(True)>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface IComEventsEmployeeInfo
'ToDo: add event(s) - optional
Event ValueUpdatedEmployeeInfo()
End Interface
Create an interface for EmployeeInfo (in EmployeeInfo.vb)
IEmployeeInfo
<ComVisible(True)>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface IEmployeeInfo
Property EmployeeId() As String
Property FirstName() As String
Property LastName() As String
Sub AddComputer(compAssetNumber As String, compDescription As String, compMake As String, compModel As String)
Sub AddPhone(phoneNumberType As String, phoneNumber As String)
Function GetPhone()
Function GetPhoneAsJson()
Function GetPhoneAsJsonNewtonsoft()
Sub SendUpdates(data As String)
End Interface
Add code to the EmployeeInfo class (in EmployeeInfo.vb)
EmployeeInfo
Note: EmployeeInfo
implements IEmployeeInfo
, but IComEventsEmployeeInfo
is only specified in <ComSourceInterfaces(GetType(IComEventsEmployeeInfo))>
<ComVisible(True)>
<ClassInterface(ClassInterfaceType.None)>
<ComDefaultInterface(GetType(IEmployeeInfo))>
<ComSourceInterfaces(GetType(IComEventsEmployeeInfo))>
Public Class EmployeeInfo
Implements IEmployeeInfo
'ToDo: add event(s) - optional
Public Event ValueUpdatedEmployee As EventHandlerEmployeeInfoValueUpdated
Private empId As String = String.Empty
Private fName As String = String.Empty
Private lName As String = String.Empty
Public Property CompanyAssets As List(Of CompanyAssetInfo) = New List(Of CompanyAssetInfo)
Public Property EmployeeId() As String Implements IEmployeeInfo.EmployeeId
Get
Return empId
End Get
Set(ByVal value As String)
empId = value
End Set
End Property
Public Property FirstName() As String Implements IEmployeeInfo.FirstName
Get
Return fName
End Get
Set(ByVal value As String)
fName = value
End Set
End Property
Public Property LastName() As String Implements IEmployeeInfo.LastName
Get
Return lName
End Get
Set(ByVal value As String)
lName = value
End Set
End Property
Public Property Phone As List(Of PhoneInfo) = New List(Of PhoneInfo)
Public Sub AddComputer(compAssetNumber As String, compDescription As String, compMake As String, compModel As String) Implements IEmployeeInfo.AddComputer
Dim assetInfo = New CompanyAssetInfo()
assetInfo.AddComputer(compAssetNumber, compDescription, compMake, compModel)
'add to list
Me.CompanyAssets.Add(assetInfo)
End Sub
Public Sub AddPhone(phoneNumberType As String, phoneNumber As String) Implements IEmployeeInfo.AddPhone
Phone.Add(New PhoneInfo(phoneNumberType, phoneNumber))
End Sub
Public Function GetPhone() Implements IEmployeeInfo.GetPhone
Dim sb As New StringBuilder
If Phone IsNot Nothing AndAlso Phone.Count > 0 Then
For Each pInfo As PhoneInfo In Phone
sb.AppendFormat("PhoneNumberType: {0}, PhoneNumber: {1}", pInfo.PhoneNumberType, pInfo.PhoneNumber)
Next
End If
Return sb.ToString()
End Function
Public Function GetPhoneAsJson() Implements IEmployeeInfo.GetPhoneAsJson
Dim jsonStr As String = String.Empty
Dim jsonInnerStr As String = String.Empty
If Phone IsNot Nothing AndAlso Phone.Count > 0 Then
jsonStr = String.Format("[")
For Each pInfo As PhoneInfo In Phone
jsonInnerStr += String.Format("{{""PhoneNumberType"":""{0}"",""PhoneNumber"":""{1}""}}", pInfo.PhoneNumberType, pInfo.PhoneNumber)
Next
jsonStr += jsonInnerStr
jsonStr += String.Format("]")
End If
Return jsonStr
End Function
Public Function GetPhoneAsJsonNewtonsoft() Implements IEmployeeInfo.GetPhoneAsJsonNewtonsoft
Return JsonConvert.SerializeObject(Phone)
End Function
Public Sub SendUpdates(data As String) Implements IEmployeeInfo.SendUpdates
Dim valueArgs As EmployeeInfoValueUpdatedArgs = New EmployeeInfoValueUpdatedArgs(data)
RaiseEvent ValueUpdatedEmployee(Me, valueArgs)
End Sub
End Class
Create a class named: PhoneInfo.vb
PhoneInfo.vb
Add the following statements:
Option Strict Off
Option Explicit On
Imports System.Runtime.InteropServices
Imports Newtonsoft.Json
To allow use with event(s), add the following interface (in PhoneInfo.vb):
IComEventsPhoneInfo
<ComVisible(True)>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface IComEventsPhoneInfo
'ToDo: add event(s) - optional
End Interface
Create an interface for EmployeeInfo (in PhoneInfo.cs):
IPhoneInfo
<ComVisible(True)>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface IPhoneInfo
Property ImeiNumber As String
Property Manufacturer As String
Property Model As String
Property PhoneNumber As String
Property PhoneNumberType As String
End Interface
Add code to the PhoneInfo class (in PhoneInfo.vb):
Note: In PhoneInfo, I show another way of declaring a property.
PhoneInfo
Public Class PhoneInfo
Implements IPhoneInfo
Public Property ImeiNumber As String Implements IPhoneInfo.ImeiNumber
Public Property Manufacturer As String Implements IPhoneInfo.Manufacturer
Public Property Model As String Implements IPhoneInfo.Model
Public Property PhoneNumber As String Implements IPhoneInfo.PhoneNumber
Public Property PhoneNumberType As String Implements IPhoneInfo.PhoneNumberType
Public Sub New()
End Sub
Public Sub New(phoneNumber As String, phoneNumberType As String)
Me.PhoneNumber = phoneNumber
Me.PhoneNumberType = phoneNumberType
End Sub
Public Sub New(imeiNumber As String, manufacturer As String, model As String, phoneNumber As String, phoneNumberType As String)
Me.ImeiNumber = imeiNumber
Me.Manufacturer = manufacturer
Me.Model = model
Me.PhoneNumber = phoneNumber
Me.PhoneNumberType = phoneNumberType
End Sub
End Class
To Use:
Example:
Private Async Function TestHostObjectEmployeeInfo() As Task
Dim scriptToRun As New System.Text.StringBuilder
Dim employee As EmployeeInfo = New EmployeeInfo()
'subscribe to event(s)
AddHandler employee.ValueUpdatedEmployee, AddressOf Employee_ValueUpdatedEmployee
'add host object
WebView21.CoreWebView2.AddHostObjectToScript("employee", employee)
'#1
scriptToRun = New StringBuilder()
scriptToRun.AppendFormat("function sendData(){{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" try{{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" window.chrome.webview.postMessage('hello 1a'); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" chrome.webview.hostObjects.sync.employee.FirstName = 'John'; {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" let result = chrome.webview.hostObjects.sync.employee.FirstName; {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" return result; {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" }} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" catch(err){{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" window.chrome.webview.postMessage('Error: ' + err.message); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" }} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat("}} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat("sendData(); {0}", System.Environment.NewLine)
System.Diagnostics.Debug.WriteLine(scriptToRun.ToString())
Dim result1 = Await WebView21.CoreWebView2.ExecuteScriptAsync(scriptToRun.ToString())
System.Diagnostics.Debug.WriteLine("Info: result1: " & result1)
'#2
scriptToRun = New StringBuilder()
scriptToRun.AppendFormat("function sendData(){{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" try{{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" window.chrome.webview.postMessage('hello 2a'); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" chrome.webview.hostObjects.sync.employee.AddPhone('home','800-555-1234'); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" let result = chrome.webview.hostObjects.sync.employee.GetPhoneAsJsonNewtonsoft(); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" window.chrome.webview.postMessage(result); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" return result; {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" }} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" catch(err){{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" window.chrome.webview.postMessage('Error: ' + err.message); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" }} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat("}} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat("sendData(); {0}", System.Environment.NewLine)
Dim result2 = Await WebView21.CoreWebView2.ExecuteScriptAsync(scriptToRun.ToString())
System.Diagnostics.Debug.WriteLine("Info: result2: " & result2)
'#3
scriptToRun = New StringBuilder()
scriptToRun.AppendFormat("function sendData(){{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" try{{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" window.chrome.webview.postMessage('hello 1a'); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" chrome.webview.hostObjects.sync.employee.FirstName = 'John'; {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" let result = chrome.webview.hostObjects.sync.employee.FirstName; {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" chrome.webview.hostObjects.sync.employee.SendUpdates(result + ' hello from JS'); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" return result; {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" }} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" catch(err){{ {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" window.chrome.webview.postMessage('Error: ' + err.message); {0}", System.Environment.NewLine)
scriptToRun.AppendFormat(" }} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat("}} {0}", System.Environment.NewLine)
scriptToRun.AppendFormat("sendData(); {0}", System.Environment.NewLine)
Dim result3 = Await WebView21.CoreWebView2.ExecuteScriptAsync(scriptToRun.ToString())
System.Diagnostics.Debug.WriteLine("Info: result3: " & result3)
Try
'remove host object
WebView21.CoreWebView2.RemoveHostObjectFromScript("employee")
Catch ex As System.Runtime.InteropServices.COMException
System.Diagnostics.Debug.WriteLine("Error (COMException): " & ex.Message)
Catch ex As Exception
System.Diagnostics.Debug.WriteLine("Error: " & ex.Message)
End Try
'unsubscribe from event(s)
RemoveHandler employee.ValueUpdatedEmployee, AddressOf Employee_ValueUpdatedEmployee
employee = Nothing
End Sub
Employee_ValueUpdatedEmployee
Private Sub Employee_ValueUpdatedEmployee(sender As Object, e As EmployeeInfoValueUpdatedArgs)
System.Diagnostics.Debug.WriteLine("Info: Employee_ValueUpdatedEmployee event raised")
System.Diagnostics.Debug.WriteLine("Info: e.Data: " & e.Data)
End Sub
Some other things that can be specified are Guid, DispId, and ProgId. Note: Dispin is only specified in the interface(s). Dispin numbers should be sequential.
According to this post:
DispId: Uniquely identifies methods and properties of an interface/class to the COM Interop system. ... The ID’s that you assign in DispID need to be sequential (1, 2, 3, etc.).
Guid: Each COM visible component (Classes, Interfaces, etc.) within your assembly is identified within the Interop system through a Guid.
ComInterfaceType:
ClassInterfaceType (lets COM know whether your class supports Early Binding, Late Binding, or both.)
Note: To be available in VB6/VBA, the class may need to be registered using RegAsm.exe.
Also see:
IComEventsEmployeeInfo
<ComVisible(True)>
<Guid("your guid 1 here - containing only letters, numbers, and dashes")>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface IComEventsEmployeeInfo
'ToDo: add event(s) - optional
Event ValueUpdatedEmployeeInfo()
End Interface
IEmployeeInfo
<ComVisible(True)>
<Guid("your guid 2 here - containing only letters, numbers, and dashes")>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface IEmployeeInfo
<DispId(&H10000000)>
Property EmployeeId() As String
<DispId(&H10000001)>
Property FirstName() As String
<DispId(&H10000003)>
Property LastName() As String
...
End Interface
EmployeeInfo
<ComVisible(True)>
<Guid("your guid 3 here - containing only letters, numbers, and dashes")>
<ClassInterface(ClassInterfaceType.None), ProgId("your progId")>
<ComDefaultInterface(GetType(IEmployeeInfo))>
<ComSourceInterfaces(GetType(IComEventsEmployeeInfo))>
Public Class EmployeeInfo
Implements IEmployeeInfo
'ToDo: add event(s) - optional
Public Event ValueUpdatedEmployee As EventHandlerEmployeeInfoValueUpdated
Private empId As String = String.Empty
Private fName As String = String.Empty
...
End Class
Note: According to Exposing COM interfaces of a .NET class library for Late Binding, this is the same ProgId that you'd use in VB6 or vbscript
set myobject = createobject ("your progId")
Resources
Although the recommendation in CA1408: Do not use AutoDual ClassInterfaceType applies only to ClassInterfaceType.AutoDual, the [Obsolete("Support for IDispatch may be unavailable in future releases")]
attribute is on both ClassInterfaceType.AutoDispatch and ClassInterfaceType.AutoDual: https://github.com/dotnet/runtime/blob/1af80ba017f6f7644305e1781d8cc9845a92b5f8/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs#L153-L162
There is no ObsoleteAttribute on ComInterfaceType.InterfaceIsIDispatch.
One thing that is a little disconcerting about this is that we need to use this:
Originally posted by @RickStrahl in https://github.com/MicrosoftEdge/WebView2Feedback/issues/199#issuecomment-632965966
AB#28585274