I'm writing a C++ DLL to be accessed from Excel VBA (it is only completing mathematical equations and doesn't need to be accessed from the worksheet). The DLL is intended to replace current VBA code and I want to write it in C/C++ for performance.
Since I'm am using existing code and simply want to replace the current VBA function with the new DLL I have to use VBA Variants containing single vector arrays. The following is an abbreviated example of the VBA calling code:
Sub VBACaller()
Dim InputR0(1 To 5) As Variant, vResult As Variant
InputR0(1) = 26.8
InputR0(2) = 27.8
InputR0(3) = 28.8
InputR0(4) = 29.8
vResult = ReadArrayVBA(InputR0)
End Sub
I can pass this Variant array to my C++ function either ByVal
or ByRef
as a VARIANT
data type and it appears to be received correctly. My problems occur when I try to read the contents of the array after converting it into a SAFEARRAY
using SafeArrayAccessData
. The SAFEARRAY
conversion seems to work but the contents of the array aren't correct. The following is my C++ code:
VARIANT __stdcall ReadArrayByRef(VARIANT *input0)
{
//Variable to assign value from Array
double d_m = 0;
//Check if this is a variant type or not
if (V_VT(input0) & VT_ARRAY)
{
SAFEARRAY* pSafeArrayInput0 = NULL;
pSafeArrayInput0 = V_ARRAY(input0);
double* pVals;
HRESULT hr = SafeArrayAccessData(pSafeArrayInput0, (void **)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lLBound = -1, lUBound = 1; // get array bounds
SafeArrayGetLBound(pSafeArrayInput0, 1, &lLBound);
SafeArrayGetUBound(pSafeArrayInput0, 1, &lUBound);
if (lLBound > -1 && lUBound > -1)
{
d_m = pVals[1];
}
SafeArrayUnaccessData(pSafeArrayInput0);
}
}
//Output
VARIANT v;
VariantInit(&v);
v.vt = VT_R8;
v.dblVal = d_m;
return v;
}
The examples I've found seem to indicate that the .parray
should hold the data, however inspection of input0
indicates that it is in .pparray
. If I try to assign pSafeArrayInput0 = input0.pparray
it gives an error. The value d_m
returns is along the lines of 1.05319234616515E-307
.
If I change the input to ByVal
then I can access the elements of the array correctly using the following C++ code (the only difference being the address of input0 is being accessed by the SAFEARRAY
.
VARIANT __stdcall ReadArrayByVal(VARIANT input0)
{
//Variable to assign value from Array
double d_m = 0;
//Check if this is a variant type or not
if (V_VT(&input0) & VT_ARRAY)
{
SAFEARRAY* pSafeArrayInput0 = NULL;
pSafeArrayInput0 = V_ARRAY(&input0);
double* pVals;
HRESULT hr = SafeArrayAccessData(pSafeArrayInput0, (void **)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lLBound = -1, lUBound = 1; // get array bounds
SafeArrayGetLBound(pSafeArrayInput0, 1, &lLBound);
SafeArrayGetUBound(pSafeArrayInput0, 1, &lUBound);
if (lLBound > -1 && lUBound > -1)
{
d_m = pVals[1];
}
SafeArrayUnaccessData(pSafeArrayInput0);
}
}
VARIANT v;
VariantInit(&v);
v.vt = VT_R8;
v.dblVal = d_m;
return v;
}
All the examples I've found indicate that passing the VARIANT
input by a pointer should works per my ReadArrayByRef function (Ie http://support.microsoft.com/kb/167668 which is slightly different but the same at the points I'm after).
What am I doing wrong in my ByRef function?
You should check the VT_BYREF
flag, and if it is set, dereference the pointer to access the array like so:
pSafeArrayInput0 = *V_ARRAYREF(input0);