Discussion:
tricky COM call - solved (PB9)
(too old to reply)
Wolfgang Εnzinger
2017-10-01 12:16:33 UTC
Permalink
Hello! Is there still anybody out there? ;-)

Recently I had to code a COM call in PB9 which gave me something to chew
over. It's solved now so I thought I'd share it here, for future reference
and in order to bring a little sign of life into this group.

Given is a COM library (ESRI MapObjects) which offers a class called
Points; in Microsoft's "OLE/COM Object Viewer", and narrowed to the
interesting part (calling the GetPoints() method), it looks like this:

-----------------------

[
uuid(9BD6A666-CE75-11D1-AF04-204C4F4F5020),
helpstring("Points collection indexed by number"),
helpcontext(0x000005dc)
]
coclass Points {
[default] dispinterface IMoPoints;
};

[
uuid(9BD6A665-CE75-11D1-AF04-204C4F4F5020),
helpstring("Points collection indexed by number"),
helpcontext(0x000005dc)
]
dispinterface IMoPoints {
properties:

[...]

[id(0x00000013), helpstring("Gets arrays of X, Y, Z and measure
values from a points object."), helpcontext(0x000005f4)]
void GetPoints(
[in, out] SAFEARRAY(double)* xArray,
[in, out] SAFEARRAY(double)* yArray,
[in, out, optional] SAFEARRAY(double)* zArray,
[in, out, optional] SAFEARRAY(double)*
measureArray);
[...]
};

-----------------------

In the PowerBASIC COM browser, that interface looks like this:

-----------------------

' Interface Name : IMoPoints
' Description : Points collection indexed by number
' Class Name : Points
' ClassID : $CLSID_MapObjects2_Points
' Version ProgID : $PROGID_MapObjects2_PointsPoints
Interface IDBind IMoPoints
[...]
Member Call GetPoints <19> (Out xArray As DWord<0>, _
Out yArray As DWord<1>, _
Opt Out zArray As DWord<2>, _
Opt Out measureArray As DWord<3>)
[...]
End Interface

-----------------------

Well, DWord is indeed what's being pushed onto the stack, though this lacks
the information that the DWords are actually pointers to SafeArray
structures. Which is not a problem under normal circumstances, however,
since the class offers no direct but only a dispatch interface ("Interface
IDBind"), the compiler must convert all parameters to variants, and with
the definition above, the variants will be of VARIANTVT %VT_UI4. This is
not what the GetPoints() method expects, so it will complain.

So in this case, we have to do the conversion manually; I came up with this
solution (pts is an object instance of type IMOPoints):

-----------------------
LOCAL x AS DWORD
LOCAL sax, say AS SAFEARRAY PTR
LOCAL sab AS SAFEARRAYBOUND
LOCAL px, py AS DOUBLE PTR
LOCAL pv AS VARIANTAPI PTR
LOCAL v1, v2 AS VARIANT

OBJECT CALL pts.Count TO x 'get number of coordinate pairs
sab.cElements = x
sab.cLbound = 0

sax = SafeArrayCreate(%VT_R8, 1, sab)
say = SafeArrayCreate(%VT_R8, 1, sab)

pv = VARPTR(v1)
@pv.vt = %VT_R8 OR %VT_ARRAY OR %VT_BYREF
@pv.vd.psArray = VARPTR(sax)

pv = VARPTR(v2)
@pv.vt = %VT_R8 OR %VT_ARRAY OR %VT_BYREF
@pv.vd.psArray = VARPTR(say)

OBJECT CALL pts.GetPoints(v1, v2) '<-- COM call to GetPoints()

v1 = EMPTY
v2 = EMPTY
px = @sax.pvData
py = @say.pvData
...
'at this point, we have pointers to arrays holding the X and Y coordinates
...
'when done, do not forget to release the SafeArrays:
SafeArrayDestroy say
SafeArrayDestroy sax
-----------------------

That's it. Maybe it's of service and benefit to others.

Cheers Wolfgang
Wolfgang Εnzinger
2017-10-01 14:37:23 UTC
Permalink
Post by Wolfgang Εnzinger
That's it.
Not quite. ;-)

#INCLUDE "VBAPI32.INC"

DECLARE FUNCTION SafeArrayCreate LIB "OLEAUT32.DLL" _
ALIAS "SafeArrayCreate" ( _
BYVAL VarType AS INTEGER, _
BYVAL cDims AS LONG, _
rgsabound AS SAFEARRAYBOUND) AS DWORD

DECLARE FUNCTION SafeArrayDestroy LIB "OLEAUT32.DLL" _
ALIAS "SafeArrayDestroy" ( _
BYVAL saP AS DWORD) AS DWORD

Cheers Wolfgang

Loading...