【问题标题】:Inconsistency in passing objects from VBA to .NET via COM通过 COM 将对象从 VBA 传递到 .NET 的不一致
【发布时间】:2010-03-30 19:30:07
【问题描述】:

我定义了以下接口来向 COM 公开 .NET 类:

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("6A983BCC-5C83-4b30-8400-690763939659")]
[ComVisible(true)]
public interface IComClass
{
    object Value { get; set; }

    object GetValue();

    void SetValue(object value);
}

这个接口的实现很简单:

[ClassInterface(ClassInterfaceType.None)]
[Guid("66D0490F-718A-4722-8425-606A6C999B82")]
[ComVisible(true)]
public class ComClass : IComClass
{
    private object _value = 123.456;

    public object Value
    {
        get
        {
            return this._value;
        }

        set
        {
            this._value = value;
        }
    }

    public object GetValue()
    {
        return this._value;
    }

    public void SetValue(object value)
    {
        this._value = value;
    }
}

然后我使用 RegAsm 注册了它,并尝试通过以下代码从 Excel 中调用它:

Public Sub ComInterop()

    Dim cc As ComClass
    Set cc = New ComClass

    cc.SetValue (555.555)

    valueByGetter = cc.GetValue 
    valueByProperty = cc.Value 

    cc.Value = 555.555 

End Sub

当我单步执行此代码时,valueByGetter = 555.5555 和 valueByProperty = 555.555 符合预期。但是,我在最后一行收到“需要对象”运行时错误。

为什么通过setter方法设置值有效,但通过属性设置失败?我必须进行哪些更改才能使该属性按预期工作?

编辑:我收到了一些有用的回复,所以我的另一个问题是“这个问题会出现在用其他语言编写的 COM 客户端,还是特定于 VBA?”。

【问题讨论】:

  • 您是否尝试过将调试器附加到 excel 进程并介入并查看它到底在哪里爆炸?
  • 不确定您指的是哪个调试器。如果我通过将 excel 作为外部程序启动来通过 VS.Net 调试 .net 代码,我不会到达 Value 属性的 set 部分中的断点,这表明这是一个 excel 问题。如果您的意思是直接调试excel,我不知道从哪里开始-您能给我更多信息吗?谢谢。

标签: .net vba com


【解决方案1】:

你的接口像这样导出到类型库:

dispinterface IComClass {
    properties:
    methods:
        [id(00000000), propget]
        VARIANT Value();
        [id(00000000), propputref]             // <=== problem here
        void Value([in] VARIANT rhs);
        [id(0x60020002)]
        VARIANT GetValue();
        [id(0x60020003)]
        void SetValue([in] VARIANT Value);
};

问题是 Value 属性设置器,它被声明为 propputref 而不是 propput。 COM 中存在一个令人讨厌的一致性问题,这是由它支持默认属性引起的。这就是为什么您必须在 VBA 中使用 Set 关键字的原因。问题是:您没有在 VBA 代码中传递对象,即使 .NET 需要一个。

经过一番研究,我发现没有现成的解决方案可以解决这个问题。 Let关键字在VBA中可能起作用的可能性很小,我没有尝试过。唯一的中途修复是强制后期绑定,而不是使用 ComInterfaceType.InterfaceIsDual 使用 ComInterfaceType.InterfaceIsIDispatch。或者避免使用“对象”。

【讨论】:

  • 感谢您提供的信息 - 虽然不是我想要的答案 :) 那么这个问题只会发生在 VBA 客户端中吗?我一直在通过 VBA 测试我的 COM 接口,但无法控制客户端的编写语言。
  • 真的必须支持变体吗?你真的必须支持早期绑定吗?您的问题中没有足够的信息来拨打电话。
  • 我觉得早期绑定 + 智能感知让我的 API 的使用者更容易(因此更少的支持调用!)。至于支持变体(对象),考虑到我需要 Value 接受的类型,我可以指定 Value 是一个字符串并适当地解释它,但这对于 .NET 客户端来说似乎很奇怪(可能并不比它是对​​象更陌生...... )。无论如何,您认为最初的问题是特定于 VBA 还是更一般的 COM 问题?
【解决方案2】:

这似乎是tlbexp 和 MS 建议使用这样的后期绑定调用的问题:

Dim cc As Object ' this one changed from ComClass
Set cc = New ComClass
...
cc.Value = 555.555

您可以将其提前绑定并尝试更简单的修复:

Set cc.Value = CVar(555.555)

【讨论】:

  • 你后期绑定的建议有效,但 CVar 方法给出了与以前相同的错误(我尝试过使用和不使用 Set 关键字)。
  • 你知道我是否会遇到用不同语言编写的客户的相同问题吗?即这是 COM 问题还是 VBA 问题?
  • 这是 .Net COM 互操作问题。您确定CVar 不会更改错误消息吗?
  • 我再次检查了 CVar 方法,它肯定失败了 :( 鉴于这是一个 .NET COM 互操作问题并且其他客户端可能没有后期绑定选项,听起来我需要要么用 getter/setter 替换属性,否则更改属性类型。
  • 这个答案和来自 nobugz 的答案都非常有帮助。他的回答为问题的原因提供了更多背景信息,因此我将他设置为可接受的答案 - 希望没问题。
【解决方案3】:

你需要在最后一行使用 Set 关键字:

Set cc.Value = 555.555

【讨论】:

  • 当我尝试它失败并出现编译错误:需要对象。
【解决方案4】:

你有没有尝试过类似的东西(vb有点生疏,你明白了):

Dim newValue as Int
int= 555.555
cc.Value = newValue (or maybe set cc.Value=newValue)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-05
    • 1970-01-01
    • 2014-10-08
    • 2011-06-28
    • 2012-05-15
    • 2019-02-04
    • 2023-03-31
    • 1970-01-01
    相关资源
    最近更新 更多