/** @file * MS COM / XPCOM Abstraction Layer: * Safe array helper class declaration */ /* * Copyright (C) 2006-2007 Sun Microsystems, Inc. * * 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. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 USA or visit http://www.sun.com if you need * additional information or have any questions. */ #ifndef ___VBox_com_array_h #define ___VBox_com_array_h #include /** @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. * * In order to pass input BSTR array parameters delcared using the * ComSafeArrayIn (INPTR BSTR, aParam) macro to the SafeArray<> constructor * using the ComSafeArrayInArg() macro, you should use INPTR 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/assert.h" #include "iprt/cpputils.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 marco. * * @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 marco. * * @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) //////////////////////////////////////////////////////////////////////////////// /** * Contains various helper constants for SafeArray. */ template struct SafeArrayTraits { protected: static void Init (T &aElem) { aElem = 0; } static void Uninit (T &aElem) { aElem = 0; } 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; } }; #else /* defined (VBOX_WITH_XPCOM) */ //////////////////////////////////////////////////////////////////////////////// /** * Contains various helper constants for SafeArray. */ template struct SafeArrayTraits { // Arbitrary types are not supported }; template<> struct SafeArrayTraits { protected: static VARTYPE VarType() { return VT_I4; } static void Copy (LONG aFrom, LONG &aTo) { aTo = aFrom; } }; template<> struct SafeArrayTraits { protected: static VARTYPE VarType() { return VT_UI4; } static void Copy (ULONG aFrom, ULONG &aTo) { aTo = aFrom; } }; template<> struct SafeArrayTraits { protected: static VARTYPE VarType() { return VT_I8; } static void Copy (LONG64 aFrom, LONG64 &aTo) { aTo = aFrom; } }; template<> struct SafeArrayTraits { protected: static VARTYPE VarType() { return VT_UI8; } static void Copy (ULONG64 aFrom, ULONG64 &aTo) { aTo = aFrom; } }; template<> struct SafeArrayTraits { protected: static VARTYPE VarType() { return VT_BSTR; } static void Copy (BSTR aFrom, BSTR &aTo) { aTo = aFrom ? ::SysAllocString ((const OLECHAR *) aFrom) : NULL; } }; #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 acessing 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-dimentional 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. Must be greater * than 0. * * @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) { reset (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 ComSafeArrayOutArg 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)); } m.arr = arg; m.isWeak = true; AssertReturnVoid (accessRaw() != NULL); #endif /* defined (VBOX_WITH_XPCOM) */ } /** * Creates a deep copy of the goven standard C++ container. * * @param aCntr Container object to copy. * * @param C Standard C++ container template class (normally deduced from * @c aCntr). */ template