Using COM to Pass Arrays


Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame


COM is a great tool when programming in windows. It gives us the ability to communicate with other programs and allows us to scale our products.  There are a few things we are are used to doing however, that are more difficult with COM, one of these is passing arrays as parameters in COM methods.  This article describes the process to pass arrays of data to and from COM objects, without writing our own marshaler.


It's a myth really that COM only supports VARIANT data type as parameters.  Most COM developers don't know about this because we are used to using the wizards that only provide these as options.  We can however, pass any datatypes we want, even structs and arrays, but if we need this functionality then we must write our own marshaler.   This is not really a hard task, but it does require some additional time, and for most applications, it's much easier to use the system marshaler (oleaut32.dll).  The cost of using the system marshaler is we must use VARIANT types.

To pass an array of data, we have two choices.  We can write our own marhaler, or we can send the array as a VARIANT.  The VARIANT structure (which is what it really is) allows for a an element called SAFEARRAY.  The examples below show how to do this.


This is an example of how to send a file via COM to a server, who in turn writes the file to it's own disk system.  This is kind of like a file transfer protocol via COM.   It's not the quickest way to do it, but it works, and more importantly, we used COM, and we did it without writing our own marshaler.

//The Client

BOOL CClient::StreamIn()
	char *fBuf;
	fBuf = (char*)malloc(4096); // Allocate our buffer

	VARIANT varTemp;
	varTemp.vt = VT_UI1 | VT_ARRAY;
	long lLen = m_File->GetLength();

	long nBytesRead = m_File->Read( fBuf, 4096 ); // Read from our file into our buffer (note: file opened as binary)
	int iCount = 4096;
	while(nBytesRead>0) // While we read bytes
		bound.cElements = nBytesRead; // Set up size of array
		bound.lLbound = 0;
		varTemp.parray = SafeArrayCreate(VT_UI1, 1, &bound); // Create it
		void* pDest;
		SafeArrayAccessData(varTemp.parray, &pDest);
		memcpy(pDest, fBuf, nBytesRead); // Copy into array

		long retval = 0;
		// m_server was created using the smart pointers (#import "yourserver.tlb" no_namespace ... etc)
		HRESULT _hr = m_server->raw_SendFile(varTemp); // Call the server method, pass our VARIANT
		if(retval != 0 && !FAILED(_hr))
			return false;
		SafeArrayDestroy(varTemp.parray); // destroy the array.

		nBytesRead = m_File->Read( fBuf, 4096 );  Read moredata

	} // End of while
	return true;

//The Server
// IDL:
	[id(1), helpstring("method SendFile")] HRESULT SendFile([in] VARIANT buffer);
// Implementation
STDMETHODIMP CServer::SendFile(VARIANT buffer)
	AFX_MANAGE_STATE(AfxGetStaticModuleState()) // To support MFC

	void* pDest;
	// Previously the server object had opened it's own file
	if(m_File!=NULL) // Which should mean that it is open
		if (buffer.vt == (VT_ARRAY | VT_UI1))
			SafeArrayAccessData(buffer.parray, &pDest); // Get the data
			SafeArrayUnaccessData(buffer.parray);	    // Unaccess it.
			m_File->Write(pDest, buffer.parray->rgsabound->cElements); // Write it to the server file


	return S_OK;


