Search code examples
c++vbamatrixdllsafearray

Passing a SAFEARRAY variant to vba from a c++ dll


I have in a DLL the following code, which is supposed to take some input from a routine written in VBA, do some stuff with it, and return two 2D-arrays back (so a function is not really doable, besides, if I wanted to return an array containing the other two, I'd have to face code-optimization problems in VBA), which can be accessed from VBA. Without further ado, the code looks like this:

void _stdcall MySub(VARIANT RealCo, VARIANT ImagCo, VARIANT RealCr, VARIANT ImagCr, ULONG PointsTheta, ULONG PointsPhi, double Res, VARIANT CoPol, VARIANT CrPol)
{

/* Do my stuff here, and at the end produces the following arrays */

CoPol.vt = VT_ARRAY | VT_VARIANT;
CrPol.vt = VT_ARRAY | VT_VARIANT;
sab2[1].lLbound = 0; sab2[1].cElements = 360;
sab2[0].lLbound = 0; sab2[0].cElements = 360;
CoPol.parray = SafeArrayCreate(VT_VARIANT, 2, sab2);
CrPol.parray = SafeArrayCreate(VT_VARIANT, 2, sab2);

/* Those arrays are then stuffed with data */

}

Which I call from VBA as follows:

Private Declare Sub MySub Lib "Mypath/MyFile.dll" ( _
        ByVal RealCo As Variant, _
        ByVal ImagCo As Variant, _
        ByVal RealCr As Variant, _
        ByVal ImagCr As Variant, _
        ByVal PointsTheta As Long, _
        ByVal PointsPhi As Long, _
        ByVal Res As Double, _
        ByVal CoPol As Variant, _
        ByVal CrPol As Variant)

Dim CoPol As Variant, CrPol As Variant

' [...]

MySub CVar(RealCo), CVar(ImagCo), CVar(RealCr), CVar(ImagCr), PointsTheta, PointsPhi, Resolution, CoPol, CrPol

CVaris because the input arrays RealCo, RealCr, ImagCo, ImagCr are actually Single type in VBA.

It works, but my problem is the following: as I move from the last } in C++ to VBA, the content of CoPol and CrPol (which are the two matrices I would like to use further in the VBA code) disappears.

Notice that I can actually debug the code and check that CoPol and CrPol do contain data. I tried to pass them to another subroutine in C++, and since it didn't work as well, I came out with the solution of passing them via reference:

void _stdcall MySub(VARIANT RealCo, VARIANT ImagCo, VARIANT RealCr, VARIANT ImagCr, ULONG PointsTheta, ULONG PointsPhi, double Res, VARIANT &CoPol, VARIANT &CrPol)

This solved my problem inside C++ but not between C++ and VBA. I also tried giving back pointers to the VARIANTS, and calling the needed arrays ByRef and As Long, but it doesn't work (CoPol and CrPol are then in VBA just a Long number, which I can't work with).

What should I write in order to make VBA able to access the values inside the matrix built in C++?

EDIT: if you want to downvote, at least explain why.


Solution

  • How do you free memory afterwards if its allocated by a single function call in C++?

    I would have done it the other way; declared and dim'ed the arrays in VBA and then passed pointer to array data to your C++ function, which then fills it up with values.

    (Btw, I think its __stdcall with two underscores.)

    void __stdcall fillarray(const int n, double *out) {
        for (int i = 0; i < n; ++i) {
            out[i] = i;
        }
    }
    

    And then

    Public Declare PtrSafe Sub fillarray Lib "Mypath/MyFile.dll" (ByVal n As Long, ByRef out As Double)
    
    Dim A() as Double
    Redim A(1 to 100)
    fillarray 100, A(1)   'A(1) first element pointing to data