Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

STL Compliant Class for SafeArrays

0.00/5 (No votes)
22 Nov 2006 8  
Encapsulates the details of SafeArray in a manner consistent with STL. Hides all the messy details.

What does it do?

It allows the transparent use of STL iterators, algorithms and idioms on SAFEARRAYs of all automation supported types.

When should I use it?

Whenever you wish to pass plain data to and/or from automation clients from C++. For VB clients, the use of Arrays is simple - now your use from C++ can be even more powerful. With the recent support of automation marshalling support for UDTs, this technique is an efficient means of transferring arbitrary quantities of semantically empty data from one process to another. You can even use the SAFEARRAY to transfer an array of COM interface (or IDispatch) pointers.

Why should I use it?

Look at this!

#include <algorithm>

#include "VectorSafeArray.h"

#include "VectorSafeArrayRecord.h"


IID StructIID = 
  {0xA8B2548C,0x9324,0x43B2,0xB1,0xAD,0x48,0xE2,0x84,0x05,0x2A,0x7E};

typedef safearrayvec::RecordType<TestStructure1, 
   &LIBID_ATLDEMO, &StructIID, 1, 0>  Struct1;
typedef safearrayvec::VectorSafeArray< TestStructure1, Struct1> Struct1Array;

STDMETHODIMP CTestVBStructure::ReverseArray(struct tagSAFEARRAY **sp)
{

    Struct1Array    a1;
// Since sp is an in,out parameter hand over

// ownership to the Array class for now.

    a1.Attach( sp);

// Let STL do the business

    std::reverse( a1.begin(), a1.end());

// Now transfer ownership back to the out parameter

    *sp = a1.Detach();
    return S_OK;
}

Five lines of code to reverse the order of a SAFEARRAY of user defined structures! This could equally well have been any arbitrary algorithm on any automation data type. Don't even consider doing this the hard way!

How do I use it?

Download the source code (Doh!). Make the header files available to your project. There is a small lib file which holds the definitions of a couple of static data members - include that in your link, or selectively add the appropriate .cpp files to your project. You will need to #include "VectorSafeArray.h" plus at least one of the VectorSafeArrayXXX files.

If you are using a structure, you will need to declare it in your IDL file, and ensure that it has an associated GUID. Then typedef the RecordType to suit.

For BSTR, VARIANT, IUnknown*, and IDispatch*, there are predefined types available for your use. For fundamental data types (automation compatible), simply typedef FundType<type, VARENUM>.

Sample Code

The sample code consists of an ATL DLL offering two interfaces through two classes (for no particular reason), and an MFC DLL with one class and interface which illustrates the use of the library. The associated VB6 clients serve to drive the samples and as a crude test harness. There is also a VBScript sample which uses a VARIANT to pass an array as an [in/out] parameter.

How efficient is it?

Reasonably! The VectorSafeArray<> class offers the same complexity guarantees as std::vector<>, but because it is using Windows 32 SafeArrayxxx functions, it is slower. The supplied template implementation of RecordType<> uses IRecordInfo functions to allocate, copy, and destroy memory. This is obviously slow, but it makes implementation easy. If you find that this is a problem in your application, you should replace it with a custom class to handle your structure.

The VariantType class is also very slow, it uses VariantXXX functions for copying, allocating, and destroying. Again, if this is a problem in your application, consider replacing it.

How does it work?

Glad you asked! But if you don't really want to know, you should still be able to use it quite happily - just like the rest of us use STL, ATL, or whatever!

I was motivated to write a generic handler for SAFEARRAYs of UDTs because I found myself writing reams of code just to test out an idea. Once started, it seemed to me that if I was to wrap the SAFEARRAY, it should be in the most useful way possible: which led me straight to the present implementation. I chose to emulate an std::vector<> because the internal structure of this class is just a chunk of memory with all the contained items arranged contiguously, just like a one dimensional SAFEARRAY! So I figured, I could 'leverage' a lot of existing code.

