Passing arrays of structures in COM

The sample code used in this article was compiled using Visual C++ 6.0. The article demonstrates the use of a VARIANT to pass arrays of structures in COM. The class that does all the work is a template class called CComArray (not very original but effective). You create an instance like any other template class by passing in the name of your structure (i.e. CComArray"<"MYSTRUCT">" myArray;). In order to replicate the [in]/[out] functionality of COM calls, you have the choice of calling the Initialize method or the Attach method. The Initialize member function takes a VARIANT pointer, a size value and a boolean value which tells CComArray if it is the owner of the VARIANT. If it is the owner, the constructor will clean up all memory associated with the VARIANT. If not, you are responsible for the cleanup. The second member function, Attach, attaches a VARIANT pointer. Once again you can specify if you want this instance to own the VARIANT. When calling this method it is assumed that the VARIANT has already been initialized somewhere in your code via the Initialize method. Both methods return false if they are used improperly. The CComArray also has two methods to access the elements of the array. They are GetAt and SetAt which get and set your structures at the index you specify. These methods also return false if an error occurs such as specifying an index which is out of bounds. Please feel free to use and/or modify the code as you wish. There are lots of improvements an industrious programmer could make.
template "<"class T">" class CComArray
{

// Attributes
private:

    int      m_nSize;
    bool     m_bOwner;
    VARIANT* m_pVariant;

// Implemenation
public:

CComArray() 
    :m_nSize(0),m_bOwner(false),m_pVariant(NULL)
{ 
}

virtual ~CComArray() 
{ 
    Clear();
}

void Clear()
{
    if(m_bOwner && m_pVariant)
    {
        if(m_pVariant->parray)
	        SafeArrayDestroy(m_pVariant->parray);
        m_pVariant->parray=NULL;
        VariantClear(m_pVariant);
    }

    m_nSize=0;
    m_bOwner=false;
    m_pVariant=NULL;
}

bool Initialize(VARIANT* pVar,int nSize,bool bOwner)
{
    if(m_pVariant!=NULL || pVar==NULL || nSize<1)
        return false;

    VariantInit(pVar);
 
    pVar->vt=VT_ARRAY|VT_UI1;

    SAFEARRAYBOUND s= { nSize*sizeof(T),0 };

    if((pVar->parray=SafeArrayCreate(VT_UI1,1,&s))==NULL)
        return false;
	
    m_nSize=nSize;
    m_bOwner=bOwner;
    m_pVariant=pVar;

    return true;	
}

bool Attach(VARIANT* pVar,bool bOwner)
{
    if(m_pVariant!=NULL || pVar==NULL)
        return false;

    if(pVar->parray==NULL || pVar->vt!=(VT_ARRAY|VT_UI1))
        return false;

    long nLBound=0L;
    long nUBound=0L;

    if(FAILED(SafeArrayGetLBound(pVar->parray,1,&nLBound)))
        return false;

    if(FAILED(SafeArrayGetUBound(pVar->parray,1,&nUBound)))
        return false;

    if(nUBound>0L)
        m_nSize=(int)(((nUBound-nLBound)+1L)/sizeof(T));
	
    m_bOwner=bOwner;
    m_pVariant=pVar;

    return true;	
}

int Size()
{
    return m_nSize;
}

bool GetAt(int n,T& Item)
{
    if(!m_pVariant || n<0 || n>=m_nSize)
        return false;

    LPBYTE pData;
    SafeArrayAccessData(m_pVariant->parray,(void**)&pData);
    CopyMemory(&Item,&pData[n*sizeof(T)],sizeof(T));
    SafeArrayUnaccessData(m_pVariant->parray);
    return true;
}

bool SetAt(int n,T& Item)
{
    if(!m_pVariant || n<0 || n>=m_nSize)
        return false;

    LPBYTE pData;
    SafeArrayAccessData(m_pVariant->parray,(void**)&pData);
    CopyMemory(&pData[n*sizeof(T)],&Item,sizeof(T));
    SafeArrayUnaccessData(m_pVariant->parray);
    return true;
}
};

Download Source Code and Example

Last updated: 05 March 1999