Home > database >  How to pass an array from COM back to Visual Basic?
How to pass an array from COM back to Visual Basic?

Time:11-20

This should be simple. But I'm not finding a solution. Maybe because it's so old (VB and COM)

I am trying to return a small array of integers (or even just bytes as I've tried in this example for the question) a COM registered in-proc DLL written in C using ATL, to a VB6 application.

I sure found a lot of information about going the other way.

As I understand it, a VARIANT is the solution, since just a C based array passed back to the VB app would have to be marshalled and needs to have the size known.

I have the advantage that I am developing the COM object, as well as testing it against a VB app. Although my skill with VB is next to zero.

A dozens of other calls work as expected, sending data into, and getting integers or BSTR's out of the COM DLL, and using them in VB. However, this array of raw values is not working.

First I thought I was not coding the VB correctly, because I know very little about VB. Then I thought it must be the C code not creating the VARIANT array correctly. Now I don't know what to think.

My results are that the call in VB returns, and the VB Watch windows shows the VARIANT but no contents (literally blank, not described as 'empty' or 'null' or anything). (Why VB insists on capitalizing the first letter of the variable is anybody's guess...)

enter image description here

When I try to access an index from the VARINAT, it fails with index out of bounds.

I tried "ReDim"ing it, and got more VARIANTS, all say "empty". But at least they do tell me 'empty'.

So it appears the VARIANT coming back from the DLL doesn't have anything in it.

I have tried several VT_'s from the OLE header, and either get the empty VARIANT, or get a failure that the "automation type is not supported".

The C code in the COM server is below (scrubbed for posting). I have tried types of VT_UI1, VT_UI4, VT_INT, and several others :

STDMETHODIMP CClass::getArrayData(VARIANT* pVal, long *result) {
    unsigned char arry[4];
    unsigned long count;
    unsigned char *pData;
    arry[0] = 5; arry[1] = 6; arry[2] = 7; arry[3] = 8;     //  Just throw some data in
    count = 4;
    *result = count;

    pVal->vt = VT_ARRAY |VT_UI1;                            //   A VT of type unsigned bytes array
    pVal->parray = SafeArrayCreateVector(VT_UI1,0,count);   //  Create 4 elements
    SafeArrayAccessData(pVal->parray, (void**) &pData);     //  Lock the memory for access
    memcpy(pData, arry, count * sizeof(unsigned char));     //  Copy the bytes in
    SafeArrayUnaccessData(pVal->parray);                    //  Release it
    return S_OK;                                            //  And we're happy...
}

The IDL entry for this is

[id(13), helpstring("method getArrayData")] HRESULT getArrayData([out] VARIANT* pVal, [out, retval] long * result);

The VB code is:

Private Sub getdata_Click()
    Dim result As Integer
    Dim data() As Variant
    Dim value As Integer
    Dim strvalue As String

    result = mInterface.getArrayData(data)
    value = data(1)
    strvalue = CStr(value)
    ListBox.AddItem (" "   strvalue)
End Sub

The VB app does get a return value of 4, so I know that much is working. But the array has nothing in it.

I am out of ideas. The goal here is to return a very small array of values to VB, so the VB app can access them using indexing: data(0), data(1), data(2) ....

I really don't want to write a dozen or so functions to pull the values back one at a time.

Any help TIA.

Scotty

CodePudding user response:

You were not far from the solution. With your getArrayData definition, you can simply call it like this from VB:

Dim b As Variant
Dim result As Integer
result = mInterface.getArrayData(b)

However, you can also define it as a property in .idl, like this:

[id(2), propget] // note propget
HRESULT ArrayData([out, retval] VARIANT* pVal);

like this in C/C (you don't need result since the array is self descriptive):

HRESULT STDMETHODCALLTYPE get_ArrayData(VARIANT* pVal) ...

and like this in VB:

Dim a As Variant
a = mInterface.ArrayData

Here are the VB watches for a & b variants:

enter image description here

PS: VB(6) is old, but COM is not, it's still used everywhere in Windows :-)

  • Related