Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Sugest me: IUnknown based EventSink

6 views
Skip to first unread message

handjive

unread,
Sep 19, 2004, 1:21:25 AM9/19/04
to
Hi.

I'm trying to implement SQL-DMO wrapper classes.
SQL-DMO has some event callback, but it derrived from IUnknown.
Dolphin's EventSink mechanism is seems only focused on IDispatch
interface, so, I'm troubled with that.

Anybody suggest me!

Blair McGlashan

unread,
Sep 20, 2004, 1:37:54 PM9/20/04
to
"handjive" <jku...@st.rim.or.jp> wrote in message
news:f635137379a7012a...@localhost.talkaboutprogramming.com...

The class comment of AXEventSink does give a hint how to do this.
Essentially it comes down to implementing a COM interface (the event
interface) against an object in Dolphin.
A custom interface event sink will be significantly less complicated than
AXEventSink itself, but you'll have to implement the event interface methods
to perform the actions you need, rather than having the inbound calls
generically converted to Smalltalk events. Since the overhead of IDispatch
will be avoided, however, the solution will have much higher performance.

The easiest way to do this is to subclass COMInterfaceImp (see e.g.
http://www.object-arts.co.uk/wiki/html/Dolphin/DefiningTheSmalltalkComponentClasses.htm,
or much more detailed but as yet incomplete
http://object-arts.com/Lib/Downloads/Misc/DevelopingCOMComponents.pdf),
although you can implement COM interfaces on any Dolphin object with a
little more effort. Assuming that the event interfaces are exposed as
connection points, wiring up your implementation to the SQL-DMO object will
follow the same pattern as in AXEventSink>>connect:, i.e.

SQLDMOServerSinkImp>>connect: anIUnknown
"Connect the receiver to appropriate connection point of the the COM
object, anIUnknown.
Answer the advise cookie.
Note: If the Active-X object has been implemented using ATL, which is
common, then one of
these standard error codes may result:
0x80040200 - CONNECT_E_NOCONNECTION
This indicates that there is no such connection point on the source.
0x80040202
This indicates that the source has queried back for the sink interface
off the receiver
and received an E_NOINTERFACE response. This should not occur because of
the receiver's
implementation of #supportedInterfaces.
"

| container iidSource |
self disconnect.
container := anIUnknown queryInterface: IConnectionPointContainer.
connector := container findConnectionPoint: SQLDMOServerSink.
cookie := connector advise: (self server queryInterface: SQLDMOServerSink).
^cookie

Assuming that cookie is defined as an instance variable, and #disconnect has
been copied from AXEventSink.

I hope this gets you started.

Regards

Blair


handjive

unread,
Sep 23, 2004, 4:39:14 AM9/23/04
to
Blair

Thanks for your suggestion. I tryed, but can not breaking it.
Let me ask you point me out the mistakes?

*Make a class named "AXEventSinkDiscoverie" as subclass of
COMInterfaceImp.
*Imprement methods below:
AXEventSinkDiscoverie class>>clsid
"ID for BackupSink from typelib"
^CLSID fromString: '{10021F09-E260-11CF-AE68-00AA004A34D5}'

AXEventSinkDiscoverie>>supportedInterfaces
^Array with: SQLDMOBackupSink

AXEventSinkDiscoverie>>connect: anIUnknown
|container|


self disconnect.
container := anIUnknown queryInterface: IConnectionPointContainer.

connector := container findConnectionPoint: SQLDMOBackupSink.


cookie := connector advise: (self server queryInterface:

SQLDMOBackupSink).
^cookie

*Create instance of "_Backup" and, try to connect.
aBackup := IDispatch creteObject: 'SQLDMO.Backup'.
sink := AXEventSinkDiscoverie new.
sink connect: aBackup.

Finaly, HRESULTError said, 'Interface does not supported'...

handjive

unread,
Sep 23, 2004, 5:01:37 AM9/23/04
to
Blair

Thanks for your suggestion. I tryed, but can not breaking it.
Let me ask you point me out the mistakes?

*Make a class named "AXEventSinkDiscoverie" as subclass of
COMInterfaceImp.
*Imprement methods below:
AXEventSinkDiscoverie class>>clsid
"ID for BackupSink from typelib"
^CLSID fromString: '{10021F09-E260-11CF-AE68-00AA004A34D5}'

AXEventSinkDiscoverie>>supportedInterfaces
^Array with: SQLDMOBackupSink

AXEventSinkDiscoverie>>connect: anIUnknown
|container|


self disconnect.
container := anIUnknown queryInterface: IConnectionPointContainer.

connector := container findConnectionPoint: SQLDMOBackupSink.


cookie := connector advise: (self server queryInterface:

Blair McGlashan

unread,
Sep 23, 2004, 8:44:26 AM9/23/04
to
"handjive" <jku...@st.rim.or.jp> wrote in message
news:472d47481a2ea1e0...@localhost.talkaboutprogramming.com...

> Blair
>
> Thanks for your suggestion. I tryed, but can not breaking it.
> Let me ask you point me out the mistakes?

It was a good effort, and you're almost there. You were lucky I read your
post at the start of my lunch break - see below (or download from
ftp://object-arts.com/Lib/Downloads/Misc/SQLDMOEvents.zip). :-)

Regards

Blair

P.S. In the attached I've implemented a slightly more generic solution that
encapsulates the custom event subscription mechanism into a general class,
which I've then used in another class where I've implemented stuff specific
to this case. See SQLDMOBackupHost class>>example1. Note that it assumes
you've generated an SQLDMO package using the AX Component Wizard
-------------------------
| package |
package := Package name: 'SQLDMO Events'.
package paxVersion: 0;
basicComment: 'Example of using SQL-DMO, including sinking events.

Copyright (c) Object Arts Ltd, 2004'.

package classNames
add: #AXCustomEventSink;
add: #SQLDMOBackupHost;
yourself.

package binaryGlobalNames: (Set new
yourself).

package globalAliases: (Set new
yourself).

package allResourceNames: (Set new
yourself).

package setPrerequisites: (IdentitySet new
add: '..\..\..\Dolphin\ActiveX\Connection Points\ActiveX Connection
Points';
add: '..\..\..\Dolphin\Base\Dolphin';
add: '..\..\..\Dolphin\ActiveX\COM\OLE COM';
add: '..\..\..\..\SQLDMO';
yourself).

package!

COMInterfaceImp subclass: #AXCustomEventSink
instanceVariableNames: 'interfaceClass server connector cookie'
classVariableNames: ''
poolDictionaries: ''
classInstanceVariableNames: ''!
COMInterfaceImp subclass: #SQLDMOBackupHost
instanceVariableNames: 'backup backupSink'
classVariableNames: ''
poolDictionaries: 'SQLDMOConstants'
classInstanceVariableNames: ''!

AXCustomEventSink guid: (GUID fromString:
'{F07D81A3-9BAE-45B6-805C-1F83C206C10E}')!
AXCustomEventSink comment: 'AXCustomEventSink is a Smalltalk COM object that
provides the basic mechanism for subscribing to Active-X events through a
connection point where the event interface is not IDispatch based. You can
use it in conjunction with another Smalltalk COM object that implements the
actual interface methods (in which case the ''server'' should be set to
reference that object), or subclass it and implement the interface in that
subclass (in which case ''server'' should be initialized to reference
''self'').

See also AXEventSink.

Instance Variables:
interfaceClass <Class> - IUnknown subclass that represents the event
interface.
server <COMObject>. The implementor of the interfaceClass, possibly this
object (in a subclass)
connector <IConnectionPoint>. Server''s connection point.
cookie <integer>. Cookie supplied by server when subscribing, needed to
unsubscribe.

'!
!AXCustomEventSink categoriesForClass!COM-Implementations! !
!AXCustomEventSink methodsFor!

connect: anIUnknown
| container |
self disconnect.
container := anIUnknown queryInterface: IConnectionPointContainer.

connector := container findConnectionPoint: self interfaceClass.
cookie := connector advise: (self server queryInterface: IUnknown).
"Transcript nextPutAll: 'Connected '; print: self; cr."
^cookie!

disconnect
"Disconnect the receiver from the connection point to which it has
previously been
connected."

connector notNull ifTrue: [
"Transcript nextPutAll: 'Disconnecting '; print: self; cr."
connector Unadvise: cookie.
connector free.
connector := nil]
!

interfaceClass
^interfaceClass!

interfaceClass: anObject
"Set the value of the receiver's ''interfaceClass'' instance variable to
the argument, anObject."

interfaceClass := anObject!

server
^server!

server: anObject
"Set the server object on behalf of which the receiver is 'sinking' an
interface. Any invocations
of methods in the interface (i.e. arriving events) will be forwarded to
this object, assuming the
the server is set before the interface is queried."

server := anObject!

supportedInterfaces
^Array with: self interfaceClass! !
!AXCustomEventSink categoriesFor: #connect:!operations!public! !
!AXCustomEventSink categoriesFor: #disconnect!operations!public! !
!AXCustomEventSink categoriesFor: #interfaceClass!public! !
!AXCustomEventSink categoriesFor: #interfaceClass:!accessing!public! !
!AXCustomEventSink categoriesFor: #server!accessing!public! !
!AXCustomEventSink categoriesFor: #server:!accessing!public! !
!AXCustomEventSink categoriesFor: #supportedInterfaces!constants!public! !

SQLDMOBackupHost guid: (GUID fromString:
'{2319F8E5-13B1-490A-9083-6FD1371BFD08}')!
SQLDMOBackupHost comment: 'Hosts for SQLDMO backup service and also sinks
events from that service. At present it doesn''t do anything useful with the
events, but these could be translated to Smalltalk events, etc.

N.B. Watch out for object lifetime issues. There is effectively a circular
reference between any SQLDMO objects hosted by the receiver, and the
receiver because the receiver is sinking events from those objects. i.e. it
holds an interface pointer to the SQLDMO object, which in turn is holding an
interface pointer to the receiver through which it passes back events. This
circular reference cannot be cleared up by garbage collection (because COM
uses plain reference counting), so it must be explicitly broken, e.g. by
sending #free to this object.'!
!SQLDMOBackupHost categoriesForClass!COM-Implementations! !
!SQLDMOBackupHost methodsFor!

Complete: aString
"Helpstring: Backup has completed."

Transcript print: 'Backup Completed: '; display: aString; cr.
^S_OK!

free
backupSink notNil ifTrue: [backupSink disconnect. backupSink := nil].
backup notNull ifTrue: [backup free. backup := nil]!

interface
backup isNull
ifTrue:
[backup := SQLDMO_Backup new.
backupSink := (AXCustomEventSink new)
server: self;
interfaceClass: SQLDMOBackupSink;
yourself.
backupSink connect: backup].
^backup!

NextMedia: aString
"Helpstring: Request for next volume in backup"

Transcript nextPutAll: 'Backup, Next Media: '; display: aString; cr.
^S_OK!

PercentComplete: aString Percent: anInteger
"Helpstring: Percent completion message during Backup process"

Transcript
nextPutAll: 'Backup Progress: ';
display: aString;
nextPut: $(;
print: anInteger;
nextPutAll: '%)';
cr.
^S_OK!

supportedInterfaces
^Array with: SQLDMOBackupSink! !
!SQLDMOBackupHost categoriesFor: #Complete:!COM
Interfaces-BackupSink!public! !
!SQLDMOBackupHost categoriesFor: #free!public!realizing/unrealizing! !
!SQLDMOBackupHost categoriesFor: #interface!accessing!public! !
!SQLDMOBackupHost categoriesFor: #NextMedia:!COM
Interfaces-BackupSink!public! !
!SQLDMOBackupHost categoriesFor: #PercentComplete:Percent:!COM
Interfaces-BackupSink!public! !
!SQLDMOBackupHost categoriesFor: #supportedInterfaces!constants!public! !

!SQLDMOBackupHost class methodsFor!

example1
| backup dmo server |
dmo := SQLDMOBackupHost new.
backup := dmo interface.
backup backupSetName: 'Dolphin Test'.
backup database: 'Northwind'.
backup action: SQLDMOBackup_Database.
backup files: 'c:\TestSQLBackup'.
server := SQLDMO_SQLServer new.
server loginSecure: true.
server connect.
backup sqlBackup: server.
server close.
dmo free! !
!SQLDMOBackupHost class categoriesFor: #example1!examples!public! !


handjive

unread,
Sep 24, 2004, 3:53:17 AM9/24/04
to
Blair

Thank you for your supports, and I apologize.
Your first suggestion is sufficient for break my probrem, but I could't
use that well.

Because I touched and modified some around of COM classes, and falled into
a confusion.
At first, Your program did not work. But with fresh image, its work well
:-(


0 new messages