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
Why should I use it?
Look at this!
IID StructIID =
&LIBID_ATLDEMO, &StructIID, 1, 0> Struct1;
typedef safearrayvec::VectorSafeArray< TestStructure1, Struct1> Struct1Array;
STDMETHODIMP CTestVBStructure::ReverseArray(struct tagSAFEARRAY **sp)
// Since sp is an in,out parameter hand over
// ownership to the Array class for now.
// Let STL do the business
std::reverse( a1.begin(), a1.end());
// Now transfer ownership back to the out parameter
*sp = a1.Detach();
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
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
RecordType to suit.
IDispatch*, there are predefined types available for your use. For fundamental data types (automation compatible), simply
typedef FundType<type, VARENUM>.
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
How efficient is it?
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
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.
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.
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
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
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
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
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.
template< typename T, const VARENUM vt>
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.
VariantType is complete and requires no parameters.
template< typename iType, const unsigned short Flag>
typedef'ed instances available for use for
typedef InterfaceType< IUnknown, FADF_UNKNOWN> IUnknownType;
typedef InterfaceType< IDispatch, FADF_DISPATCH > IDispatchType;
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
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
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.
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!