Help needed passing an array of float values by refference.

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Help needed passing an array of float values by refference.

Christopher J. Demers
Here is the what the interface browser has (compFlowLbmol_Hr is the
problem):
=========
[id(0x00000003)]
short GetStreamByID(
short streamID,
single* tempR,
single* presPsia,
single* moleVapFrac,
single* enthBtu_Hr,
VARIANT compFlowLbmol_Hr);
==========
Here is the code that was generated
^(self invokeId: 3 withArguments:
((Array new: 6)
basicAt: 1 put: streamID;
basicAt: 2 put: tempR;
basicAt: 3 put: presPsia;
basicAt: 4 put: moleVapFrac;
basicAt: 5 put: enthBtu_Hr;
basicAt: 6 put: compFlowLbmol_Hr;
yourself))
==========
This is what I was doing in the code.
==========
flowArray := Array new: 301.
1 to: flowArray size do: [:count | flowArray at: count put: 0].
flowArray := SAFEARRAY withAll: flowArray elementClass: FLOAT.
==========

There are two parts to the interface, I send values in the array for one
function, and then get them back via another.  I was originally using the
same array for both assuming it would just overwrite the values.  I do not
get any errors when I set the values.  However I just noticed that I am not
getting the correct return values.  I just get back the same values I sent.
I figure this has something to do with variant wrapping an by ref or
something.  I did a google search and found some info, but I am still having
problems getting anything to work.  Does anyone have any ideas about the
correct way to do this?

When I try this there is no error, but there is no data either.
======
   flowArray := Array new: 301.
   1 to: flowArray size do: [:count | flowArray at: count put: 0].
   flowArray := SAFEARRAY withAll: flowArray elementClass: FLOAT.
   flowArray := flowArray asVariant.
====
Then I added this line bellow, and I now get an error "Unrecognised
HRESULT - 16rC0000005 (16r5: Unrecognised HRESULT - 16rC0000005)" at the
external call.
====
   flowArray vt: flowArray vt | (AXAutomationConstants  at: #VT_BYREF).
====

Here is the VBA code (It always seems so simple in VBA ;)):
====
Dim compFlowLbmol_Hr(0 To 300) As Single
check = streams.GetStreamByID(ID, TempR, presPsia, moleVapFrac, EnthBtu_Hr,
compFlowLbmol_Hr)
=====

Any help or suggestions would be greatly appreciated.

Chris


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Christopher J. Demers
"Christopher J. Demers" <[hidden email]> wrote in
message news:b1pivp$15od7h$[hidden email]...
>
> Here is the VBA code (It always seems so simple in VBA ;)):
> ====
> Dim compFlowLbmol_Hr(0 To 300) As Single
> check = streams.GetStreamByID(ID, TempR, presPsia, moleVapFrac,
EnthBtu_Hr,
> compFlowLbmol_Hr)
> =====

I still can't get this to work.  I have tried many variations using FLOAT,
DOUBLE, VARIANT.  Every time I try to force it to be passed by reference I
get the "Unrecognised HRESULT" error and when I don't I do not get return
values.  I also see random data for the values, but only when it is made to
be by reference.  Do I need to do some additional locking or something?
Surely there is an easy way this can be done in Dolphin?  I guess I need to
know the equivalent of compFlowLbmol_Hr(0 To 300) As Single in Dolphin, and
how to pass it by reference.

I do like the Dolphin ActiveX interface, but unfortunately there are still
these situations that just seem more complex than in VB.  I don't really
want to have to build my own VB wrapper just to make this work.

Chris


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Blair McGlashan
"Christopher J. Demers" <[hidden email]> wrote in
message news:b1srla$16hr88$[hidden email]...

