【问题标题】:Return array of interface from a .NET method via COM4J通过 COM4J 从 .NET 方法返回接口数组
【发布时间】:2012-09-24 12:56:51
【问题描述】:

如何通过 COM4J 从 C# 方法将对象数组(实现 COM 接口)返回到 Java 方法?

生成数组的示例 C# 类:

using System;
using System.Runtime.InteropServices;

namespace Example
{

    [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IAnimal
    {
        string Speak();
    }

    [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IFarm
    {
        [return:MarshalAs(UnmanagedType.SafeArray,
        SafeArraySubType=VarEnum.VT_UNKNOWN)]
        IAnimal[] GetAnimals();
    }

    [ComVisible(true), ClassInterface(ClassInterfaceType.None)]
    public class Farm : IFarm
    {
        public IAnimal[] GetAnimals()
        {
            return new IAnimal[] { new Cow(), new Pig() };
        }
    }

    internal class Cow: IAnimal
    {
        public string Speak()
        {
            return "Moo";
        }
    }

    internal class Pig: IAnimal
    {
        public string Speak()
        {
            return "Oink";
        }
    }
}

生成的 .tlb 中的接口声明如下所示:

[
  odl,
  uuid(1FB5E376-E78D-3A2E-BEF3-F3C798FCF44C),
  version(1.0),
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "Example.IFarm")
]
interface IFarm : IUnknown
{
    HRESULT _stdcall GetAnimals([out, retval] SAFEARRAY(IUnknown*)* pRetVal);
};

Java 客户端代码:

import com4j.*;

public class Example {
    public static void main(String[] args) {
        IFarm farm = ClassFactory.createFarm();
        Com4jObject[] animals = farm.getAnimals();

        for (Com4jObject o: animals) {
            IAnimal animal = o.queryInterface(IAnimal.class);

            if (animal != null) {
                animal.speak();
            }
        }
    }
}

这可以编译,但我在运行时收到此异常:

Exception in thread "main" com4j.ComException: 
    unexpected conversion type: 500 : .\invoke.cpp:470
        at com4j.Wrapper.invoke(Wrapper.java:185)
        at $Proxy5.getAnimals(Unknown Source)
        at MainClass.main(MainClass.java:7)
Caused by: com4j.ComException: unexpected conversion type: 500 : .\invoke.cpp:470
        at com4j.Native.invoke(Native Method)
        at com4j.StandardComMethod.invoke(StandardComMethod.java:35)
        at com4j.Wrapper$InvocationThunk.call(Wrapper.java:354)
        at com4j.Task.invoke(Task.java:55)
        at com4j.ComThread.run0(ComThread.java:157)
        at com4j.ComThread.run(ComThread.java:137)

我尝试过的其他事情:

  • 编组为SAFEARRAY(VARIANT)* 而不是SAFEARRAY(IUnknown*)*
    (这会引发相同的异常。)
  • 删除MarshalAs属性(tlbimp创建代理方法失败)

有没有办法编组数组以便 COM4J 可以将其转换为有效的 Java 数组?

另外有没有办法在 Java 中分配一个数组并允许 .NET 方法填充它? (我试过了,但是 .NET 方法接收到数组的副本,Java 代码永远不会看到插入到副本中的对象。也许有办法覆盖它?)


编辑:这可能是相关的:https://stackoverflow.com/a/6340144/12048 - VBScript 中似乎有类似的东西

【问题讨论】:

  • 愚蠢的问题,但它是 GetAnimal 还是 getAnimal。该错误表示它无法解析 getAnimal。
  • @zam664 两者都是正确的; COM4J 在生成 Java 类时去掉了方法名的初始大写。
  • Pig 和 Cow 既不是 ComVisible,我也没有看到任何适用于它们的 Java 类。难道这是缺少的东西?
  • @JanHruby:他们不应该是ComVisible,他们不应该是。它们实现了ComVisible 接口并由ComVisible 方法返回,这足以使它们可访问。正如我在对答案的评论中所说,如果我通过集合而不是数组导出相同的对象,它会起作用。

标签: c# java com com-interop com4j


【解决方案1】:

您是否尝试过将接口声明为 InterfaceIsDual 和/或 InterfaceIsIDispatch?

[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IAnimal
{
...

大多数 COM 互操作库都需要调度接口;但是,我不确定 COM4J。您也可以使用抽象的 COM 基类而不是接口来实现相同的目标,但如果上述更改不能让您启动并运行,我会感到惊讶。

【讨论】:

  • COM4J 使用 IUnknown 接口 - 不需要 Dispatch。我的问题仅在于数组(导出集合工作正常。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-05
  • 2018-02-23
  • 1970-01-01
  • 2014-08-07
  • 1970-01-01
相关资源
最近更新 更多