Passing an instance of a C # class back to managed code from JavaScript via COM

The basic outline of my problem is shown in the code below. I place the WebBrowser control on the form and provide ObjectForScripting two ways: GiveMeAGizmo and GiveMeAGizmoUser . Both methods return the corresponding instances of the class:

 [ComVisible] public class Gizmo { public string name { get; set; } } [ComVisible] public class GizmoUser { public void doSomethingWith(object oGizmo) { Gizmo g = (Gizmo) oGizmo; System.Diagnostics.Debug.WriteLine(g.name); } } 

In JavaScript, I instantiate both classes, but I need to pass the first instance to the method in the second instance. JS code looks something like this:

 var // Returns a Gizmo instance gizmo = window.external.GiveMeAGizmo(), // Returns a GizmoUser instance gUser = window.external.GiveMeAGizmoUser(); gizmo.name = 'hello'; // Passes Gizmo instance back to C# code gUser.doSomethingWith(gizmo); 

Here I hit the wall. My C # GizmoUser.doSomethingWith() method cannot return the object back to the Gizmo type. It produces the following error:

Unable to pass COM object of type "System .__ ComObject" to interface type "Gizmo"

I don’t know how to act, I tried a couple of other things:

  • Safe casting Gizmo g = oGizmo as Gizmo; ( g is null )
  • If the classes implement IDispatch and invoke InvokeMember , as described. The member "name" is null .

I need this to work with a version of the .NET Framework below 4.0, so I cannot use dynamic . Does anyone know how I can make this work?

+8
javascript c # com webbrowser-control
source share
2 answers

How interesting. When we return the oGizmo object back to doSomethingWith , it is of type Windows Runtime Object . This behavior is compatible with JavaScript and VBScript.

Now, if we explicitly point MarshalAs(UnmanagedType.IUnknown) to the return value of the GiveMeAGizmo() method, everything works fine , the object can be dropped on Gizmo inside doSomethingWith :

 [ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch)] public class ObjectForScripting { [return: MarshalAs(UnmanagedType.IUnknown)] public object GiveMeAGizmo() { return new Gizmo(); } public object GiveMeAGizmoUser() { return new GizmoUser(); } } 

However, if we specify UnmanagedType.IDispatch or UnmanagedType.Struct (by default this will marshal the object as COM VARIANT ), the problem will return.

Thus, there is a workaround, but so far there has not been a reasonable explanation for this interaction with the COM interface.

[UPDATE] A few experiments below. Please note that getting gizmo1 was successful, but gizmo2 is not:

C # :

 // pass a Gizmo object to JavaScript this.webBrowser.Document.InvokeScript("SetGizmo", new Object[] { new Gizmo()}); // get it back, this works var gizmo1 = (Gizmo)this.webBrowser.Document.InvokeScript("GetGizmo"); // get a new Gizmo, via window.external.GiveMeAGizmo() // this fails var gizmo2 = (Gizmo)this.webBrowser.Document.InvokeScript("GetGizmo2"); 

JavaScript:

 var _gizmo; function SetGizmo(gizmo) { _gizmo = gizmo; } function GetGizmo() { return _gizmo; } function GetGizmo2() { return window.external.GiveMeAGizmo(); } 

This is just an assumption, but I think this behavior may have something to do with the .NET security permission sets imposed by WebBrowser.ObjectForScripting .

+4
source share

You need to do two things

  • Find out the type of object as described here .

  • Extract the actual object from it using Marshal.GetObjectForIUnknown (read to the end, there is an interface for implementation).

0
source share

All Articles