> "Christopher J. Demers" <[hidden email]> wrote in
> message news:b1pivp$15od7h$[hidden email]...
> >
> > Here is the VBA code (It always seems so simple in VBA ;)):
> > ====
> > Dim compFlowLbmol_Hr(0 To 300) As Single
> > check = streams.GetStreamByID(ID, TempR, presPsia, moleVapFrac,
> EnthBtu_Hr,
> > compFlowLbmol_Hr)
> > =====
>
> I still can't get this to work.  I have tried many variations using FLOAT,
> DOUBLE, VARIANT.  Every time I try to force it to be passed by reference I
> get the "Unrecognised HRESULT" error and when I don't I do not get return
> values.  I also see random data for the values, but only when it is made
to
> be by reference.  Do I need to do some additional locking or something?
> Surely there is an easy way this can be done in Dolphin?  I guess I need
to
> know the equivalent of compFlowLbmol_Hr(0 To 300) As Single in Dolphin,
and
> how to pass it by reference.

What documentation do you have for the interface? Its not clear from the IDL
exactly how the compFlowLbmol_Hr argument should be set up, its just a
loosely typed VARIANT. That could be anything from "nil" (or the Automation
equivalent), to a String, or an array of other VARIANTs. Plainly put, there
is not enough type information in that IDL to know how to set up that
parameter correctly. Indeed if it is specified like that, then the designer
of the component has effectively said that any variant compatible value
_might_ be valid as an argument.

What IDL does Dolphin itself reverse engineer for the interface?

I think that VB(A) will pass the first parameter as a VT_I2|VT_BYREF, the
float parameters as VT_R4|VT_BYREF, and the last (array) parameter as a
VT_ARRAY|VT_R4, i.e. not VT_BYREF. This is because the array is already (in
effect) passed by reference. I don't know whether you are expecting the
component to reallocate the array completely, or modify the existing one in
place, but I would have thought if you execute something like:

    flowArray := (SAFEARRAY length: 301 elementClass: FLOAT) asVariant.
    streams getStreamByID: 1 tempR: 1.1 presPsia: 0.1 moleVapFrac: 0.2
enthBtu_Hr: 0.3
            compFlowLbmol_Hr: flowArray.
Then on exit from the GetStreamByID call, if the method has modified the
values in the array, then those modifications will be visible in the array
held by the VARIANT, flowArray. Note, however, that strictly the scalar
parameters should be passed by reference since their direction is not
declared in the IDL and they are pointers. You might find it will work for
non-reference parameters since most IDispatch implementations will coerce
things over, and it is quite likely that one or more of these parameters is
really an input parameter anyway. If you needed to pass them by reference
then you'd need to wrap them up in instances of SWORD (for the shorts) and
FLOAT. When these structures are converted to VARIANTs, the result is a
byref variant, for example:

>
> I do like the Dolphin ActiveX interface, but unfortunately there are still
> these situations that just seem more complex than in VB.  I don't really
> want to have to build my own VB wrapper just to make this work.

I'm afraid that it is somewhat inevitable. Automation (not COM) was designed
for use from VB. Active-X objects that use a lot of SAFEARRAYs and VARIANTs
in their interfaces, and especially if they implement dispinterfaces
(IDispatch only) rather than dual interfaces (IDispatch with additional
custom methods), were probably designed for use from VB and tested against
it.

VARIANT is a native type in VB. The types one can hold in a VARIANT are the
native VB representations for integers, strings, etc. SAFEARRAYs are the
native VB array type. There's certainly room for improvement in Dolphin's
use of ActiveX components, but it may not be possible to make it quite as
seamless as it is from the language into which it was designed to fit (and
vice versa). When the IDL is loose, then insufficient type information is
available to automatically perform the necessary marshalling from the
Smalltalk objects. Of course marshalling isn't necessary when the types are
already in the correct representation, and the implementor of the object
will probably then have tested against VB and made the component work with
whatever it happens to pass.

Always bear in mind that if you have a specific issue with using a COM
component, then we are able to offer more help:
http://www.object-arts.com/Support.htm

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Christopher J. Demers
"Blair McGlashan" <[hidden email]> wrote in message
news:b1tsr4$16pkvi$[hidden email]...

