/////////////////////////////////////////////////////////////////////////////// // Name: msw/ole/safearray.h // Purpose: Helpers for working with OLE SAFEARRAYs. // Author: PB // Created: 2012-09-23 // Copyright: (c) 2012 wxWidgets development team // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// #ifndef _MSW_OLE_SAFEARRAY_H_ #define _MSW_OLE_SAFEARRAY_H_ #include "wx/msw/ole/oleutils.h" #if wxUSE_OLE && wxUSE_VARIANT /* wxSafeArray is wxWidgets wrapper for working with MS Windows SAFEARRAYs. It also has convenience functions for converting between SAFEARRAY and wxVariant with list type or wxArrayString. */ // The base class with type-independent methods. It exists solely in order to // reduce the template bloat. class WXDLLIMPEXP_CORE wxSafeArrayBase { public: // If owns a SAFEARRAY, it's unlocked and destroyed. virtual ~wxSafeArrayBase() { Destroy(); } // Unlocks and destroys the owned SAFEARRAY. void Destroy(); // Unlocks the owned SAFEARRAY, returns it and gives up its ownership. SAFEARRAY* Detach(); // Returns true if has a valid SAFEARRAY. bool HasArray() const { return m_array != NULL; } // Returns the number of dimensions. size_t GetDim() const; // Returns lower bound for dimension dim in bound. Dimensions start at 1. bool GetLBound(size_t dim, long& bound) const; // Returns upper bound for dimension dim in bound. Dimensions start at 1. bool GetUBound(size_t dim, long& bound) const; // Returns element count for dimension dim. Dimensions start at 1. size_t GetCount(size_t dim) const; protected: // Default constructor, protected so the class can't be used on its own, // it's only used as a base class of wxSafeArray<>. wxSafeArrayBase() { m_array = NULL; } bool Lock(); bool Unlock(); SAFEARRAY* m_array; }; // wxSafeArrayConvertor<> must be specialized for the type in order to allow // using it with wxSafeArray<>. // // We specialize it below for the standard types. template <VARTYPE varType> struct wxSafeArrayConvertor {}; /** Macro for specializing wxSafeArrayConvertor for simple types. The template parameters are: - externType: basic C data type, e.g. wxFloat64 or wxInt32 - varType: corresponding VARIANT type constant, e.g. VT_R8 or VT_I4. */ #define wxSPECIALIZE_WXSAFEARRAY_CONVERTOR_SIMPLE(externType, varType) \ template <> \ struct wxSafeArrayConvertor<varType> \ { \ typedef externType externT; \ typedef externT internT; \ static bool ToArray(const externT& from, internT& to) \ { \ to = from; \ return true; \ } \ static bool FromArray(const internT& from, externT& to) \ { \ to = from; \ return true; \ } \ } wxSPECIALIZE_WXSAFEARRAY_CONVERTOR_SIMPLE(wxInt16, VT_I2); wxSPECIALIZE_WXSAFEARRAY_CONVERTOR_SIMPLE(wxInt32, VT_I4); wxSPECIALIZE_WXSAFEARRAY_CONVERTOR_SIMPLE(wxFloat32, VT_R4); wxSPECIALIZE_WXSAFEARRAY_CONVERTOR_SIMPLE(wxFloat64, VT_R8); // Specialization for VT_BSTR using wxString. template <> struct wxSafeArrayConvertor<VT_BSTR> { typedef wxString externT; typedef BSTR internT; static bool ToArray(const wxString& from, BSTR& to) { BSTR bstr = wxConvertStringToOle(from); if ( !bstr && !from.empty() ) { // BSTR can be NULL for empty strings but if the string was // not empty, it means we failed to allocate memory for it. return false; } to = bstr; return true; } static bool FromArray(const BSTR from, wxString& to) { to = wxConvertStringFromOle(from); return true; } }; // Specialization for VT_VARIANT using wxVariant. template <> struct wxSafeArrayConvertor<VT_VARIANT> { typedef wxVariant externT; typedef VARIANT internT; static bool ToArray(const wxVariant& from, VARIANT& to) { return wxConvertVariantToOle(from, to); } static bool FromArray(const VARIANT& from, wxVariant& to) { return wxConvertOleToVariant(from, to); } }; template <VARTYPE varType> class wxSafeArray : public wxSafeArrayBase { public: typedef wxSafeArrayConvertor<varType> Convertor; typedef typename Convertor::internT internT; typedef typename Convertor::externT externT; // Default constructor. wxSafeArray() { m_array = NULL; } // Creates and locks a zero-based one-dimensional SAFEARRAY with the given // number of elements. bool Create(size_t count) { SAFEARRAYBOUND bound; bound.lLbound = 0; bound.cElements = count; return Create(&bound, 1); } // Creates and locks a SAFEARRAY. See SafeArrayCreate() in MSDN // documentation for more information. bool Create(SAFEARRAYBOUND* bound, size_t dimensions) { wxCHECK_MSG( !m_array, false, wxS("Can't be created twice") ); m_array = SafeArrayCreate(varType, dimensions, bound); if ( !m_array ) return false; return Lock(); } /** Creates a 0-based one-dimensional SAFEARRAY from wxVariant with the list type. Can be called only for wxSafeArray<VT_VARIANT>. */ bool CreateFromListVariant(const wxVariant& variant) { wxCHECK(varType == VT_VARIANT, false); wxCHECK(variant.GetType() == wxS("list"), false); if ( !Create(variant.GetCount()) ) return false; VARIANT* data = static_cast<VARIANT*>(m_array->pvData); for ( size_t i = 0; i < variant.GetCount(); i++) { if ( !Convertor::ToArray(variant[i], data[i]) ) return false; } return true; } /** Creates a 0-based one-dimensional SAFEARRAY from wxArrayString. Can be called only for wxSafeArray<VT_BSTR>. */ bool CreateFromArrayString(const wxArrayString& strings) { wxCHECK(varType == VT_BSTR, false); if ( !Create(strings.size()) ) return false; BSTR* data = static_cast<BSTR*>(m_array->pvData); for ( size_t i = 0; i < strings.size(); i++ ) { if ( !Convertor::ToArray(strings[i], data[i]) ) return false; } return true; } /** Attaches and locks an existing SAFEARRAY. The array must have the same VARTYPE as this wxSafeArray was instantiated with. */ bool Attach(SAFEARRAY* array) { wxCHECK_MSG(!m_array && array, false, wxS("Can only attach a valid array to an uninitialized one") ); VARTYPE vt; HRESULT hr = SafeArrayGetVartype(array, &vt); if ( FAILED(hr) ) { wxLogApiError(wxS("SafeArrayGetVarType()"), hr); return false; } wxCHECK_MSG(vt == varType, false, wxS("Attaching array of invalid type")); m_array = array; return Lock(); } /** Indices have the same row-column order as rgIndices in SafeArrayPutElement(), i.e. they follow BASIC rules, NOT C ones. */ bool SetElement(long* indices, const externT& element) { wxCHECK_MSG( m_array, false, wxS("Uninitialized array") ); wxCHECK_MSG( indices, false, wxS("Invalid index") ); internT* data; if ( FAILED( SafeArrayPtrOfIndex(m_array, (LONG *)indices, (void**)&data) ) ) return false; return Convertor::ToArray(element, *data); } /** Indices have the same row-column order as rgIndices in SafeArrayPutElement(), i.e. they follow BASIC rules, NOT C ones. */ bool GetElement(long* indices, externT& element) const { wxCHECK_MSG( m_array, false, wxS("Uninitialized array") ); wxCHECK_MSG( indices, false, wxS("Invalid index") ); internT* data; if ( FAILED( SafeArrayPtrOfIndex(m_array, (LONG *)indices, (void**)&data) ) ) return false; return Convertor::FromArray(*data, element); } /** Converts the array to a wxVariant with the list type, regardless of the underlying SAFEARRAY type. If the array is multidimensional, it is flattened using the algorithm originally employed in wxConvertOleToVariant(). */ bool ConvertToVariant(wxVariant& variant) const { wxCHECK_MSG( m_array, false, wxS("Uninitialized array") ); size_t dims = m_array->cDims; size_t count = 1; for ( size_t i = 0; i < dims; i++ ) count *= m_array->rgsabound[i].cElements; const internT* data = static_cast<const internT*>(m_array->pvData); externT element; variant.ClearList(); for ( size_t i1 = 0; i1 < count; i1++ ) { if ( !Convertor::FromArray(data[i1], element) ) { variant.ClearList(); return false; } variant.Append(element); } return true; } /** Converts an array to an ArrayString. Can be called only for wxSafeArray<VT_BSTR>. If the array is multidimensional, it is flattened using the algorithm originally employed in wxConvertOleToVariant(). */ bool ConvertToArrayString(wxArrayString& strings) const { wxCHECK_MSG( m_array, false, wxS("Uninitialized array") ); wxCHECK(varType == VT_BSTR, false); size_t dims = m_array->cDims; size_t count = 1; for ( size_t i = 0; i < dims; i++ ) count *= m_array->rgsabound[i].cElements; const BSTR* data = static_cast<const BSTR*>(m_array->pvData); wxString element; strings.clear(); strings.reserve(count); for ( size_t i1 = 0; i1 < count; i1++ ) { if ( !Convertor::FromArray(data[i1], element) ) { strings.clear(); return false; } strings.push_back(element); } return true; } static bool ConvertToVariant(SAFEARRAY* psa, wxVariant& variant) { wxSafeArray<varType> sa; bool result = false; if ( sa.Attach(psa) ) result = sa.ConvertToVariant(variant); if ( sa.HasArray() ) sa.Detach(); return result; } static bool ConvertToArrayString(SAFEARRAY* psa, wxArrayString& strings) { wxSafeArray<varType> sa; bool result = false; if ( sa.Attach(psa) ) result = sa.ConvertToArrayString(strings); if ( sa.HasArray() ) sa.Detach(); return result; } wxDECLARE_NO_COPY_TEMPLATE_CLASS(wxSafeArray, varType); }; #endif // wxUSE_OLE && wxUSE_VARIANT #endif // _MSW_OLE_SAFEARRAY_H_