SAFEARRAY of IUnknowns

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

SAFEARRAY of IUnknowns

Maxim Fridental
How should one properly create a SAFEARRAY of IUnknowns having an array of
COM interfaces?
When I try the most apparent solution such as:

    iface := IUnknown createObject: 'Scripting.FileSystemObject'.
    array := Array with: iface.
    safearray := array asSAFEARRAY

the safearray gets the vartype VT_VARIANT, and I need it to be VT_UNKNOWN.
The SAFEARRAY class has many other constructor methods, but none of them
could create what I want (I know obviously not so much about the
safearrays).
I've found finally a working solution, but I'm afraid it works only
accidentally. Could someone please comment on that:

withAllComInterfaces: aSequenceableCollection
 | result |
 result := SAFEARRAY
    length: aSequenceableCollection size
    vt: IUnknown vt
    elementClass: IUnknown.
 1 to: aSequenceableCollection size
  do:
   [:i |
   result elementAt: (Array with: i + result start - 1)    "the normal
#at:put: fails"
    put: ((aSequenceableCollection at: i) queryInterface: IUnknown)].
 ^result


Reply | Threaded
Open this post in threaded view
|

Re: SAFEARRAY of IUnknowns

Blair McGlashan
Maxim

You wrote in message news:bbkfmj$a45qj$[hidden email]...
> How should one properly create a SAFEARRAY of IUnknowns having an array of
> COM interfaces?
> When I try the most apparent solution such as:
>
>     iface := IUnknown createObject: 'Scripting.FileSystemObject'.
>     array := Array with: iface.
>     safearray := array asSAFEARRAY
>
> the safearray gets the vartype VT_VARIANT, and I need it to be VT_UNKNOWN.

Given that a Smalltalk Array may be of heterogenous content, the only
comparable form of SAFEARRAY is an array of VARIANT, hence this is the
default conversion.

> The SAFEARRAY class has many other constructor methods, but none of them
> could create what I want (I know obviously not so much about the
> safearrays).
> I've found finally a working solution, but I'm afraid it works only
> accidentally. Could someone please comment on that:
>
> withAllComInterfaces: aSequenceableCollection
>  | result |
>  result := SAFEARRAY
>     length: aSequenceableCollection size
>     vt: IUnknown vt
>     elementClass: IUnknown.
>  1 to: aSequenceableCollection size
>   do:
>    [:i |
>    result elementAt: (Array with: i + result start - 1)    "the normal
> #at:put: fails"
>     put: ((aSequenceableCollection at: i) queryInterface: IUnknown)].
>  ^result

This will actually work fine. I consider the failure of #at:put: when used
with COM interface objects to be a bug, which I've recorded as #1285. A fix
is below. With that fix installed a slightly simpler form would be:

array := SAFEARRAY length: aSequenceableCollection size vt: IUnknown vt.
aSequenceableCollection keysAndValuesDo: [:i :each | array at: i put: each].

However it is worth bearing in mind that SAFEARRAYs can actually be strongly
typed to a particular interface (not just generic IUnknown or IDispatch). A
SAFEARRAY of this form can (with the patch) be constructed as in the
following example:

randoms := (1 to: 10) collect: [:i | COMRandomStream new].
array := SAFEARRAY length: randoms size interfaceClass: IRandomStream.
randoms keysAndValuesDo: [:i :each | each put_Seed: i*9973. array at: i put:
each].
array collect: [:each | each next]

Regards

Blair
-----------------
!ExternalStructure class methodsFor!

fromObject: anObject
 "Answer an instance of the receiver whose value is the argument."

 ^(self newBuffer)
  value: anObject;
  yourself! !
!ExternalStructure class categoriesFor: #fromObject:!instance
creation!public! !

!COMInterface class methodsFor!

fromObject: anObject
 "Answer an instance of the receiver whose value is the argument."

 ^anObject queryInterface: self! !
!COMInterface class categoriesFor: #fromObject:!instance creation!public! !

!BSTR class methodsFor!

fromObject: anObject
 ^self fromString: anObject! !
!BSTR class categoriesFor: #fromObject:!instance creation!public! !

!SAFEARRAY methodsFor!

at: index put: value
 "Set the value of the element of the receiver at the specified <integer>
index
 to the specified value, which must be representable by the element class of
 the receiver. Assumes that the receiver is a single-dimensioned array, if
 not an error will result. .
 Note: This isn't likely to be terribly fast!!"

 self elementAt: (Array with: index + self start - 1) put: (self
elementClass fromObject: value).
 ^value! !
!SAFEARRAY categoriesFor: #at:put:!accessing!public! !