通过COM将C#类实例从JavaScript传递回托管代码

时间:2014-01-14 17:38:08

标签: c# javascript .net com webbrowser-control

我的问题的基本概要显示在下面的代码中。我在表单中托管了一个WebBrowser控件,并为ObjectForScripting提供了两种方法:GiveMeAGizmoGiveMeAGizmoUser。两种方法都返回相应的类实例:

[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);
    }
}

在JavaScript中,我创建了两个类的实例,但我需要将第一个实例传递给第二个实例上的方法。 JS代码看起来有点像这样:

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);

这是我撞墙的地方。我的C#方法GizmoUser.doSomethingWith()无法将对象强制转换为Gizmo类型。它会引发以下错误:

  

无法将'System .__ ComObject'类型的COM对象转换为接口类型'Gizmo'

不确定如何继续,我尝试了其他一些事情:

  • 安全施法Gizmo g = oGizmo as Gizmo;gnull
  • 让类实现IDispatch并调用InvokeMemberexplained here。成员“名称”为null

我需要这个以使用低于4.0的.NET框架版本,所以我不能使用dynamic。有谁知道我怎么能这样工作?

2 个答案:

答案 0 :(得分:4)

多么有趣。当我们收到oGizmo中的doSomethingWith对象时,它的类型为Windows Runtime Object。这种行为在JavaScript和VBScript之间是一致的。

现在,如果我们在MarshalAs(UnmanagedType.IUnknown)方法的返回值上明确指定 GiveMeAGizmo() 一切正常,则可以投射对象回到Gizmo内的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();
    }
}

但是,如果我们指定UnmanagedType.IDispatchUnmanagedType.Struct(将对象编组为COM VARIANT的默认值),则问题又回来了。

因此,到目前为止,有一种解决方法,但对此类COM互操作行为没有合理的解释。

[更新] 下面还有一些实验。请注意获取gizmo1成功的方式,而gizmo2不是:

<强> 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(); }

这只是猜测,但我认为这种行为可能与WebBrowser.ObjectForScripting强加的.NET安全权限集有关。

答案 1 :(得分:0)

你需要做两件事

  1. 找出here所描述的对象类型。

  2. 使用Marshal.GetObjectForIUnknown提取实际对象(读到最后,有一个接口要实现)。