I created a ATL/COM class, with a method (IDL) :
[id(4)] HRESULT MULT3(double lCoeff, [in, out] VARIANT* pVarInOut);
It takes pVarInOut, checks that it is an array whose coefficients are real numbers, and updates all its coefficients by multiplying them all by lCoeff.
1) When I call this method from VBA for excel in the following fashion :
Dim OneArrayIn As Variant
OneArrayIn = ws.Range("OneArrayIn")
Call Obj.MULT3(2#,OneArrayIn)
ws.Range("OneArrayOut").Offset(0, 0).Value2 = OneArrayOut
everything is alright : OneArrayIn is updated indeed, with the expected values in it.
2) When I call this method from c# in the following fashion :
MyVARIANT_TESTLib.TheATLObject TheATLObj = new MyVARIANT_TESTLib.TheATLObject();
double TheDouble = 2.0;
object[,] TheMatrix = { { -3.0, 3.0, 2.0, 1.0 } };
TheATLObj.MULT(TheDouble, TheMatrix);
there is a problem : TheMatrix doesn't change at all, as if TheMatrix was not passed by "reference" as it expected to be... even it MULT3 is declared as [in, out] in the IDL, with a pointer to VARIANT*.
For sake of completeness I give the code of MULT3 here :
STDMETHODIMP CTheATLObject::MULT3(double lCoeff, /*[in, out]*/ VARIANT* pVarInOut)
{
THEVARIANT pMyVarInOut = THEVARIANT(pVarInOut);
long lNbLines = pMyVarInOut.GETNBLINES();
long lNbColumns = pMyVarInOut.GETNBCOLS();
ATL::CComVariant vTMPInOut;
try
{
for (long i = 0; i < lNbLines; ++i)
{
for (long j = 0; j < lNbColumns; ++j)
{
pMyVarInOut.GET(i, j, vTMPInOut);
if (vTMPInOut.vt != VT_R8)
{
return E_FAIL;
}
else
{
vTMPInOut.dblVal = vTMPInOut.dblVal * lCoeff;
pMyVarInOut.FILL(i, j, vTMPInOut, vTMPInOut.vt);
}
}
}
pMyVarInOut.ATTACH(pVarInOut);
return S_OK;
}
catch (...) // for now
{
}
return S_OK;
}
This is based on the VARIANT wrapper class THEVARIANT, defined as follows in the header file (note that I have updated the FILL method to make it take a reference to a ATL::CComVariant, as this ATL::CComVariant was passed by value before) :
#pragma once
#include <atlcomcli.h>
#include "atlsafe.h"
#include <vector>
class THEVARIANT
{
private:
ATL::CComVariant vTMP;
ATL::CComSafeArray<VARIANT> *pSA;
ATL::CComSafeArrayBound plDIM[2];
long plINDEX[2];
bool bIS_INIT;
long lNBLINES;
long lNBCOLUMNS;
public:
THEVARIANT(void);
THEVARIANT(VARIANT * pVARIANT_IN);
THEVARIANT(double * pdARRAY_IN, long NBLINES, long NBCOLUMNS);
THEVARIANT(std::vector<double>& pdARRAY_IN, long NBLINES, long NBCOLUMNS);
THEVARIANT(std::vector<int>& pdARRAY_IN, long NBLINES, long NBCOLUMNS);
THEVARIANT(const THEVARIANT& MyV_COPY);
~THEVARIANT(void);
void SET_LDIMENSIONS(long NBLINES, long NBCOLUMNS);
void FILL(long lLINE, long lCOL, ATL::CComVariant & VARIANT_IN, VARTYPE VARIANT_TYPE);
void ATTACH(VARIANT * pVARIANT_OUT);
void GET(long lLINE, long lCOL, ATL::CComVariant& VARIANT_OUT);
long GETNBCOLS( void ) const;
long GETNBLINES( void ) const;
};
and implemented as follows in the cpp file :
#include "StdAfx.h"
#include "THEVARIANT.h"
#include <comutil.h>
#include <atlconv.h>
#include <comdef.h>
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__)
#define new DEBUG_NEW
#endif
THEVARIANT::THEVARIANT(void)
{
pSA = NULL;
bIS_INIT = false;
}
THEVARIANT::THEVARIANT(VARIANT * pVARIANT_IN)
{
pSA = NULL;
if ( V_VT(pVARIANT_IN) == VT_DISPATCH )
{
unsigned int uiArgErr;
DISPID dispidValue;
LPOLESTR XName = L"Value";
pVARIANT_IN->pdispVal->GetIDsOfNames(IID_NULL, &XName, 1, LOCALE_SYSTEM_DEFAULT, &dispidValue);
VARIANT * pVARIANT_IN_EXTENSION = new VARIANT();
VariantInit(pVARIANT_IN_EXTENSION);
DISPPARAMS dispparams;
dispparams.cArgs = 0;
dispparams.cNamedArgs = 0;
EXCEPINFO exception;
pVARIANT_IN->pdispVal->Invoke(dispidValue, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, pVARIANT_IN_EXTENSION, &exception, &uiArgErr);
if( V_VT(pVARIANT_IN_EXTENSION) & VT_ARRAY )
{
if(pVARIANT_IN_EXTENSION->parray->cDims == 2)
{
plDIM[0].SetLowerBound(pVARIANT_IN_EXTENSION->parray->rgsabound[0].lLbound);
plDIM[0].SetCount(pVARIANT_IN_EXTENSION->parray->rgsabound[0].cElements);
plDIM[1].SetLowerBound(pVARIANT_IN_EXTENSION->parray->rgsabound[1].lLbound);
plDIM[1].SetCount(pVARIANT_IN_EXTENSION->parray->rgsabound[1].cElements);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
pSA->CopyFrom(pVARIANT_IN_EXTENSION->parray);
lNBLINES = plDIM[1].cElements;
lNBCOLUMNS = plDIM[0].cElements;
bIS_INIT = true;
}
else if(pVARIANT_IN_EXTENSION->parray->cDims == 1)
{
plDIM[0].SetLowerBound(pVARIANT_IN_EXTENSION->parray->rgsabound[0].lLbound);
plDIM[0].SetCount(pVARIANT_IN_EXTENSION->parray->rgsabound[0].cElements);
plDIM[1].SetLowerBound(pVARIANT_IN_EXTENSION->parray->rgsabound[0].lLbound);
plDIM[1].SetCount(1);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
pSA->CopyFrom(pVARIANT_IN_EXTENSION->parray);
lNBLINES = plDIM[1].cElements;
lNBCOLUMNS = plDIM[0].cElements;
bIS_INIT = true;
}
}
else
{
plDIM[0].SetLowerBound(0);
plDIM[0].SetCount(1);
plDIM[1].SetLowerBound(0);
plDIM[1].SetCount(1);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
ATL::CComVariant varArrayWrapper(*pVARIANT_IN_EXTENSION);
plINDEX[0]=0;
plINDEX[1]=0;
HRESULT hr = pSA->MultiDimSetAt(plINDEX,varArrayWrapper);
ATLASSERT(hr == S_OK);
lNBLINES = plDIM[1].cElements;
lNBCOLUMNS = plDIM[0].cElements;
bIS_INIT = true;
}
}
else if( V_VT(pVARIANT_IN) & VT_ARRAY ) // this case
{
if(pVARIANT_IN->parray->cDims == 2)
{
plDIM[0].SetLowerBound(pVARIANT_IN->parray->rgsabound[0].lLbound);
plDIM[0].SetCount(pVARIANT_IN->parray->rgsabound[0].cElements);
plDIM[1].SetLowerBound(pVARIANT_IN->parray->rgsabound[1].lLbound);
plDIM[1].SetCount(pVARIANT_IN->parray->rgsabound[1].cElements);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
pSA->CopyFrom(pVARIANT_IN->parray);
lNBLINES = plDIM[1].cElements;
lNBCOLUMNS = plDIM[0].cElements;
bIS_INIT = true;
}
else if(pVARIANT_IN->parray->cDims == 1)
{
plDIM[0].SetLowerBound(pVARIANT_IN->parray->rgsabound[0].lLbound);
plDIM[0].SetCount(pVARIANT_IN->parray->rgsabound[0].cElements);
plDIM[1].SetLowerBound(pVARIANT_IN->parray->rgsabound[0].lLbound);
plDIM[1].SetCount(1);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
pSA->CopyFrom(pVARIANT_IN->parray);
lNBLINES = plDIM[1].cElements;
lNBCOLUMNS = plDIM[0].cElements;
bIS_INIT = true;
}
}
else
{
plDIM[0].SetLowerBound(0);
plDIM[0].SetCount(1);
plDIM[1].SetLowerBound(0);
plDIM[1].SetCount(1);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
ATL::CComVariant varArrayWrapper(*pVARIANT_IN);
plINDEX[0]=0;
plINDEX[1]=0;
HRESULT hr = pSA->MultiDimSetAt(plINDEX,varArrayWrapper);
ATLASSERT(hr == S_OK);
lNBLINES = plDIM[1].cElements;
lNBCOLUMNS = plDIM[0].cElements;
bIS_INIT = true;
}
}
THEVARIANT::THEVARIANT(double * pdARRAY_IN, long NBLINES, long NBCOLUMNS)
{
plDIM[0].SetLowerBound(0);
plDIM[0].SetCount(NBLINES);
plDIM[1].SetLowerBound(0);
plDIM[1].SetCount(NBCOLUMNS);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
for(int j=0; j<NBCOLUMNS; j++)
{
for(int i=0; i<NBLINES; i++)
{
vTMP = pdARRAY_IN[i+j*NBLINES];
vTMP.vt = VT_R8;
plINDEX[0] = i;
plINDEX[1] = j;
HRESULT hr = pSA->MultiDimSetAt(plINDEX,vTMP);
ATLASSERT(hr == S_OK);
}
}
lNBLINES = NBLINES;
lNBCOLUMNS = NBCOLUMNS;
bIS_INIT = true;
}
THEVARIANT::THEVARIANT(std::vector<double>& pdARRAY_IN, long NBLINES, long NBCOLUMNS)
{
plDIM[0].SetLowerBound(0);
plDIM[0].SetCount(NBLINES);
plDIM[1].SetLowerBound(0);
plDIM[1].SetCount(NBCOLUMNS);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
for(int j=0; j<NBCOLUMNS; j++)
{
for(int i=0; i<NBLINES; i++)
{
vTMP = pdARRAY_IN[i+j*NBLINES];
vTMP.vt = VT_R8;
plINDEX[0] = i;
plINDEX[1] = j;
HRESULT hr = pSA->MultiDimSetAt(plINDEX,vTMP);
ATLASSERT(hr == S_OK);
}
}
lNBLINES = NBLINES;
lNBCOLUMNS = NBCOLUMNS;
bIS_INIT = true;
}
THEVARIANT::THEVARIANT(std::vector<int>& pdARRAY_IN, long NBLINES, long NBCOLUMNS)
{
plDIM[0].SetLowerBound(0);
plDIM[0].SetCount(NBLINES);
plDIM[1].SetLowerBound(0);
plDIM[1].SetCount(NBCOLUMNS);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
for(int j=0; j<NBCOLUMNS; j++)
{
for(int i=0; i<NBLINES; i++)
{
vTMP = pdARRAY_IN[i+j*NBLINES];
vTMP.vt = VT_I4;
plINDEX[0] = i;
plINDEX[1] = j;
HRESULT hr = pSA->MultiDimSetAt(plINDEX,vTMP);
ATLASSERT(hr == S_OK);
}
}
lNBLINES = NBLINES;
lNBCOLUMNS = NBCOLUMNS;
bIS_INIT = true;
}
THEVARIANT::THEVARIANT(const THEVARIANT& MyV_COPY)
{
pSA = NULL;
if( MyV_COPY.pSA != NULL && MyV_COPY.bIS_INIT )
{
plDIM[0].SetLowerBound(0);
plDIM[0].SetCount(MyV_COPY.plDIM[0].GetLowerBound());
plDIM[1].SetLowerBound(0);
plDIM[1].SetCount(MyV_COPY.plDIM[1].GetLowerBound());
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
pSA->CopyFrom(*MyV_COPY.pSA);
lNBLINES = MyV_COPY.lNBLINES;
lNBCOLUMNS = MyV_COPY.lNBCOLUMNS;
bIS_INIT = true;
}
}
THEVARIANT::~THEVARIANT(void)
{
delete pSA;
pSA = NULL;
bIS_INIT = false;
}
void THEVARIANT::SET_LDIMENSIONS(long NBLINES, long NBCOLUMNS)
{
if(!bIS_INIT)
{
plDIM[0].SetLowerBound(0);
plDIM[0].SetCount(NBLINES);
plDIM[1].SetLowerBound(0);
plDIM[1].SetCount(NBCOLUMNS);
pSA = new ATL::CComSafeArray<VARIANT>(plDIM,2);
lNBLINES = NBLINES;
lNBCOLUMNS = NBCOLUMNS;
bIS_INIT=true;
}
}
void THEVARIANT::FILL(long lLINE, long lCOL, ATL::CComVariant & VARIANT_IN, VARTYPE VARIANT_TYPE)
{
if(bIS_INIT)
{
vTMP = VARIANT_IN;
vTMP.vt = VARIANT_TYPE;
plINDEX[0] = lLINE;
plINDEX[1] = lCOL;
HRESULT hr = pSA->MultiDimSetAt(plINDEX,vTMP);
ATLASSERT(hr == S_OK);
}
}
void THEVARIANT::ATTACH(VARIANT * pVARIANT_OUT)
{
if(bIS_INIT)
{
ATL::CComVariant varArrayWrapper(*pSA);
varArrayWrapper.Detach(pVARIANT_OUT);
}
}
void THEVARIANT::GET(long lLINE, long lCOL, ATL::CComVariant& VARIANT_OUT)
{
plINDEX[0] = lLINE+plDIM[1].lLbound; //be careful with the order
plINDEX[1] = lCOL+plDIM[0].lLbound;
HRESULT hr = pSA->MultiDimGetAt(plINDEX,VARIANT_OUT);
ATLASSERT(hr == S_OK);
}
long THEVARIANT::GETNBCOLS( void ) const
{
return lNBCOLUMNS ;
}
long THEVARIANT::GETNBLINES( void ) const
{
return lNBLINES ;
}
Maybe there is an error in this variant wrapper class THEVARIANT, but I tested it, and I don't think so.