395 lines
11 KiB
C
395 lines
11 KiB
C
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// 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, 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, 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 alghoritm
|
||
|
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 alghoritm 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_
|