Improve SAFEARRAY support in wxMSW OLE Automation code.

Add a new wxSafeArray<> class wrapping SAFEARRAY.

Also add support for converting VARIANTs containing other, previously
unsupported, standard types.

Closes #14637.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72543 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2012-09-23 22:49:50 +00:00
parent 2abce4af22
commit 226fa6db3d
18 changed files with 989 additions and 159 deletions

View File

@@ -48,6 +48,7 @@
#endif
#include "wx/msw/ole/oleutils.h"
#include "wx/msw/ole/safearray.h"
#if defined(__VISUALC__) && (__VISUALC__ > 1000)
#include <docobj.h>
@@ -131,96 +132,6 @@ wxBasicString::~wxBasicString()
#if wxUSE_VARIANT
namespace
{
// Helper class for creating and filling SAFEARRAY. To use it, call Create()
// first, then SetElement() for each element and finally Detach() the SAFEARRAY
// from it if you don't want it to be deleted when this class is.
class wxSafeArrayHelper
{
public:
wxSafeArrayHelper();
~wxSafeArrayHelper();
bool Create(VARTYPE vt, long count); // creates and locks the array
bool SetElement(size_t index, const wxVariant& variant);
bool SetElement(size_t index, const wxString& str);
SAFEARRAY* Detach(); // unlocks the array and gives up its ownership
private:
void Unlock();
SAFEARRAY* m_array;
};
wxSafeArrayHelper::wxSafeArrayHelper()
{
m_array = NULL;
}
wxSafeArrayHelper::~wxSafeArrayHelper()
{
if ( m_array )
{
Unlock();
SafeArrayDestroy(m_array);
}
}
bool wxSafeArrayHelper::Create(VARTYPE vt, long count)
{
SAFEARRAYBOUND saBound;
saBound.lLbound = 0;
saBound.cElements = count;
m_array = SafeArrayCreate(vt, 1, &saBound);
if ( !m_array )
return false;
return SUCCEEDED( SafeArrayLock(m_array) );
}
bool wxSafeArrayHelper::SetElement(size_t index, const wxVariant& variant)
{
VARIANT* data = (VARIANT*)m_array->pvData;
return wxConvertVariantToOle(variant, data[index]);
}
bool wxSafeArrayHelper::SetElement(size_t index, const wxString& str)
{
BSTR bstr = wxConvertStringToOle(str);
if ( !bstr && !str.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;
}
BSTR* data = (BSTR*)m_array->pvData;
data[index] = bstr;
return true;
}
SAFEARRAY* wxSafeArrayHelper::Detach()
{
Unlock();
SAFEARRAY* result = m_array;
m_array = NULL;
return result;
}
void wxSafeArrayHelper::Unlock()
{
if ( m_array )
SafeArrayUnlock(m_array);
}
} // unnamed namespace
// ----------------------------------------------------------------------------
// wxVariantDataCurrency
// ----------------------------------------------------------------------------
@@ -323,6 +234,52 @@ bool wxVariantDataErrorCode::Write(wxString& str) const
}
// ----------------------------------------------------------------------------
// wxVariantDataSafeArray
// ----------------------------------------------------------------------------
#if wxUSE_ANY
bool wxVariantDataSafeArray::GetAsAny(wxAny* any) const
{
*any = m_value;
return true;
}
wxVariantData* wxVariantDataSafeArray::VariantDataFactory(const wxAny& any)
{
return new wxVariantDataSafeArray(wxANY_AS(any, SAFEARRAY*));
}
REGISTER_WXANY_CONVERSION(SAFEARRAY*, wxVariantDataSafeArray)
#endif // wxUSE_ANY
bool wxVariantDataSafeArray::Eq(wxVariantData& data) const
{
wxASSERT_MSG( (data.GetType() == wxS("safearray")),
"wxVariantDataSafeArray::Eq: argument mismatch" );
wxVariantDataSafeArray& otherData = (wxVariantDataSafeArray&) data;
return otherData.m_value == m_value;
}
#if wxUSE_STD_IOSTREAM
bool wxVariantDataSafeArray::Write(wxSTD ostream& str) const
{
wxString s;
Write(s);
str << s;
return true;
}
#endif
bool wxVariantDataSafeArray::Write(wxString& str) const
{
str.Printf(wxS("SAFEARRAY: %p"), (void*)m_value);
return true;
}
WXDLLEXPORT bool wxConvertVariantToOle(const wxVariant& variant, VARIANTARG& oleVariant)
{
@@ -351,6 +308,25 @@ WXDLLEXPORT bool wxConvertVariantToOle(const wxVariant& variant, VARIANTARG& ole
oleVariant.vt = VT_CY;
oleVariant.cyVal = c->GetValue();
}
else if (type == wxT("safearray"))
{
wxVariantDataSafeArray* const
vsa = wxStaticCastVariantData(variant.GetData(),
wxVariantDataSafeArray);
SAFEARRAY* psa = vsa->GetValue();
VARTYPE vt;
wxCHECK(psa, false);
HRESULT hr = SafeArrayGetVartype(psa, &vt);
if ( FAILED(hr) )
{
wxLogApiError(wxS("SafeArrayGetVartype()"), hr);
SafeArrayDestroy(psa);
return false;
}
oleVariant.vt = vt | VT_ARRAY;
oleVariant.parray = psa;
}
else if (type == wxT("long"))
{
oleVariant.vt = VT_I4;
@@ -409,36 +385,22 @@ WXDLLEXPORT bool wxConvertVariantToOle(const wxVariant& variant, VARIANTARG& ole
}
else if (type == wxT("list"))
{
wxSafeArrayHelper sah;
if (!sah.Create(VT_VARIANT, variant.GetCount()))
wxSafeArray<VT_VARIANT> safeArray;
if (!safeArray.CreateFromListVariant(variant))
return false;
for (size_t i = 0; i < variant.GetCount(); i++)
{
if (!sah.SetElement(i, variant[i]))
return false;
}
oleVariant.vt = VT_VARIANT | VT_ARRAY;
oleVariant.parray = sah.Detach();
oleVariant.parray = safeArray.Detach();
}
else if (type == wxT("arrstring"))
{
wxArrayString strings(variant.GetArrayString());
wxSafeArrayHelper sah;
wxSafeArray<VT_BSTR> safeArray;
if (!sah.Create(VT_BSTR, strings.GetCount()))
if (!safeArray.CreateFromArrayString(variant.GetArrayString()))
return false;
for (size_t i = 0; i < strings.GetCount(); i++)
{
if (!sah.SetElement(i, strings[i]))
return false;
}
oleVariant.vt = VT_BSTR | VT_ARRAY;
oleVariant.parray = sah.Detach();
oleVariant.parray = safeArray.Detach();
}
else
{
@@ -458,63 +420,53 @@ wxConvertOleToVariant(const VARIANTARG& oleVariant, wxVariant& variant)
bool ok = true;
if ( oleVariant.vt & VT_ARRAY )
{
// Compute the total number of elements in all array dimensions
int cElements = 1;
for ( int cDims = 0; cDims < oleVariant.parray->cDims; cDims++ )
cElements *= oleVariant.parray->rgsabound[cDims].cElements;
// Get a pointer to the data
void* pvdata;
HRESULT hr = SafeArrayAccessData(oleVariant.parray, &pvdata);
if ( FAILED(hr) )
return false;
// TODO: We currently return arrays as wxVariant of the list type
// containing the flattened form of array but we should allow
// getting it as wxVariantDataSafeArray instead. Doing this is
// simple, we'd just need to do something like this:
//
// if ( oleVariant.parray && SafeArrayGetDim(oleVariant.parray) > 1 )
// {
// variant.SetData(new wxVariantDataSafeArray(oleVariant.parray));
// }
//
// but currently we don't do it for compatibility reasons.
switch (oleVariant.vt & VT_TYPEMASK)
{
case VT_VARIANT:
{
variant.ClearList();
VARIANTARG *variant_data=(VARIANTARG*)pvdata;
for ( int i = 0; i < cElements; i++ )
{
VARIANTARG& oleElement = variant_data[i];
wxVariant vElement;
if ( !wxConvertOleToVariant(oleElement, vElement) )
{
ok = false;
variant.ClearList();
break;
}
variant.Append(vElement);
}
}
case VT_I2:
ok = wxSafeArray<VT_I2>::ConvertToVariant(oleVariant.parray, variant);
break;
case VT_I4:
ok = wxSafeArray<VT_I4>::ConvertToVariant(oleVariant.parray, variant);
break;
case VT_R4:
ok = wxSafeArray<VT_R4>::ConvertToVariant(oleVariant.parray, variant);
break;
case VT_R8:
ok = wxSafeArray<VT_R8>::ConvertToVariant(oleVariant.parray, variant);
break;
case VT_VARIANT:
ok = wxSafeArray<VT_VARIANT>::ConvertToVariant(oleVariant.parray, variant);
break;
case VT_BSTR:
{
wxArrayString strings;
BSTR *string_val=(BSTR*)pvdata;
for ( int i = 0; i < cElements; ++i )
{
wxString str=wxConvertStringFromOle(*string_val);
strings.Add(str);
++string_val;
}
variant=strings;
if ( wxSafeArray<VT_BSTR>::ConvertToArrayString(oleVariant.parray, strings) )
variant = strings;
else
ok = false;
}
break;
default:
wxLogDebug(wxT("unhandled VT_ARRAY type %x in wxConvertOleToVariant"),
oleVariant.vt & VT_TYPEMASK);
variant = wxVariant();
ok = false;
break;
}
SafeArrayUnaccessData(oleVariant.parray);
if ( !ok )
{
wxLogDebug(wxT("unhandled VT_ARRAY type %x in wxConvertOleToVariant"),
oleVariant.vt & VT_TYPEMASK);
variant = wxVariant();
}
}
else if ( oleVariant.vt & VT_BYREF )
{