> What IDL does Dolphin itself reverse engineer for the interface?

The IDL is in my previous message that I replied to.  Here it is again:
[id(0x00000003)]
short GetStreamByID(
short streamID,
single* tempR,
single* presPsia,
single* moleVapFrac,
single* enthBtu_Hr,
VARIANT compFlowLbmol_Hr);

> effect) passed by reference. I don't know whether you are expecting the
> component to reallocate the array completely, or modify the existing one
in
> place, but I would have thought if you execute something like:
>
>     flowArray := (SAFEARRAY length: 301 elementClass: FLOAT) asVariant.
>     streams getStreamByID: 1 tempR: 1.1 presPsia: 0.1 moleVapFrac: 0.2
> enthBtu_Hr: 0.3
>             compFlowLbmol_Hr: flowArray.

That is one of the things I tried, it does not error, but the values are not
in flowArray.

> I'm afraid that it is somewhat inevitable. Automation (not COM) was
designed
> for use from VB. Active-X objects that use a lot of SAFEARRAYs and
VARIANTs
> in their interfaces, and especially if they implement dispinterfaces
> (IDispatch only) rather than dual interfaces (IDispatch with additional
> custom methods), were probably designed for use from VB and tested against
> it.

Yes, it seems that this component was designed with VBA in Excel in mind.
Unfortunately I don't have any control over that.  I solved my immediate
problem by writing a wrapper in VB to communicate between Dolphin and the
ActiveX object.  Not the most elegant solution, but it solves the immediate
problem.  If others are interested this is how I declared the function:

Public Function WrapGetStreamByID(cc5 As Object, ID As Integer, ByRef TempR
As Single, ByRef presPSia As Single, ByRef moleVapFrac As Single, ByRef
EnthBtu_Hr As Single, ByRef compFlowLbmol_Hr() As Single) As Integer

then I make the call from there, and it just works its VB magic and the
values get returned in Smalltalk.

> Always bear in mind that if you have a specific issue with using a COM
> component, then we are able to offer more help:
> http://www.object-arts.com/Support.htm

I will have to budget for this in future ActiveX projects.

Chris


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Blair McGlashan
"Christopher J. Demers" <[hidden email]> wrote in
message news:b1v2js$15usmv$[hidden email]...
> "Blair McGlashan" <[hidden email]> wrote in message
> news:b1tsr4$16pkvi$[hidden email]...
>
> > What IDL does Dolphin itself reverse engineer for the interface?
>
> The IDL is in my previous message that I replied to.  Here it is again:
>...

OK. Is it the same from OLEView? Also how does it get represented by the
VB(A) object browser? I just want to eliminate the possibility that the the
Dolphin TLA is not reading the typelibrary correctly, since as you'll see
below I had no problems with this declaration in a test component I built.

> >
> >     flowArray := (SAFEARRAY length: 301 elementClass: FLOAT) asVariant.
> >     streams getStreamByID: 1 tempR: 1.1 presPsia: 0.1 moleVapFrac: 0.2
> > enthBtu_Hr: 0.3
> >             compFlowLbmol_Hr: flowArray.
>
> That is one of the things I tried, it does not error, but the values are
not
> in flowArray.

I built a test Active-X in C++/ATL using that IDL, and if I call it from
Dolphin exactly like that, in order to see what VB passed in for the array.
It was as I expected passed as VT_ARRAY|VT_R4, i.e. not explicitly as a
VT_BYREF. I tried invoking it from Dolphin and on return from the method the
array contains the updated values I stored in it. I'll zip it all up and
send it to you for examination. As I say, if you require further assistance
with the actual component (they vary a great deal, so it often isn't
possible to give guidance in the abstract), then we can provide it.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Christopher J. Demers
"Blair McGlashan" <[hidden email]> wrote in message
news:b1vuru$17b5rj$[hidden email]...
> OK. Is it the same from OLEView? Also how does it get represented by the
> VB(A) object browser? I just want to eliminate the possibility that the
the
> Dolphin TLA is not reading the typelibrary correctly, since as you'll see
> below I had no problems with this declaration in a test component I built.

