【问题标题】:How does on marshal class parameters to COM methods?如何将类参数编组到 COM 方法?
【发布时间】:2014-04-30 17:24:03
【问题描述】:

我在 VBSript 中定义了一个类:

Class Foo
  Public i
End Class

我想将一个实例传递给用 C# 编写的 COM 对象:

Dim oCOMTEST : Set oCOMTEST = CreateObject("COMTests.Class1")

Dim oFoo : Set oFoo = new Foo
oFoo.i = 42
Call oCOMTEST.M(oFoo)

方法M定义为:

public void M(ref object foo)

目前该方法需要object,但我宁愿使用该方法 采用 C# 等效类型 Foo。其中,我尝试过更改objectFoo,与

[StructLayout(LayoutKind.Sequential)]
public class Foo
{
  [MarshalAs(UnmanagedType.U4)]
  public int i;
}

但没有成功。因此我的问题是:

VBScript 类型 Foo 的封送 C# 版本的外观如何?

--

这是我的最小测试用例。

using System.Runtime.InteropServices;

namespace COMTests
{
  [ComVisible(true)]
  [Guid("4624071F-2D98-4ECC-8898-558F4D24EEC9")]
  [InterfaceType(ComInterfaceType.InterfaceIsDual)]
  public interface IClass1
  {
    void M(ref object foo);
  }

  [ComVisible(true)]
  [Guid("9F2B6958-742F-4E5D-A5F3-D6BDC6C841DB")]
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("COMTests.Class1")]
  public class Class1 : IClass1
  {
    public void M(ref object foo)
    {
      System.Diagnostics.Debugger.Launch();
    }
  }
}

还有下面的VBScript:

Dim oCOMTEST : Set oCOMTEST = CreateObject("COMTests.Class1")

Dim oFoo : Set oFoo = new Foo
oFoo.i = 42
Call oCOMTEST.M(oFoo)

Class Foo
  Public i
End Class

【问题讨论】:

  • 好问题。在 VB6 中,除了在幕后为您创建 CLSID 之外,VB 还自动为您创建的每个类创建一个接口。您的Foo 类将获得一个自动的_Foo 接口和一个关联的IID__Foo 标识符(注意两个下划线)。我会假设 VBScript 会做类似的事情。
  • 不能,脚本引擎只支持后期绑定。使用 dynamic 关键字是最好的。

标签: c# vbscript com pinvoke


【解决方案1】:

VBScript 类不是一个完整的 COM 对象,因为它没有与之关联的 IID(接口 ID)。所以,我可能是错的,但我不认为你可以让它匹配一个 .NET 类或接口(许多属性,如 ComImport 期望一个关联的 Guid 属性)。如果 Foo 是从“真正的”COM 对象创建的,它就可以工作。在后台,我相信 VBScript 的 Foo 对象只实现了IDispatch,所以它本质上是非常动态的。

但是,您仍然可以通过 c# dynamic 伪类型关键字轻松使用它的属性和方法,例如,以下代码应该可以正常工作:

[ComVisible(true)]
[Guid("9F2B6958-742F-4E5D-A5F3-D6BDC6C841DB")]
[ProgId("COMTests.Class1")]
public class Class1
{
    public void M(dynamic foo)
    {
        Console.WriteLine("foo:" + foo.i);
    }
}

PS:在这种情况下,您不需要 interface 或 ref 属性。使用我上面的代码,这将由 .NET 自动创建(在 IDL 术语中,为了便于阅读,稍微简化了一点):

[
  uuid(E66DC08B-A63A-41A8-B63D-15ED6F4569AB),
  version(1.0),
]
library ClassLibrary1
{
    interface _Class1;

    [
      uuid(9F2B6958-742F-4E5D-A5F3-D6BDC6C841DB),
      version(1.0),
    ]
    coclass Class1 {
        [default] interface _Class1;
        interface _Object;
    };

    [
      odl,
      uuid(F56AF0FC-D93B-399E-8FBD-9B72CF50D7D9),
      hidden,
      dual,
      oleautomation,
    ]
    interface _Class1 : IDispatch {
    };
};

【讨论】:

  • 你确定不需要` [ClassInterface(ClassInterfaceType.None)]. When I open the generated tlb` 添加/删除ClassInterface(...) 显示不同的结果。
  • 是的,它是不同的,但在这种情况下,你只需要我的代码中的内容(你不需要ClassInterfaceAttribute,你不需要IClass1 类,你不需要' t 需要作为 ref 传递的参数)。这只是一个备注,如果你喜欢,你可以让代码保持最初的样子。
  • 你能解释一下,为什么不需要那些东西。我想了解。
  • 因为没有它它也能正常工作,因为 .NET 会自动创建 IDispatch 接口并且 VBScript 可以调用 IDispatch 接口。查看我的更新。
猜你喜欢
  • 1970-01-01
  • 2018-10-26
  • 1970-01-01
  • 2012-02-25
  • 2011-09-18
  • 1970-01-01
  • 2016-06-26
  • 2014-01-20
  • 2016-01-04
相关资源
最近更新 更多