/** @file * MS COM / XPCOM Abstraction Layer: * Safe array helper class declaration */ /* * Copyright (C) 2006-2007 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. */ #ifndef ___VBox_com_array_h #define ___VBox_com_array_h /** @defgroup grp_COM_arrays COM/XPCOM Arrays * @{ * * The COM/XPCOM array support layer provides a cross-platform way to pass * arrays to and from COM interface methods and consists of the com::SafeArray * template and a set of ComSafeArray* macros part of which is defined in * VBox/com/defs.h. * * This layer works with interface attributes and method parameters that have * the 'safearray="yes"' attribute in the XIDL definition: * @code * @endcode * * Methods generated from this and similar definitions are implemented in * component classes using the following declarations: * @code STDMETHOD(TestArrays) (ComSafeArrayIn (LONG, aIn), ComSafeArrayOut (LONG, aOut), ComSafeArrayOut (LONG, aRet)); * @endcode * * And the following function bodies: * @code STDMETHODIMP Component::TestArrays (ComSafeArrayIn (LONG, aIn), ComSafeArrayOut (LONG, aOut), ComSafeArrayOut (LONG, aRet)) { if (ComSafeArrayInIsNull (aIn)) return E_INVALIDARG; if (ComSafeArrayOutIsNull (aOut)) return E_POINTER; if (ComSafeArrayOutIsNull (aRet)) return E_POINTER; // Use SafeArray to access the input array parameter com::SafeArray in (ComSafeArrayInArg (aIn)); for (size_t i = 0; i < in.size(); ++ i) LogFlow (("*** in[%u]=%d\n", i, in [i])); // Use SafeArray to create the return array (the same technique is used // for output array paramters) SafeArray ret (in.size() * 2); for (size_t i = 0; i < in.size(); ++ i) { ret [i] = in [i]; ret [i + in.size()] = in [i] * 10; } ret.detachTo (ComSafeArrayOutArg (aRet)); return S_OK; } * @endcode * * Such methods can be called from the client code using the following pattern: * @code ComPtr component; // ... com::SafeArray in (3); in [0] = -1; in [1] = -2; in [2] = -3; com::SafeArray out; com::SafeArray ret; HRESULT rc = component->TestArrays (ComSafeArrayAsInParam (in), ComSafeArrayAsOutParam (out), ComSafeArrayAsOutParam (ret)); if (SUCCEEDED (rc)) for (size_t i = 0; i < ret.size(); ++ i) printf ("*** ret[%u]=%d\n", i, ret [i]); * @endcode * * For interoperability with standard C++ containers, there is a template * constructor that takes such a container as argument and performs a deep copy * of its contents. This can be used in method implementations like this: * @code STDMETHODIMP Component::COMGETTER(Values) (ComSafeArrayOut (int, aValues)) { // ... assume there is a |std::list mValues| data member com::SafeArray values (mValues); values.detachTo (ComSafeArrayOutArg (aValues)); return S_OK; } * @endcode * * The current implementation of the SafeArray layer supports all types normally * allowed in XIDL as array element types (including 'wstring' and 'uuid'). * However, 'pointer-to-...' types (e.g. 'long *', 'wstring *') are not * supported and therefore cannot be used as element types. * * Note that for GUID arrays you should use SafeGUIDArray and * SafeConstGUIDArray, customized SafeArray<> specializations. * * Also note that in order to pass input BSTR array parameters declared * using the ComSafeArrayIn (IN_BSTR, aParam) macro to the SafeArray<> * constructor using the ComSafeArrayInArg() macro, you should use IN_BSTR * as the SafeArray<> template argument, not just BSTR. * * Arrays of interface pointers are also supported but they require to use a * special SafeArray implementation, com::SafeIfacePointer, which takes the * interface class name as a template argument (e.g. com::SafeIfacePointer * ). This implementation functions identically to com::SafeArray. */ #if defined (VBOX_WITH_XPCOM) # include #endif #include "VBox/com/defs.h" #include "VBox/com/ptr.h" #include "VBox/com/assert.h" #include "iprt/cpp/utils.h" #if defined (VBOX_WITH_XPCOM) /** * Wraps the given com::SafeArray instance to generate an expression that is * suitable for passing it to functions that take input safearray parameters * declared using the ComSafeArrayIn macro. * * @param aArray com::SafeArray instance to pass as an input parameter. */ #define ComSafeArrayAsInParam(aArray) \ (aArray).size(), (aArray).__asInParam_Arr ((aArray).raw()) /** * Wraps the given com::SafeArray instance to generate an expression that is * suitable for passing it to functions that take output safearray parameters * declared using the ComSafeArrayOut macro. * * @param aArray com::SafeArray instance to pass as an output parameter. */ #define ComSafeArrayAsOutParam(aArray) \ (aArray).__asOutParam_Size(), (aArray).__asOutParam_Arr() #else /* defined (VBOX_WITH_XPCOM) */ #define ComSafeArrayAsInParam(aArray) (aArray).__asInParam() #define ComSafeArrayAsOutParam(aArray) (aArray).__asOutParam() #endif /* defined (VBOX_WITH_XPCOM) */ /** * */ namespace com { #if defined (VBOX_WITH_XPCOM) //////////////////////////////////////////////////////////////////////////////// /** * Provides various helpers for SafeArray. * * @param T Type of array elements. */ template struct SafeArrayTraits { protected: /** Initializes memory for aElem. */ static void Init (T &aElem) { aElem = 0; } /** Initializes memory occupied by aElem. */ static void Uninit (T &aElem) { aElem = 0; } /** Creates a deep copy of aFrom and stores it in aTo. */ static void Copy (const T &aFrom, T &aTo) { aTo = aFrom; } public: /* Magic to workaround strict rules of par. 4.4.4 of the C++ standard (that * in particular forbid casts of 'char **' to 'const char **'). Then initial * reason for this magic is that XPIDL declares input strings * (char/PRUnichar pointers) as const but doesn't do so for pointers to * arrays. */ static T *__asInParam_Arr (T *aArr) { return aArr; } static T *__asInParam_Arr (const T *aArr) { return const_cast (aArr); } }; template struct SafeArrayTraits { // Arbitrary pointers are not supported }; template<> struct SafeArrayTraits { protected: static void Init (PRUnichar * &aElem) { aElem = NULL; } static void Uninit (PRUnichar * &aElem) { if (aElem) { ::SysFreeString (aElem); aElem = NULL; } } static void Copy (const PRUnichar * aFrom, PRUnichar * &aTo) { AssertCompile (sizeof (PRUnichar) == sizeof (OLECHAR)); aTo = aFrom ? ::SysAllocString ((const OLECHAR *) aFrom) : NULL; } public: /* Magic to workaround strict rules of par. 4.4.4 of the C++ standard */ static const PRUnichar **__asInParam_Arr (PRUnichar **aArr) { return const_cast (aArr); } static const PRUnichar **__asInParam_Arr (const PRUnichar **aArr) { return aArr; } }; template<> struct SafeArrayTraits { protected: static void Init (const PRUnichar * &aElem) { aElem = NULL; } static void Uninit (const PRUnichar * &aElem) { if (aElem) { ::SysFreeString (const_cast (aElem)); aElem = NULL; } } static void Copy (const PRUnichar * aFrom, const PRUnichar * &aTo) { AssertCompile (sizeof (PRUnichar) == sizeof (OLECHAR)); aTo = aFrom ? ::SysAllocString ((const OLECHAR *) aFrom) : NULL; } public: /* Magic to workaround strict rules of par. 4.4.4 of the C++ standard */ static const PRUnichar **__asInParam_Arr (const PRUnichar **aArr) { return aArr; } }; template<> struct SafeArrayTraits { protected: static void Init (nsID * &aElem) { aElem = NULL; } static void Uninit (nsID * &aElem) { if (aElem) { ::nsMemory::Free (aElem); aElem = NULL; } } static void Copy (const nsID * aFrom, nsID * &aTo) { if (aFrom) { aTo = (nsID *) ::nsMemory::Alloc (sizeof (nsID)); if (aTo) *aTo = *aFrom; } else aTo = NULL; } /* This specification is also reused for SafeConstGUIDArray, so provide a * no-op Init() and Uninit() which are necessary for SafeArray<> but should * be never called in context of SafeConstGUIDArray. */ static void Init (const nsID * &aElem) { NOREF (aElem); AssertFailed(); } static void Uninit (const nsID * &aElem) { NOREF (aElem); AssertFailed(); } public: /** Magic to workaround strict rules of par. 4.4.4 of the C++ standard. */ static const nsID **__asInParam_Arr (nsID **aArr) { return const_cast (aArr); } static const nsID **__asInParam_Arr (const nsID **aArr) { return aArr; } }; #else /* defined (VBOX_WITH_XPCOM) */ //////////////////////////////////////////////////////////////////////////////// struct SafeArrayTraitsBase { protected: static SAFEARRAY *CreateSafeArray (VARTYPE aVarType, SAFEARRAYBOUND *aBound) { return SafeArrayCreate (aVarType, 1, aBound); } }; /** * Provides various helpers for SafeArray. * * @param T Type of array elements. * * Specializations of this template must provide the following methods: * // Returns the VARTYPE of COM SafeArray elements to be used for T static VARTYPE VarType(); // Returns the number of VarType() elements necessary for aSize // elements of T static ULONG VarCount (size_t aSize); // Returns the number of elements of T that fit into the given number of // VarType() elements (opposite to VarCount (size_t aSize)). static size_t Size (ULONG aVarCount); // Creates a deep copy of aFrom and stores it in aTo static void Copy (ULONG aFrom, ULONG &aTo); */ template struct SafeArrayTraits : public SafeArrayTraitsBase { protected: // Arbitrary types are treated as passed by value and each value is // represented by a number of VT_Ix type elements where VT_Ix has the // biggest possible bitness necessary to represent T w/o a gap. COM enums // fall into this category. static VARTYPE VarType() { if (sizeof (T) % 8 == 0) return VT_I8; if (sizeof (T) % 4 == 0) return VT_I4; if (sizeof (T) % 2 == 0) return VT_I2; return VT_I1; } static ULONG VarCount (size_t aSize) { if (sizeof (T) % 8 == 0) return (ULONG) ((sizeof (T) / 8) * aSize); if (sizeof (T) % 4 == 0) return (ULONG) ((sizeof (T) / 4) * aSize); if (sizeof (T) % 2 == 0) return (ULONG) ((sizeof (T) / 2) * aSize); return (ULONG) (sizeof (T) * aSize); } static size_t Size (ULONG aVarCount) { if (sizeof (T) % 8 == 0) return (size_t) (aVarCount * 8) / sizeof (T); if (sizeof (T) % 4 == 0) return (size_t) (aVarCount * 4) / sizeof (T); if (sizeof (T) % 2 == 0) return (size_t) (aVarCount * 2) / sizeof (T); return (size_t) aVarCount / sizeof (T); } static void Copy (T aFrom, T &aTo) { aTo = aFrom; } }; template struct SafeArrayTraits { // Arbitrary pointer types are not supported }; /* Although the generic SafeArrayTraits template would work for all integers, * we specialize it for some of them in order to use the correct VT_ type */ template<> struct SafeArrayTraits : public SafeArrayTraitsBase { protected: static VARTYPE VarType() { return VT_I4; } static ULONG VarCount (size_t aSize) { return (ULONG) aSize; } static size_t Size (ULONG aVarCount) { return (size_t) aVarCount; } static void Copy (LONG aFrom, LONG &aTo) { aTo = aFrom; } }; template<> struct SafeArrayTraits : public SafeArrayTraitsBase { protected: static VARTYPE VarType() { return VT_UI4; } static ULONG VarCount (size_t aSize) { return (ULONG) aSize; } static size_t Size (ULONG aVarCount) { return (size_t) aVarCount; } static void Copy (ULONG aFrom, ULONG &aTo) { aTo = aFrom; } }; template<> struct SafeArrayTraits : public SafeArrayTraitsBase { protected: static VARTYPE VarType() { return VT_I8; } static ULONG VarCount (size_t aSize) { return (ULONG) aSize; } static size_t Size (ULONG aVarCount) { return (size_t) aVarCount; } static void Copy (LONG64 aFrom, LONG64 &aTo) { aTo = aFrom; } }; template<> struct SafeArrayTraits : public SafeArrayTraitsBase { protected: static VARTYPE VarType() { return VT_UI8; } static ULONG VarCount (size_t aSize) { return (ULONG) aSize; } static size_t Size (ULONG aVarCount) { return (size_t) aVarCount; } static void Copy (ULONG64 aFrom, ULONG64 &aTo) { aTo = aFrom; } }; template<> struct SafeArrayTraits : public SafeArrayTraitsBase { protected: static VARTYPE VarType() { return VT_BSTR; } static ULONG VarCount (size_t aSize) { return (ULONG) aSize; } static size_t Size (ULONG aVarCount) { return (size_t) aVarCount; } static void Copy (BSTR aFrom, BSTR &aTo) { aTo = aFrom ? ::SysAllocString ((const OLECHAR *) aFrom) : NULL; } }; template<> struct SafeArrayTraits : public SafeArrayTraitsBase { protected: /* Use the 64-bit unsigned integer type for GUID */ static VARTYPE VarType() { return VT_UI8; } /* GUID is 128 bit, so we need two VT_UI8 */ static ULONG VarCount (size_t aSize) { AssertCompileSize (GUID, 16); return (ULONG) (aSize * 2); } static size_t Size (ULONG aVarCount) { return (size_t) aVarCount / 2; } static void Copy (GUID aFrom, GUID &aTo) { aTo = aFrom; } }; /** * Helper for SafeArray::__asOutParam() that automatically updates m.raw after a * non-NULL m.arr assignment. */ class OutSafeArrayDipper { OutSafeArrayDipper (SAFEARRAY **aArr, void **aRaw) : arr (aArr), raw (aRaw) { Assert (*aArr == NULL && *aRaw == NULL); } SAFEARRAY **arr; void **raw; template friend class SafeArray; public: ~OutSafeArrayDipper() { if (*arr != NULL) { HRESULT rc = SafeArrayAccessData (*arr, raw); AssertComRC (rc); } } operator SAFEARRAY **() { return arr; } }; #endif /* defined (VBOX_WITH_XPCOM) */ //////////////////////////////////////////////////////////////////////////////// /** * The SafeArray class represents the safe array type used in COM to pass arrays * to/from interface methods. * * This helper class hides all MSCOM/XPCOM specific implementation details and, * together with ComSafeArrayIn, ComSafeArrayOut and ComSafeArrayRet macros, * provides a platform-neutral way to handle safe arrays in the method * implementation. * * When an instance of this class is destroyed, it automatically frees all * resources occupied by individual elements of the array as well as by the * array itself. However, when the value of an element is manually changed * using #operator[] or by accessing array data through the #raw() pointer, it is * the caller's responsibility to free resources occupied by the previous * element's value. * * Also, objects of this class do not support copy and assignment operations and * therefore cannot be returned from functions by value. In other words, this * class is just a temporary storage for handling interface method calls and not * intended to be used to store arrays as data members and such -- you should * use normal list/vector classes for that. * * @note The current implementation supports only one-dimensional arrays. * * @note This class is not thread-safe. */ template > class SafeArray : public Traits { public: /** * Creates a null array. */ SafeArray() {} /** * Creates a new array of the given size. All elements of the newly created * array initialized with null values. * * @param aSize Initial number of elements in the array. * * @note If this object remains null after construction it means that there * was not enough memory for creating an array of the requested size. * The constructor will also assert in this case. */ SafeArray (size_t aSize) { resize (aSize); } /** * Weakly attaches this instance to the existing array passed in a method * parameter declared using the ComSafeArrayIn macro. When using this call, * always wrap the parameter name in the ComSafeArrayInArg macro call like * this: *
     *  SafeArray safeArray (ComSafeArrayInArg (aArg));
     * 
