4

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

4
  • I don't think data should be an array of Variant, I think it should be a scalar Variant (which then ends up holding an array). You're pulling a switcheroo on VB by changing the element type from Variant to (something else). Commented Nov 18, 2022 at 20:52
  • I'd be a little nervous about accessing anything on pVal as existing when it has out in the IDL. It might or might not be safe. Commented Nov 18, 2022 at 20:55
  • And I would stay away from any item types which don't have a native VB type. I wouldn't trust VB to do the right thing with anything unsigned, and I don't remember if there is a byte-sized VB integer type. I think signed I2 or I4 should be safe. Commented Nov 18, 2022 at 20:56
  • @Craig Thanks for those tips. That was scrubbed for this question. The actual data is 32 bit ints which will be validated by the end product. VB was just my test tool to validate the COM service worked. And hopefully learn something. Commented Nov 19, 2022 at 20:38

1 Answer 1

6

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 :-)

Sign up to request clarification or add additional context in comments.

1 Comment

Thank. I had to examine your reply closer, you should have said "Leave off the parenthesis from the Variant declaration" That was a variation I hadn't tried. Also, thanks for the tip on returning the array, I was hoping to do something like that as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.