【问题标题】:Passing a C# class instance back to managed code from JavaScript via COM通过 COM 将 C# 类实例从 JavaScript 传递回托管代码
【发布时间】:2014-01-14 17:38:08
【问题描述】:

我的问题的基本轮廓如下面的代码所示。我在表单中托管了一个 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 并调用InvokeMember,如explained here。成员“姓名”是null

我需要它与低于 4.0 的 .NET 框架版本一起使用,所以我不能使用 dynamic。有人知道我怎样才能让它工作吗?

【问题讨论】:

  • oGizmo 在调试器中是什么样子的?还是总是为空?
  • @Abhinav:它的类型是System.__ComObject。如果我尝试oGizmo.GetType().GetMembers(),它会返回nullGetProperties()GetMethods() 相同。
  • 你能显示“GiveMeAGizmo()”代码吗?

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


【解决方案1】:

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

现在,如果我们在 GiveMeAGizmo() 方法的返回值上显式指定 MarshalAs(UnmanagedType.IUnknown)一切正常,对象可以被强制转换回 @987654327 @内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 互操作行为做出合理解释。

[UPDATE] 下面还有一些实验。注意获取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 安全权限集有关。

【讨论】:

  • 您在这里付出了额外的努力,给我留下了深刻的印象。据我所知,您的答案非常有效,所以绿色勾号是您的。
  • @AndyE,没问题。 WebBrowser 周围的一切都是我特别感兴趣的领域:)
  • 啊,我会记住下一个问题的 ;-)。我正在从事的项目采用了许多先进的技术来破解 WebBrowser 控件,但是我们已经在一些复杂的东西上卡住了几次(老实说,我不是 .NET 专家! )。
【解决方案2】:

你需要做两件事

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

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

【讨论】:

  • 你真的试过了吗?它在 OP 的情况下不起作用,因为他的 Gizmo 类型的原始对象已经是 managed 对象。不知何故,当从 JavaScript 传回时,它被包装为“Windows 运行时对象”。出于同样的原因,Gizmo g = (Gizmo)Marshal.CreateWrapperOfType(oGizmo, typeof(Gizmo)) 也不起作用。
猜你喜欢
  • 2011-07-31
  • 2011-03-08
  • 2013-02-19
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多