The VectorSafeArray<> is a rewrite of the <vector> header which comes with Visual C++ 6.0. The changes are to accommodate the fact that the underlying storage is owned by a SAFEARRAY, and all memory allocation, reallocation, and deallocation is done through SafeArrayXXX functions. For this reason, there is no Allocator template parameter to the class.

To support STL container operations, a class needs to have consistent copy construction, default construction, and assignment operations. The various types supported by automation in a SAFEARRAY have very different requirements for these operations. The most demanding of these types is the user defined structure. The template class RecordType:

template< typename sT, const IID *pLIBID, 
  const IID *pTypeID, const int vMajor = 1, const int vMinor = 0>
class RecordType : public sT

provides the required semantics through the use of IRecordInfo. typename sT is the user's structure type, the remaining parameters refer to the defining type library from which IRecordInfo will be initialized. Because the SAFEARRAY is a contiguous collection of 'user structures', it is clear that the RecordType cannot have any data members, or virtual functions - because if it did, it would no longer fit into the memory available in the SAFEARRAY! (see Notes below). But in order to verify the correctness of a passed array, it is necessary for the container to have access to the stored type. Each XXXXType instantiation holds a static copy of a TInfo class which stores the Flags required to validate the SAFEARRAY. This TInfo class has a function void Validate(SAFEARRAY *) which validates the SAFEARRAY flags, and optionally calls a static member of the XXXXType through a function pointer. XXXXType also holds a second function pointer for use by the XXXXType validation code if required. This second function pointer is currently used only by RecordType<> to access a cached copy of an ITypeInfo *.

This implementation through the use of the ITypeInfo is relatively slow. If you find that speed is an issue when testing, replace the generic RecordType with a specific implementation which makes use of your knowledge of the stored structure, to optimize construction and copying. E.g., a structure with only fundamental types can be copied bitwise by the compiler provided copy constructor.

The FundType<'>:

template< typename T, const VARENUM vt>
struct FundType

is used for all typed arrays of fundamental types. The SAFEARRAY will hold a VT type identifier which the class will verify against the provided template parameter. For valid values and types, see Platform SDK: Automation VARENUM - types indicated with S are OK.

The type VariantType is complete and requires no parameters.

template< typename iType, const unsigned short Flag>
struct InterfaceType

has two typedef'ed instances available for use for IUnknown and IDispatch.

typedef InterfaceType< IUnknown, FADF_UNKNOWN>    IUnknownType;
typedef InterfaceType< IDispatch, FADF_DISPATCH >    IDispatchType;

Limitations

Multi-threading has not been tested. I believe that the library should be safe for multiple thread access, provided that only one thread accesses any single instance of a container at one time. The only areas of concern are the use of ::VariantCopy in VariantType and access to the IRecordInfo * in RecordType<> - which could be called simultaneously by two or more threads if two or more instances of arrays of the same record type are in use. Both of these may be protected by a Critical Section by #define MULTITHREADING.

The library compiles in both UNICODE and MBCS. In fact, the MFC sample is MBCS and the ATL UNICODE.

When compiling IDL files with SAFEARRAY(type) *p parameters, you will see this message: "warning MIDL2039 : interface does not conform to [oleautomation] attribute :". This may be safely ignored.

Notes

MFCTest and VectorSafeArray have been compiled and tested under .NET Beta 2 (VC++ 7) and tested with the VB6 VBForMFC. The code works fine, there are a couple of warnings about signed/unsigned comparisons which may safely be ignored.

The library contains lines necessary to enforce the required size equivalence at compile time by using a small part of the Loki library - Andrei Alexandrescu "Modern C++ Design", Addison Wesley. If you haven't read it and you have an intellectual interest in C++ DO, it will blow your mind! The next version of VC++ (after .NET release!) should be able compile it! Some of the ideas presented would have been applicable to this library - if only VC++ supported partial template specialization!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here