如何在c#中使用SAFEARRAY为Excel vba编写的c ++ dll函数

时间:2016-11-19 19:32:04

标签: c# c++

我在Excel工作表中有一个vba代码使用的dll。事情很好 - 除了有一个需要SAFEARRAY的功能。

缩写的Excel VBA代码如下所示:

Private sub ExcelFoo()
    Dim v(115,17)
    ...stuff ...
    MyFunctionCollectionLib.SetMatrix v

这用d ++编写,用c ++编写。 c ++函数如下所示:

void _stdcall SetMatrix(SAFEARRAY **psaMatrix)
   ... store matrix somewhere ...

在Excel中一切正常。

现在我想在C#中使用相同的功能。 对象浏览器显示有关该函数的以下信息(在dll中):

Sub SetMatrix(psaMatrix() As Double)

所以我写了一个Dll包装器如下:

// void _stdcall SetMatrix(SAFEARRAY **psaMatrix)
[DllImport( "MyFunctionCollectionLib.dll", CallingConvention = CallingConvention.StdCall )]
public extern static void SetMatrix(ref double[,] psaMatrix);

然后在C#中我尝试按如下方式使用它:

public void ReadDefaultValues()
{
    double[,] tMatrix = _TestData.ReadDefaultValues();
    DllWrapper.DllWrapper.SetMatrix(ref tMatrix);

调试器向我显示tMatrix最初是双[115,17] - 如定义的那样,但在SetMatrix()调用之后它会变为double [1]。

还有一些其他奇怪的事情让我怀疑我是否正确地这样做了。我真的不了解Excel / vba以及所有这些。所以我被许多"陷阱"因为我真的离开了我的领域。

是否有关于SAFEARRAY的文档易于使用? (它不是必须易于阅读)我的谷歌搜索带来了许多关于COM和编组的有些模糊的页面。 这些页面似乎不能直接解决我正在寻找的问题。 Microsoft文档描述了SAFEARRAY结构,但似乎没有提供任何有关使用的详细说明。

=============================================== ========================

如果我理解建议的文档,我需要做的是更改我的包装器:

// void _stdcall SetMatrix(SAFEARRAY **psaMatrix)
[DllImport( "MyFunctionCollectionLib.dll", CallingConvention = CallingConvention.StdCall )]
[In][Out][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] double[,] pArrayOfDouble

对于包装器,有一个VT_DECIMAL,但没有VT_DOUBLE类型 - 所以我假设我应该使用VT_VARIANT作为类型。

上面的代码失败(在这里复制):

    double[,] tMatrix = _TestData.ReadDefaultValues();
    DllWrapper.DllWrapper.SetMatrix(ref tMatrix);

当我尝试使用var(这是c#变体吗?)时它也失败了

    var tMatrix = _TestData.ReadDefaultValues();
    DllWrapper.DllWrapper.SetMatrix(ref tMatrix);

两种情况下的错误消息是:指定的数组不是预期的类型。

我在哪里可以找到预期的类型?堆栈跟踪显示:

   at MyFunctionCOllection.MyFunctionCOllectionWrapper.SetMatrix(Double[,] pArrayOfDouble)

1 个答案:

答案 0 :(得分:0)

好吧,显然它想要重新定义c#“包装器”或者它所谓的:

[DllImport( "MyFunctionCollectionLib.dll", CallingConvention = CallingConvention.StdCall )]
[In][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R8)] double[,] pArrayOfDouble  

然后我可以在没有参考的情况下对其进行编码:

var tMatrix = _TestData.ReadDefaultValues();
DllWrapper.DllWrapper.SetMatrix(tMatrix);

我没有看到VT_R8是定义双打。现在至少停止抱怨数据类型等等。