This builds upon the information given by Aravid Corera in his article "Using a
Multidimensional SAFEARRAY to pass data across from COM objects." It replaces Richard
Warg’s article "Using a BSTR to Transport C++ Objects Across the DCOM
Interface." Although a creative idea, it is not a good idea to use BSTR for
things other than text.
This code shows how to pass data of any byte specific size, which may or may not be
known until runtime, through a SafeArray. This often occurs with data such as
bitmaps, binary files, or sound data. These are considered dynamic data because their size
and content can change from one session to another. Furthermore, it shows how to do
it as quick as possible. It also shows how to pack data from different variables
into the single array. The examples given are designed to be used in C++.
Other languages may or may not be able to determine the information packed into the array.
The first example uses memcpy() to copy the data directly into the SafeArray data.
This way is safe across all (D)COM processes, because it uses the standard COM
marshaller once the data is passed to the caller. The reason for using memcpy() is
speed. Calling SafeArrayPutElement() and SafeArrayGetElement() can get really slow.
Imagine calling them 2048 times( when getting and retrieving the data ) for a 1kb
file. COM is slow enough, no more overhead is needed.
The second example just passes a pointer to an area of memory that contains the data
needed. This is ONLY safe when using INPROC DLLs. I included this as more of
an example on how to past data as fast as possible, not necessarily the best and safest
way.
Example 1:
The SafeArray is created based on the total size of the data. Next we get access
to the data directly. Finally, using memcpy() the data is just transferred right
into the array.
Unpacking the data is pretty much exactly the reverse. The demo project and source
code shows how to do it. Notice that the header is passed in first, then using
pointer arithmetic, the bitmap bits are passed in.
HRESULT __stdcall CCOMDynamicData::PassBitmapCopy(LPSAFEARRAY* ppsaDynamic)
{
long lBmpSize;
if ( !m_bmp.bmBits )
{
lBmpSize = LoadDDBitmap();
if ( lBmpSize == 0 )
return S_FALSE;
}
else
lBmpSize = m_bmp.bmWidthBytes * m_bmp.bmHeight;// Create the storage
// notice the size is that of the header storage and of the bits
SAFEARRAY* psaBitmapBits = ::SafeArrayCreateVector( VT_UI1, 0, sizeof(BITMAP ) + lBmpSize );
LPVOID lpArrayData;
::SafeArrayAccessData( psaBitmapBits, &lpArrayData );
// copy the header
::memcpy( lpArrayData, &m_bmp, sizeof( BITMAP ) );
// copy the data
// offset by the bitmap
::memcpy( (char *)lpArrayData + sizeof( BITMAP ), m_bmp.bmBits,BmpSize );
::SafeArrayUnaccessData( psaBitmapBits );*ppsaDynamic = psaBitmapBits;
return S_OK;
}
Example 2:
Just pass a pointer to where the data is at. This is fast, but ONLY works in
INPROC servers. If you have no need for compatibility and will only be using INPROC
servers, then this is the way for you.
HRESULT __stdcall CCOMDynamicData::PassBitmapPtr(BITMAP* pBmp)
{
if ( m_bmp.bmBits ) // if we already got the bitmap
{
*pBmp = m_bmp; // just return it
return S_OK;
}
// else we gotta get bitmap informationif ( LoadDDBitmap() == 0 )
return S_FALSE;*pBmp = m_bmp;
return S_OK;
}
The code in the zipped files below passes a bitmap structure between an INPROC COM
server and a client. Once the bitmap is retrived it is displayed on the screen.
Change the Bitmap in the COM DLL to demonstrate to yourself how the size of a
bitmap can be determined at runtime and then still have it’s data passed successfully.