Sending and receiving arrays over COM

Sideshow Bob picture Sideshow Bob · Feb 10, 2012 · Viewed 8.3k times · Source

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.

Answer

Sideshow Bob picture Sideshow Bob · Feb 14, 2012

Got this working - my code is this (edit: though apparently not without faults - see Dietrich's answer):

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 to out
  • needed to attach to in.parray not *in.pparray (not the same thing)