The basic outline of my problem is shown in the code below. I'm hosting a WebBrowser control in a form and providing an ObjectForScripting
with two methods: GiveMeAGizmo
and GiveMeAGizmoUser
. Both methods return the respective class instances:
[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 create an instance of both classes, but I need to pass the first instance to a method on the second instance. The JS code looks a little 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);
This is where I've hit a wall. My C# method GizmoUser.doSomethingWith()
cannot cast the object back to a Gizmo type. It throws the following error:
Unable to cast COM object of type 'System.__ComObject' to interface type 'Gizmo'
Unsure how to proceed, I tried a couple of other things:
- Safe casting
Gizmo g = oGizmo as Gizmo;
(g
isnull
) - Having the classes implement
IDispatch
and callingInvokeMember
, as explained here. The member "name" isnull
.
I need this to work with .NET framework version lower than 4.0, so I cannot use dynamic
. Does anybody know how I can get this working?
How interesting. When we receive the
oGizmo
object back indoSomethingWith
, it is of the typeWindows Runtime Object
. This behavior is consistent between JavaScript and VBScript.Now, if we explicitly specify
MarshalAs(UnmanagedType.IUnknown)
on the return value of theGiveMeAGizmo()
method, everything works fine, the object can be cast back toGizmo
insidedoSomethingWith
:Still, if we specify
UnmanagedType.IDispatch
orUnmanagedType.Struct
(the default one which marshals the object as COMVARIANT
), the problem is back.Thus, there's a workaround, but no reasonable explanation for such COM interop behavior, so far.
[UPDATE] A few more experiments, below. Note how obtaining
gizmo1
is successful, whilegizmo2
is not:C#:
JavaScript:
It's only a guess, but I think such behavior might have something to do with .NET security permission sets, imposed by
WebBrowser.ObjectForScripting
.You need to do two things
Find out the type of object as described here.
Extract the actual object outta it using Marshal.GetObjectForIUnknown (read till the end, there is an interface to implement).