The OLEView tool reports the same IDL as Dolphin for the method.  This is
from the VBA Object Browser in Excel:
Function GetStreamByID(streamID As Integer, tempR As Single, presPsia As
Single, moleVapFrac As Single, enthBtu_Hr As Single, compFlowLbmol_Hr) As
Integer

> I built a test Active-X in C++/ATL using that IDL, and if I call it from
> Dolphin exactly like that, in order to see what VB passed in for the
array.
> It was as I expected passed as VT_ARRAY|VT_R4, i.e. not explicitly as a
> VT_BYREF. I tried invoking it from Dolphin and on return from the method
the
> array contains the updated values I stored in it. I'll zip it all up and
> send it to you for examination. As I say, if you require further
assistance
> with the actual component (they vary a great deal, so it often isn't
> possible to give guidance in the abstract), then we can provide it.

I looked at the example you sent to me and it works like a charm from VB and
Dolphin.  I had to remove a prerequisite from the package file and generate
the Dolphin wrapper class in order to get it to load (the global was missing
from the package).

I wonder if it is possible that the actual type library is wrong, and Visual
Basic doesn't care and is coercing things to make it work, but since Dolphin
is playing by the rules it is not.  I will explore this aspect further.  I
don't suppose you know of an easy way to check this theory?

I am afraid it would be difficult to provide the actual component.  I just
wanted to make sure I was doing things the theoretically correct way.  It
sounds like the component is doing something strange.

Thank you Blair, I really appreciate your effort.  I will let you know if I
can reproduce the problem with a component of my own.

Chris


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Christopher J. Demers
"Christopher J. Demers" <[hidden email]> wrote in
message news:b21e6b$16f2hr$1@ID-
> I wonder if it is possible that the actual type library is wrong, and
Visual
> Basic doesn't care and is coercing things to make it work, but since
Dolphin
> is playing by the rules it is not.  I will explore this aspect further.  I
> don't suppose you know of an easy way to check this theory?

I am not sure what exactly this proves, but I discovered something very
interesting:

In the component vendors VBA example when the variable that is sent the
GetStreamByID message is declared as Object it works.  However when the
variable is declared more specifically as the correct interface it returns
all 0's just like Dolphin did.  Somehow this change is causing the original
array to not be changed.  I think I can firmly say this is a problem with
the other vendors component.  I guess the type library could be wrong?  I
bet I could make the call work if I could trick Dolphin into not using the
IDL information.

I have sympathy for your support of ActiveX in Dolphin, especially when
there seem to be so many sloppy components out there.

Once again thanks for your effort.

Chris


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Blair McGlashan
"Christopher J. Demers" <[hidden email]> wrote in
message news:b21j14$1831qr$[hidden email]...
> ...
> I am not sure what exactly this proves, but I discovered something very
> interesting:
>
> In the component vendors VBA example when the variable that is sent the
> GetStreamByID message is declared as Object it works.  However when the
> variable is declared more specifically as the correct interface it returns
> all 0's just like Dolphin did.  Somehow this change is causing the
original
> array to not be changed.  I think I can firmly say this is a problem with
> the other vendors component.  I guess the type library could be wrong?  I
> bet I could make the call work if I could trick Dolphin into not using the
> IDL information.

Ah, now we're getting somewhere. I tried doing that with my dummy component,
and after fixing an error or two in its IDispatch implementation, I found
that VB passes in the array, in that case, as VT_R4|VT_ARRAY|VT_BYREF.

So, from Dolphin:

flowArray := SAFEARRAY length: 301 elementClass: FLOAT.
varArray := VARIANT new.
varArray reference: flowArray bytes basicYourAddress.
varArray vt: 24580 "VT_ARRAY|VT_R4|VT_BYREF".
streams getStreamByID: 1 tempR: 1.1 presPsia: 0.1 moleVapFrac: 0.2
enthBtu_Hr: 0.3 compFlowLbmol_Hr: varArray.
flowArray inspect.

