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



Comments

  • Memory leak when sending event

    Posted by Legacy on 06/08/2003 12:00am

    Originally posted by: Filbert Fox

    When sending a event, I am getting a memory leak, I am sending back an array, any ideas?
    
    

    server code:

    BOOL CCSTARTServer::SendNotify(enumDATATYPE dt)
    {
    CComArray<DATA_STRUCTURE> array;
    VARIANT var;

    if( !array.Initialize(&var,1,FALSE) )
    return E_FAIL;

    DATA_STRUCTURE d;
    d.m_nType = dt;
    array.SetAt( 0, d );

    HRESULT hr = Fire_Notify( var );

    return TRUE;
    }

    Client code:
    HRESULT __stdcall OnNotify( VARIANT var )
    {
    // for a notify it must have only 1 element in the array

    CComArray<DATA_STRUCTURE> array;
    if( !array.Attach(&var,TRUE) )
    return FALSE;

    int nSize = array.Size();
    if( nSize != 1 )
    return E_FAIL;

    DATA_STRUCTURE d;

    BOOL bOk = array.GetAt( 0, d );
    if( !bOk )
    return E_FAIL;

    switch( d.m_nType )
    {
    case DATA_CHANGED:
    break;
    } // switch

    MessageBox( NULL, "notify", "client", MB_OK );

    return S_OK;
    }

    Reply
  • Passing arrays of structures in COM.

    Posted by Legacy on 10/02/2002 12:00am

    Originally posted by: Neeraj Garg

    I need to pass array of structures to ASP. Can I do it ?

    Reply
  • How to pass Variant parameter to VBScript

    Posted by Legacy on 11/17/2001 12:00am

    Originally posted by: ddpanda

    I have a ActiveX control which has a custom method:Add([in]short Num1,[in]short Num2,[out,retval]VARIANT *short),how can I get the return value of this method by VBScript

    Reply
  • Byte Alignment when recieveing structures and VariantClear too

    Posted by Legacy on 06/05/2001 12:00am

    Originally posted by: Arun

    I have some code that does the same thing - passing arrays of UDT - i send and recieve structures to and from VB components across MTS deployed components

    However the byte alignment used by my application is 1 and vb by default uses 4byte alignment. This is giving me problems.

    is there a solution? right now iam trying to work around using #pragma packs but it just doesnt look so good enuff.

    And abt the VAriantclear - is it really enuff to clean the UDT array ? will it go into each UDT and release BSTRs in it and release all the memory occupised by the array ?

    Reply
  • How to access SafeArray from a VB client...

    Posted by Legacy on 12/09/2000 12:00am

    Originally posted by: Vivek Oza

    Is it possible to access a SafeArray of COM object through a Visual Basic client and iterate through the array
    

    Reply
  • Working DCOM i get DISP_E_ARRAYISLOCKED, why?

    Posted by Legacy on 03/02/2000 12:00am

    Originally posted by: David Avramov

    I work with VC++/6 .
    Sometimes i get an exception DISP_E_ARRAYISLOCKED,meaning
    "Memory is locked" , while working with DCOM.

    Why do this happen,and what shall i do to resolve it?

    Thanks,

    Reply
  • Passing array of struct's to VB

    Posted by Legacy on 01/26/2000 12:00am

    Originally posted by: Rama Nadendla

    Hi,
    You have an example of how we can pass the struct in COM/C++ env. How can i send it from COM/C++ env to VB.
    Thanks in advance.

    Reply
  • Will it work with structures containing pointers?

    Posted by Legacy on 09/29/1999 12:00am

    Originally posted by: Krishna Kadali

    I might be missing something here, but I can't figure out how this approach would work when my structure contains some pointers like BSTR. Could some one explain me a little bit if this approach can work with structure containing pointers?

    Reply
  • Is marshalling required for to pass this structure to a remote server??

    Posted by Legacy on 07/09/1999 12:00am

    Originally posted by: Kalyan


    I used this ComArray template class for passing array of structures from COM client's to Inproc servers. But i failed to pass a structure containing a BSTR elements to a remote server. Is any marshalling support required??.
    I suspect that the SetAt() fn copies only the top level pointers i.e, the BSTR [since BSTR being a ptr to a ptr] and not the data.

    Any workaround,?? help is very much appreciated.

    Reply
  • Is it really that simple?

    Posted by Legacy on 03/16/1999 12:00am

    Originally posted by: Carsten Traupe

    When I'm right the clue of this approach lies in making the information typless (VARIANT) and hope that copying it in memory bit by bit later receives the correct structure.

    But this assumes that the compiler always generates the same code for the class T on both sides of the COM barrier. When marshalling across system boundaries, for exsample from WinNT to Win3x this would crash in the same way than flattening the memory to a BSTR.

    But maybe there is no generic and system independent way to marshall arbitrary data. I my opinion this is the case. Please tell me that I'm wrong.

    Carsten.

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds