What is the right way to receive and send arrays over COM? Here's my attempt so far: a safearray of doubles wrapped in a variant.
//takes variant holding safearray of doubles
//returns a similar variant having multipled every element by 2
STDMETHODIMP MyComClass::safearraytimestwo(VARIANT in, VARIANT* out)
{
CComSafeArray<double> sa_in;
sa_in.Attach(*in.pparray);
ULONG size = sa_in.GetCount();
CComSafeArray<double> *out_sa = new CComSafeArray<double>(size);
for (long i=0;i<size;i++)
out_sa->SetAt(i,sa_in[i]*2);
out = new CComVariant(out_sa);
return S_OK;
}
Problems:
- currently compilation fails on the loop operation: error C2679: binary '=' : no operator found which takes a right-hand operand of type 'ATL::_ATL_AutomationType<DOUBLE>::_typewrapper' (or there is no acceptable conversion)
edit: solved using SetAt()
instead of operator[]
- Should I be declaring out_sa
on the heap? Will it get deallocated when out
gets deallocated (which I can only presume the client will do?)
Any help would be greatly appreciated!
Edit 2: here is a partial implementation that tries just to return a safearray.
STDMETHODIMP CSpatialNet::array3(VARIANT in, VARIANT* out)
{
CComSafeArray<double> out_sa;
out_sa.Create(2);
out_sa.SetAt(0,1.2);
out_sa.SetAt(1,3.4);
*out = CComVariant(out_sa);
out_sa.Detach();
return S_OK;
}
This also fails; lisp reports
(vl-load-com)
(setq n (vlax-create-object "sdnacomwrapper.SpatialNet"))
(setq v (vlax-make-variant 1.0))
(vlax-invoke-method n 'array3 v 'newvar)
; error: ActiveX Server returned an error: The parameter is incorrect
Replacing CComSafeArray<double>
with an array of variants produces the same error.
Got this working - the correct code is this:
STDMETHODIMP MyComClass::arraytimestwo(VARIANT in, VARIANT* out)
{
CComSafeArray<double> sa_in;
sa_in.Attach(in.parray);
ULONG size = sa_in.GetCount();
CComSafeArray<double> out_sa;
out_sa.Create(size);
for (long i=0;i<size;i++)
out_sa.SetAt(i,sa_in.GetAt(i)*2);
CComVariant(out_sa).Detach(out);
return S_OK;
}
And in Lisp...
(vl-load-com)
(setq n (vlax-create-object "mycomdll.MyComClass"))
(setq sa (vlax-make-safearray vlax-vbDouble '(0 . 1)))
(vlax-safearray-fill sa '(1 2))
(vlax-safearray->list sa)
(vlax-invoke-method n 'arraytimestwo sa 'newvar)
(vlax-safearray->list newvar)
Things specifically wrong with the original attempts:
- needed to use
Detach
method to assign value toout
- needed to attach to
in.parray
not*in.pparray
(not the same thing)
A COM method taking VARIANT parameters is responsible for checking arguments, for catching exceptions and it is not going to actually destroy [in]
array, so a more accurate implementation on C++ side would be:
STDMETHODIMP Foo(VARIANT in, VARIANT* out)
{
_ATLTRY
{
ATLENSURE_THROW(in.vt == (VT_ARRAY | VT_R8), E_INVALIDARG);
ATLENSURE_THROW(out, E_POINTER);
VariantInit(out);
CComSafeArray<DOUBLE>& sa_in =
reinterpret_cast<CComSafeArray<DOUBLE>&>(in.parray);
ULONG size = sa_in.GetCount();
CComSafeArray<DOUBLE> out_sa;
ATLENSURE_SUCCEEDED(out_sa.Create(size));
for(ULONG nIndex = 0; nIndex < size; nIndex++)
out_sa.SetAt(nIndex, sa_in.GetAt(nIndex) * 2);
// NOTE: Constructor copies data so it's accurate just inefficient
ATLVERIFY(SUCCEEDED(CComVariant(out_sa).Detach(out)));
}
_ATLCATCH(Exception)
{
return Exception;
}
return S_OK;
}
来源:https://stackoverflow.com/questions/9228029/sending-and-receiving-arrays-over-com