Two points to note:
1) I discovered there is an error in the VARIANT>>array accessor method that
prevents byref arrays printing properly - patch below.
2) It helps to add a method to VARIANT to make setting up byref array
variants easier, also below. With this in place setting up the varArray
becomes the simple expression: 'varArray arrayRef: flowArray'.

Hope this gets you going.

Blair
----------------------
!VARIANT methodsFor!

array
 "Answer a SAFEARRAY from the receiver.
 Assumes that the receiver actually references an array."

 ^SAFEARRAY
  fromAddress: (self isByRef ifTrue: [self refAddress dwordAtOffset: 0]
ifFalse: [self ulVal])
  vt: self vartype
  owner: self!

arrayRef: newValue
 "Store a reference to the <SAFEARRAY> representation of the argument,
newValue,
 into the receiver, i.e. the receiver will be of type
VT_xx|VT_ARRAY|VT_BYREF."

 data := newValue asSAFEARRAY.
 self vt: data vt | VT_BYREF.
 self ulVal: data bytes basicYourAddress! !
!VARIANT categoriesFor: #array!accessing!public! !
!VARIANT categoriesFor: #arrayRef:!accessing!public! !


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Christopher J. Demers
"Blair McGlashan" <[hidden email]> wrote in message
news:b27so1$199u4f$[hidden email]...
>...
> Ah, now we're getting somewhere. I tried doing that with my dummy
component,
> and after fixing an error or two in its IDispatch implementation, I found
> that VB passes in the array, in that case, as VT_R4|VT_ARRAY|VT_BYREF.
> ...

Perfect, this code works like a charm!  You must be the white wizard of
ActiveX!  Thank you very much.  However shall I take it that the root
problem here is that the vendor supplied type library is not correct?  Their
VBA example did the same thing Dolphin did before (no values were passed
back) when the variable was explicitly declared as the correct type (not
Object).  I take this to mean their type library is not correct.  Do you
concur?

Chris


Reply | Threaded
Open this post in threaded view
|

Re: Help needed passing an array of float values by refference.

Blair McGlashan
"Christopher J. Demers" <[hidden email]> wrote in
message news:b299kj$1a5oca$[hidden email]...
> "Blair McGlashan" <[hidden email]> wrote in message
> news:b27so1$199u4f$[hidden email]...
> >...
> > Ah, now we're getting somewhere. I tried doing that with my dummy
> component,
> > and after fixing an error or two in its IDispatch implementation, I
found
> > that VB passes in the array, in that case, as VT_R4|VT_ARRAY|VT_BYREF.
> > ...
>
> Perfect, this code works like a charm!  You must be the white wizard of
> ActiveX!  Thank you very much.  However shall I take it that the root
> problem here is that the vendor supplied type library is not correct?
Their
> VBA example did the same thing Dolphin did before (no values were passed
> back) when the variable was explicitly declared as the correct type (not
> Object).  I take this to mean their type library is not correct.  Do you
> concur?

Well if they want to declare the argument like that, then clearly they need
to be less restrictive about the type of argument they are prepared to
accept, since VB(A) will pass it differently depending on whether late or
early bound. It seems they also need to tighten up on their error checking
since if the argument type was not acceptable, an error should have
resulted. Based on your example, though, I think the IDL should probably be
declaring the argument as the actual type it is, rather than as a plain
VARIANT. If that had been done, then there could have been no confusion
about what was expected, the component wouldn't need to do any explicit type
checking, and it wouldn't have been necessary for us to deduce the correct
type by finding out what VB(A) passed. It is possible they had other reasons
for declaring it as a VARIANT (perhaps one can pass other types of value
in?), but I have often found that VARIANTs are used needlessly where a more
specific type would better serve. This was particularly common in earlier
automation object models, such as many of the MS Office interfaces, where
one often finds that every method parameter is declared as a VARIANT.

Regards

Blair