【问题标题】:C# property exposed to VBA (COM) : Run-time error '424': Object required暴露给 VBA (COM) 的 C# 属性:运行时错误“424”:需要对象
【发布时间】:2012-11-22 20:39:22
【问题描述】:

此 C# 代码位于 .NET 4.5 ComVisible 程序集中:

C#代码

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("22341123-9264-12AB-C1A4-B4F112014C31")]
public interface IComExposed
{
    double[] DoubleArray { get; set; }
    object[] ObjectArray { get; set; }
    object PlainObject { get; set; }
    double ScalarDouble { get; set; }
}

[ClassInterface(ClassInterfaceType.None)]
[Guid("E4F27EA4-1932-2186-1234-111CF2722C42")]
[ProgId("ComExposed")]
public class ComExposed : IComExposed
{
    public double[] DoubleArray { get; set; }
    public object[] ObjectArray { get; set; }
    public object PlainObject { get; set; }
    public double ScalarDouble { get; set; }
}

从 Excel 2010 32 位 VBA,我有以下行为:

VBA 代码

Dim VBArray(1 To 3) As Double
VBArray(1) = 1
VBArray(2) = 2
VBArray(3) = 3

Dim oComExposedEarlyBinding As New ComExposed

' Works
oComExposedEarlyBinding.ScalarDouble = 5

' Compile Error: Function or interface marked as restricted,
' or the function uses an Automation type not supported in Visual Basic
oComExposedEarlyBinding.DoubleArray = VBArray

' Compile Error: Function or interface marked as restricted,
' or the function uses an Automation type not supported in Visual Basic
oComExposedEarlyBinding.ObjectArray = VBArray

' Run-time error '424': Object required
oComExposedEarlyBinding.PlainObject = VBArray

' Run-time error '424': Object required
oComExposedEarlyBinding.PlainObject = 5

Dim oComExposedLateBinding As Variant
Set oComExposedLateBinding = New ComExposed

' Works
oComExposedLateBinding.ScalarDouble = 5

' Run-time error '5': Invalid procedure call or argument
oComExposedLateBinding.DoubleArray = VBArray

' Run-time error '13':  Type mismatch
oComExposedLateBinding.ObjectArray = VBArray

' Works
oComExposedLateBinding.PlainObject = VBArray

' Works
oComExposedLateBinding.PlainObject = 5

正如您所注意到的,PlainObject 正在后期绑定模式下工作,但显然以丢失输入为代价,因此在 VBA 中丢失了自动完成 (IntelliSense),这在我的场景中是不可接受的。

我在示例中关注的行是以下行:

oComExposedEarlyBinding.DoubleArray = VBArray
oComExposedEarlyBinding.ObjectArray = VBArray
oComExposedEarlyBinding.PlainObject = VBArray

让上述三行中的任何一行都可以满足我的需求,那么您是否有任何解决方法或解决方案可以使这项工作(请注意,我对将数组作为参数传递给函数不感兴趣)?

更新: 将此问题提交给 Microsoft 的支持并等待近三周后。他们确认这是一个错误,这是 KB:http://support.microsoft.com/kb/327084,C# 中唯一的解决方法是下面标记的解决方案。 但是,如果用 C++/CLI 编写,我可以确认此代码可以按预期工作。

【问题讨论】:

  • VBA 数组的下限必须为 0 才能与 C# 数组兼容。
  • 我一直在将数组作为函数参数从 COM 传递到 .NET,然后在 .NET 中将它们转换为从零开始。你是说上面的代码因为这个问题而失败了吗?

标签: c# .net vba com interop


【解决方案1】:

VBA 数组必须从零开始,在 c# 中使用 ref 参数,示例:

Option Explicit

Sub test()
    Dim VBArray(0 To 2) As Double
    VBArray(0) = 1
    VBArray(1) = 2
    VBArray(2) = 3

    Dim oComExposedEarlyBinding As New ComExposed
    oComExposedEarlyBinding.SetDoubleArray VBArray

End Sub

using System.Runtime.InteropServices;

namespace COMVisibleTest
{
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("22341123-9264-12AB-C1A4-B4F112014C31")]
    public interface IComExposed
    {
        void SetDoubleArray(ref double[] doubleArray);
    }

    [ClassInterface(ClassInterfaceType.None)]
    [Guid("E4F27EA4-1932-2186-1234-111CF2722C42")]
    [ProgId("ComExposed")]
    public class ComExposed : IComExposed
    {
        private double[] _doubleArray;

        public void SetDoubleArray(ref double[] doubleArray)
        {
            _doubleArray = doubleArray;
        }
    }
}

【讨论】:

  • 这已经在我删除的答案中说过,因为 OP 不喜欢这个解决方案。
  • 谢谢丹尼尔。我将它作为参数传递给函数没有问题。但我确实专门询问了属性,因此最后是标题和注释。
  • 好吧,亚当,我没有注意到你要求财产......即使你在问题的标题中写了它:-)......对不起!这是我知道如何将数据从 C# 传递到 VBA 的唯一方法。祝你好运!
  • 虽然这不能解决我的问题,但经过调查,我找不到解决方案。因此,我将其标记为正确答案。
【解决方案2】:

VBA 总是通过引用 (VT_VARIANT | VT_BYREF) 传递包含在变体中的数组,其中包含实际数组的另一个变体,因此在指定元素类型时不能在属性中使用数组,您需要使用一种方法,以便您可以将参数指定为“通过引用”。

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] 
[Guid("22341123-9264-12AB-C1A4-B4F112014C31")] 
public interface IComExposed
{ 
     void setDoubleArray(ref double[] myArray); 
     //(...) 
} 

类似的问题:
Pass an array from vba to c# using com-interop

该问题的答案提到了使用用户定义的集合而不是原始类型数组的选项,也许这也可以解决您的问题。

文档中的相关参考资料:

Marshaling ByRef Variants

VARIANT and VARIANTARG in WinAPI

MarshalAsAttribute class in .Net

Passing Arrays to COM in .Net

【讨论】:

  • 用户定义的集合对我没有帮助,因为此示例中的数组表示 Range.Value 是本机数组。虽然您给出的解决方案是一种解决方法,但我认为没有其他解决方案。
猜你喜欢
  • 2013-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-01
  • 1970-01-01
相关资源
最近更新 更多