Using COM to Pass Arrays

OVERVIEW:

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.

BACKGROUND:

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.

EXAMPLES:

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;
	SAFEARRAYBOUND bound;
	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
		SafeArrayUnaccessData(varTemp.parray);

		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))
		{
			free(fBuf);
			return false;
		}

		SafeArrayDestroy(varTemp.parray); // destroy the array.

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

	} // End of while
	free(fBuf);
	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;
}

NOTES:

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.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read