We could have used SAFEARRAY's and VARIANT by using MFC or ATL wrapper classes, but the examples above are easier to understand. Also, we could have added better try catch blocks etc, but I've taken them out to simply the example.  I'll leave that up to you.   In process this code is very fast, out of process or out of band (DCOM), you'll want to set the buffer size at least as high as your network packet size, if not more.   What makes this slow is calling the method over and over.  Alternatively, we could have passed a com interface to the function, but DCOM implementations make this a nightmare.


  • how to pass an array of struct from VB script to COM

    Posted by Legacy on 07/03/2003 07:00am

    Originally posted by: yuexiang

    who can help me

  • Problem in passing variant

    Posted by Legacy on 04/26/2003 07:00am

    Originally posted by: shailesh

    Hey i am passing the variant variable to a function by preparing the data as follows. Can anybody tell me where is the problem in this code. I am not getting any error but it is not showing any data. Thanks

    MyVar.vt = VT_VARIANT | VT_ARRAY;

    // a one dimensional array
    aDim[0].lLbound = 0;
    aDim[0].cElements = 1938;
    pSA = SafeArrayCreateVector (VT_VARIANT,0,1938);
    void *pDest;
    SafeArrayAccessData(pSA, &pDest);
    memcpy(pDest, tblob.lpb, 1937); // Copy into array

    MyVar.parray= pSA;
    SafeArrayDestroy(pSA); // destroy the array.

  • Can't pass ARRAY in this way!

    Posted by Legacy on 10/26/2001 07:00am

    Originally posted by: Xu Fayan

    I am going to use VB activeX dll in my VC++ project,
    but I can't call VB's dll function like below, for the parameter is an array.

    VB DLL

    Public Sub b(a() As Integer, d As Integer)

    Dim sum As Integer
    Dim i As Integer
    sum = 0
    For i = 0 To d
    sum = sum + CInt(a(i))
    Next i
    MsgBox CStr(sum)

    End Sub

    VC Client

    VARIANT varTemp;
    varTemp.vt = VT_I4 | VT_ARRAY;

    bound.cElements = 4; // Set up size of array
    bound.lLbound = 0;
    varTemp.parray = SafeArrayCreate(VT_I4, 1, &bound); // Create it
    void* pDest;

    SafeArrayAccessData(varTemp.parray, &pDest);

    memcpy(pDest, c, sizeof(c)); // Copy into array

    // t is the object of VB DLL class
    t->b(&varTemp.parray, (short *)&d);


    Can any body can help me to solve it?

    Thanks advancedly.

  • Dont d this if you are saving to an IPropertyBag!

    Posted by Legacy on 09/20/2001 07:00am

    Originally posted by: Ryan Mills

    I have a void* buffer of n bytes, which I can happily pass around functions using a SAFEARRAY. If I use it to persist data in my control (i.e., I have a control embedded in a web page), my control implements IPersistPropertyBag_Load and has to call:

    pPropBag->Write(bstrPropertyName, &vVar));

    ...where vVar is a CComVariant object containing my SAFEARRAY. However, because the VARTYPE is VT_UI1|VT_ARRAY, the implementation of IPropertyBag sees a string of chars and therefore only stores data up to the first null character!!!!!!

  • Thank You!!

    Posted by Legacy on 08/01/2001 07:00am

    Originally posted by: Mike Pulice

    I am new to COM but have a big project going, typical.
    I was stuck on the large packets I have to handle so I figured go to CodeGuru. I was relieved when I saw your article. Thanks for posting it!

    Also, thanks for the IStream hints too.


  • What us quicker

    Posted by Legacy on 02/02/2001 08:00am

    Originally posted by: Volodymyr Kozatchenko

    What design is quicker (and tackes less memory) for getting/putting in COM?

    Variant 1) ATL using uviversal marsh. (i.e. oleautomation in .IDL
    [id(1), helpstring("method GetData")] HRESULT PutDataV([in] VARIANT pVar);
    [id(1), helpstring("method GetData")] HRESULT GetDataV([out] VARIANT pVar);

    Variant 2) ATL using its proxy/stub marsh. dll (made with nmake...)
    [id(2), helpstring("method PutFoo")]
    HRESULT PutFoo([in] short m, [in, size_is(m)] char * pbyte);
    [id(3), helpstring("method GetFoo")] HRESULT GetFoo([in] short ms, [out, size_is(ms)] unsigned char * pbyte);

    Thank you.

  • Alloc SafeArray too long

    Posted by Legacy on 08/02/2000 07:00am

    Originally posted by: Riccardo Raccuglia

    if I reading 4096 byte from files I can alloc a safe array with cElements = 4096 / sizeof(UINT) ?

  • pass an array of user-defined structures from a vb client to c++ server?

    Posted by Legacy on 05/18/2000 07:00am

    Originally posted by: Unsin

    How do we pass an array of user-defined structures via COM from a vb client to c++ server? 


  • Passing any kind of data

    Posted by Legacy on 01/27/2000 08:00am

    Originally posted by: DoubleJ

    In case you have both client and server implemented in C++
    the easiest way to pass any kind of data is to use IStream interface.
    This interface can be marshalled across process/machine boundaries
    so it is universal and safe method.

    Here is fragment of server side code (no error checking):

    STDMETHODIMP CServer::GetData( LPSTREAM *ppStm )
    // Create stream in memory
    // For better performance and to avoid reallocation
    // you should preallocate memory using
    // HGLOBAL hGlob = GlobalAlloc( GMEM_MOVEABLE, dwLen )
    // and use hGlob instead of NULL in following function
    HRESULT hr = CreateStreamOnHGlobal( NULL, TRUE, ppStm );
    // Write some C++ data
    hr = *ppStm->Write( &SomeValue, sizeof( SomeValue ), NULL );
    // Write some COM objects that implement IPersistStream interface
    OleSaveToStream( com_cast<IPersistStream>(pSomeObject), *ppStm );
    // Data written
    // Seek to start of stream (for client comfort)
    ULARGE_INTEGER liNewPosition;
    LISet32(liOff, 0);
    hr = *ppStm->Seek(liOff, STREAM_SEEK_SET, &liNewPosition);

    return hr;

    Here is fragment of client side code (no error checking):

    CComPtr<IStream> pStm = NULL;
    pServer->GetData( &pStm );
    // Read C++ data from stream
    pStm->Read( &SomeValue, sizeof( SomeValue ), NULL );
    // Read COM objects
    CComPtr<ISomeObject> pSomeObject = NULL;
    OleLoadFromStream( pStm, __uuidof(ISomeObject), (LPVOID*)&pSomeObject );

    Hope this helps.

  • How to pass array of COM objects?

    Posted by Legacy on 05/21/1999 07:00am

    Originally posted by: Yalin Wei


  • Loading, Please Wait ...

  • You must have javascript enabled in order to post comments.

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

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date