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...)
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:
PS: VB(6) is old, but COM is not, it's still used everywhere in Windows :-)