* * Note that this constructor doesn't take the ownership of the array. In * particular, it means that operations that operate on the ownership (e.g. * #detachTo()) are forbidden and will assert. * * @param aArg Input method parameter to attach to. */ SafeArray (ComSafeArrayIn (T, aArg)) { #if defined (VBOX_WITH_XPCOM) AssertReturnVoid (aArg != NULL); m.size = aArgSize; m.arr = aArg; m.isWeak = true; #else /* defined (VBOX_WITH_XPCOM) */ AssertReturnVoid (aArg != NULL); SAFEARRAY *arg = *aArg; if (arg) { AssertReturnVoid (arg->cDims == 1); VARTYPE vt; HRESULT rc = SafeArrayGetVartype (arg, &vt); AssertComRCReturnVoid (rc); AssertMsgReturnVoid (vt == VarType(), ("Expected vartype %d, got %d.\n", VarType(), vt)); rc = SafeArrayAccessData (arg, (void HUGEP **) &m.raw); AssertComRCReturnVoid (rc); } m.arr = arg; m.isWeak = true; #endif /* defined (VBOX_WITH_XPCOM) */ } /** * Creates a deep copy of the given standard C++ container that stores * T objects. * * @param aCntr Container object to copy. * * @param C Standard C++ container template class (normally deduced from * @c aCntr). */ template