【问题标题】:VBA array with lower boundary > 0 passed to .NET下边界 > 0 的 VBA 数组传递给 .NET
【发布时间】:2020-04-19 13:12:30
【问题描述】:

我有一个带有 C# 方法的 .NET Dll,我在 Excel VBA 中使用它。它是通过 VBA 的 COM 包装器调用的。该方法将两个双精度数组作为参数。

当双数组在 VBA 中定义为下边界为 1,并通过 COM 传递给 C# 方法时,Excel 失败并出现以下错误:

运行时错误 5 (无效的过程调用或参数)

虽然这听起来很疯狂,但我希望能够将下界为 1 的 VBA/COM 数组传递给 C# .NET。这可能吗?

这是 C# 函数的声明方式:

public double LinearInterpolation(
                                double[] psaXvector,
                                double[] psaYvector,
                                double Xvalue)
{

C# 中的 COM 包装器

 [Guid("CE93D637-0673-4EBC-8FFA-6CE162959262")]
 [ComVisible(true)]
 [InterfaceType(ComInterfaceType.InterfaceIsDual)]
 public interface IMixedTools
 {
   [DispId(1)]
   [return: MarshalAs(UnmanagedType.R8)] double 
   LinearInterpolation(
     [MarshalAs(UnmanagedType.SafeArray)]double[] psaXvector, 
     [MarshalAs(UnmanagedType.SafeArray)]double[] psaYvector, 
     [MarshalAs(UnmanagedType.R8)]double Xvalue);

这是生成的 .tlh 文件的样子

struct __declspec(uuid("ce93d637-0673-4ebc-8ffa-6ce162959262"))
IMixedTools : IDispatch
{
  virtual HRESULT __stdcall LinearInterpolation (
    /*[in]*/ SAFEARRAY * psaXvector,
    /*[in]*/ SAFEARRAY * psaYvector,
    /*[in]*/ double Xvalue,
    /*[out,retval]*/ double * pRetVal ) = 0;

调用 VBA 代码

  Dim mixu As Object
  Set mixu = CreateObject("TlibCOM.CMixedTools")
  Dim Xarr() As Double
  Dim Yarr() As Double
  ReDim Xarr(1 To 4) As Double
  ReDim Yarr(1 To 4) As Double
  Dim x As Double

  Xarr(1) = 1
  Xarr(2) = 2
  Xarr(3) = 5
  Xarr(4) = 7.5

  Yarr(1) = 14
  Yarr(2) = 9
  Yarr(3) = 4
  Yarr(4) = -3

  x = 4
  result = mixu.LinearInterpolation(Xarr, Yarr, x)

  Set mixu = Nothing

如果我定义下边界为 0 的数组,该函数将起作用。但是我有一个相当大的遗留代码库,其中数组可以用 0 或 1 作为数组的下边界来定义,因此以数组作为参数的函数需要能够处理这个问题。

C# .NET DLL 是一个方法库,取代了传统的 ATL COM 函数。以前,无论从 VBA 传递的双精度数组的下限是 1 还是 0,这都不是问题。

有没有办法绕过这个限制,以便 VBA 可以将低边界为 1 的数组传递给 DLL 中的 C# 方法?

【问题讨论】:

  • “以前”是什么意思? “以前”是什么时候?发生了什么变化?您的意思是当您迁移到 C# 时出现问题?据我所知,C# 一直支持 only 基于 0 的数组。 VBA 支持基于 1 或 0。关于 ATL 我不知道...
  • 这里有一些关于什么有效,什么无效的提示。 stackoverflow.com/questions/56446/net-arrays-with-lower-bound-0 使用 ToArray() 您可以随时快速创建基于 0 的数组副本,如果这已经足够的话。
  • 当您在 VBA 中使用下边界为 1 的数组时,仍然有一个没有数据的索引零。因此,如果您将数组传递给 c#,则不会发生任何变化。 c#数组有一个GetLowerBound属性,就像VBA一样,你确定你在改变之前完全编译过。再次尝试确保它适用于下边界零。
  • 您必须将参数类型更改为Array 并在属性中声明SafeArraySubType。使用 Array 方法访问 C# 代码中的数组(GetLowerBound、GetUpperBound、GetValue)。
  • 是的。您使用 double[] 参数声明了 LinearInterpolation(),它需要是 Array 参数才能让 VBA 代码传递不符合要求的数组。

标签: c# excel vba windows com


【解决方案1】:

将 COM-wrappers 接口中的方法签名更改为使用 Array 而不是 double[] 并定义 SafeArraySubType:

[return: MarshalAs(UnmanagedType.R8)] double 
   LinearInterpolation(
     [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R8 )]Array psaXvector, 
     [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R8 )]Array psaYvector, 
     [MarshalAs(UnmanagedType.R8)]double Xvalue);

这允许将 COM Safearray 传递给从 [n 到 m] 定义的方法,其中 n > 0。

在将数组传递给核心 C# 方法之前,它会在 COM-wrapper 类中从 Array 转换为 double[]

public double LinearInterpolation(Array psaXvector, Array psaYvector, double Xvalue)
{
    double[] xarray = psaXvector.OfType<double>().ToArray();
    double[] yarray = psaYvector.OfType<double>().ToArray();
    return _mix.LinearInterpolation(xarray, yarray, Xvalue);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-07
    • 1970-01-01
    • 2014-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-05
    • 1970-01-01
    相关资源
    最近更新 更多