Appendix D. Passing Block Data, SAFEARRAYs

Certain Microsoft technologies are extremely useful, relatively easy to use, and almost impossible to find coherent documentation or examples for. A SAFEARRAY is just such a creature. Simply put, VB arrays are represented internally as something called a SAFEARRAY. SAFEARRAYs can be multi-dimensional, have esoteric locking considerations, and have some of the most confusing, obscure, and unhelpful documentation I've come across. In spite of this, they are very useful; they can allow you to pass arbitrary-length blocks of data over the network using type library marshaling.

Type library marshaling is the default, pre-installed marshaler that enables COM to sbasic types as arguments to your interface methods—integers, doubles, strings, and so on. But what about sending large blocks of data with a single method call? If you want to send an array of, let's say, 100 integers, things get tricky. You can either make 100 method calls passing one integer each time, or you can resort to standard marshaling. Standard marshaling allows you to pass any C++ data type and most arrays and structures. The downside is that you must use C++ and ship a proxy/stub DLL (unless you merge the proxy/stub DLL into the COM DLL, which is also an option). Even if you go to this trouble of implementing a proxy/stub DLL, VB can only use interfaces that use OLE automation types. The existence of a proxy/stub DLL usually indicates that non-automation types are being marshaled via standard marshaling; thus, a VB client or server would not be able to interact with your component. This means that, for the most part, you cannot sblocks or arrays of data to VB using standard marshaling.

This brings us to SAFEARRAYs. Arrays in VB can be sent using type library marshaling, because VB arrays are considered OLE automation types. As I've said, the underlying representation of a VB array is a SAFEARRAY, and what is simple in VB becomes somewhat complex in C++. However, code examples should make things clear. In the companion code for this book (http://www.newriders.com/complus), I've included a directory called safearrays that includes a series of examples demonstrating how to pass arrays from C++ to VB and vice versa. SAFEARRAYs don't have anything to do with C++ per se,but they are a useful tool to have in COM+ development.

It is difficult to give SAFEARRAYs adequate coverage without a dedicated chapter, so I'm going to leave it to the source code accompanying this book to explore a number of detailed SAFEARRAY scenarios and permutations. However, just to give you a preview, let's take a look at how an array of VB strings can be sent to and interpreted by a C++ component using SAFEARRAYs.

Imagine that you have the following VB client:

'Throw a few strings together into an array
Dim astrColour(0 To 3) As String
astrColour(0) = "Purple"
astrColour(1) = "Yellow"
astrColour(2) = "Green"
astrColour(3) = "Red"

Dim UseSafearray As New SERVERLib.UseSafearray Call
UseSafearray.DisplayStrings(astrColour)

Toreceive a VB array (a SAFEARRAY, remember), the IDL for the C++ component UseSafearray needs to be the following:

//snippet of IDL
interface IUseSafearray : IDispatch
    {
        [id(1), helpstring("method DisplayStrings")] HRESULT DisplayStrings ([in] SAFEARRAY(BSTR) *psa);

Note that VB strings are BSTRs in C++. ABSTR is simply an array of two-byte shorts, two bytes for every character. The first four bytes of a BSTR constitute a long that indicates the length of the string. Don't worry, there are a number of BSTR wrapper classes that make them much easier to work with.

The following coderetrieves and displays the strings of this array in C++:

STDMETHODIMP CUseSafearray::DisplayStrings(SAFEARRAY** ppsa)
{

    LONG nLBound = 0;
    //    Get lower bound of first dimension
    hr = ::SafeArrayGetLBound(*ppsa, 1, &nLBound);
    ATLASSERT(SUCCEEDED(hr));

    //    Get upper bound of first dimension
    LONG nUBound = 0;
    hr = ::SafeArrayGetUBound(*ppsa, 1, &nUBound);

    BSTR* pbstrElement = NULL;

CComBSTR bstrComposite = _
T("We received and unpacked the strings: ");

hr = ::SafeArrayAccessData(*ppsa, (void**)&pbstrElement);

for(LONG nI = nLBound; nI <= nUBound; nI++)
    {
            bstrComposite += pbstrElement[nI];
    }

::MessageBox(NULL, (TCHAR*)bstrComposite, _T("[SERVER]
   CUseSafearray::DisplayStrings()"), MB_OK);
    //    Done with guts of the array - sew it back up
    hr = ::SafeArrayUnaccessData(*ppsa);

}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset