I've got a dll that is used by vba code in an Excel worksheet. Things work fine - except there's one function that needs a SAFEARRAY.
Abbreviated Excel VBA Code looks like this:
Private sub ExcelFoo()
Dim v(115,17)
...stuff ...
MyFunctionCollectionLib.SetMatrix v
This calls a Dll, written in c++. The c++ function looks like this:
void _stdcall SetMatrix(SAFEARRAY **psaMatrix)
... store matrix somewhere ...
It all works fine in Excel.
Now I want to use the same function in C#. The object browser shows the following information about the function (in the dll):
Sub SetMatrix(psaMatrix() As Double)
So I wrote a Dll wrapper as follows:
// void _stdcall SetMatrix(SAFEARRAY **psaMatrix)
[DllImport( "MyFunctionCollectionLib.dll", CallingConvention = CallingConvention.StdCall )]
public extern static void SetMatrix(ref double[,] psaMatrix);
An then in C# I attempt to use it as follows:
public void ReadDefaultValues()
{
double[,] tMatrix = _TestData.ReadDefaultValues();
DllWrapper.DllWrapper.SetMatrix(ref tMatrix);
The debugger shows me that tMatrix is originally a double[115, 17] - as defined, but gets it gets changed to a double[1] after the SetMatrix() call.
There are a few other oddities which make me doubt I'm doing this correctly. I really don't know anything about Excel/vba and all that. So I'm getting caught by lots of "gotchas" since I'm really out of my field.
Is there documentation about SAFEARRAY that is easilly available? (It doesn't have to be easilly readable) My google searches bring up lots of somewhat obscure pages about COM and marshalling. These pages don't appear to directly address what I'm looking for. The Microsoft documentation describes the SAFEARRAY struct, but doesn't appear to offer any elaboration on usage.
=======================================================================
If I understand the suggested documentation, what I need to do is change my wrapper:
// void _stdcall SetMatrix(SAFEARRAY **psaMatrix)
[DllImport( "MyFunctionCollectionLib.dll", CallingConvention = CallingConvention.StdCall )]
[In][Out][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] double[,] pArrayOfDouble
For the wrapper there is a VT_DECIMAL, but no VT_DOUBLE type - so I assume I should use VT_VARIANT as the type.
The code above fails (copied here):
double[,] tMatrix = _TestData.ReadDefaultValues();
DllWrapper.DllWrapper.SetMatrix(ref tMatrix);
when I try with var (is this the c# variant?) it also fails
var tMatrix = _TestData.ReadDefaultValues();
DllWrapper.DllWrapper.SetMatrix(ref tMatrix);
The error message in both cases is: specified array was not the expected type.
Where do I find the expected type? The stack trace Shows:
at MyFunctionCOllection.MyFunctionCOllectionWrapper.SetMatrix(Double[,] pArrayOfDouble)