|
Connection Points
by Binh Ly
Since event mechanisms have a wide range of applicability in application
development, COM introduces a standard mechanism for 1) how a client passes its
interface to the server and 2) how a server provides the ability for a client to
connect (and disconnect) the client's interface. This mechanism is called
Connection Points.
In order to understand the connection points mechanism, let's first see what
kinds of things can be standardized in the normal
client-passes-its-interface-to-server technique described in the previous
section. We've already seen that in order for the server to call back into
the client, the client needs to pass an interface pointer to the server through
a method of the server's interface; let's call this method m1. We've also seen
that it is sometimes useful for a server to be able to accept multiple client
interface connections, as in our JazzChatRoom
example. Since multiple clients can connect to a server, any of those
clients may also want to disconnect from the server, similar to how a chatter
leaves a chat room. Disconnecting from the server obviously needs another method
on the server's interface that the client can call to disconnect itself; let's
call this method m2. In short, a server that wants a standard manner for clients to connect and
disconnect from it must implement at least m1 and m2.
IConnectionPoint
What should m1 and m2 look like? Good question!
Remember, m1 is called by the
client to connect its interface to the server and m2 is used to disconnect
itself from the server. Since the server will possibly maintain a list of
clients, the client must somehow pass an ID parameter into m2 so that the server
knows which exact client to disconnect from its list. This means that each
client must have a unique ID in order for m2 to function correctly. Since
clients can come and go, it's not a good idea for a client to hard-code its ID
together with it. That could result in ID clashes and the client doesn't really
want to be burdened with carrying an ID around all the time; after all,
the ID is useful during the duration that the client is connected to the server.
So why not
make the server generate the ID for the client? This way, every client that
connects itself to the server at runtime will get an ID (the server will
guarantee the ID's uniqueness within its list of clients) and when the client
disconnects, the ID can be thrown away since it has no use anymore. Generating
the ID can simply be part of m1, i.e. at the point where the client connects
itself to the server, the server gives it an ID that it can later on use to
disconnect itself.
Since the client connects and disconnects itself from the server, COM
introduces the term connection point to describe the abstraction of
a server's "point-of-connection" for the client. In other words, a
connection point 1) is where a client connects and disconnects itself from the
server and 2) contains a list of client interfaces maintained by the server. A
connection point has an interface and it's called, not surprisingly,
IConnectionPoint. Since m1 and m2 aren't very friendly method names, let's use
the names Advise (m1) and Unadvise (m2) instead. Each connection point contains
at least the methods Advise and Unadvise:
IConnectionPoint = interface
procedure Advise (ClientInterface; var ID); // var means that ID is
sent by reference
procedure Unadvise (ID);
end;
So when the client calls IConnectionPoint.Advise, it gets back an ID in the
ID parameter. It then keeps the ID for later use when it wants to call
IConnectionPoint.Unadvise.
|
As a side note, the ID used in this context is also called
a Cookie in other literature.
|
Here's how our JazzChatRoom would work using a connection point:
IJazzChatRoom = interface
function ConnectionPoint : IConnectionPoint; //exposes
JazzChatRoom's connection point
procedure BroadcastMessage (Message);
end;
function JazzChatRoom.ConnectionPoint : IConnectionPoint;
begin
Result = JazzChatRoom's connection point;
end;
procedure JazzChatRoom.BroadcastMessage (Message);
begin
For each Chatter in JazzChatRoom's connection point
Chatter.HeresAMessageFromFellowChatter (Message);
end;
And here's how a chatter would connect to
JazzChatRoom:
// attach to connection point
ConnectionPoint = JazzChatRoom.ConnectionPoint;
ConnectionPoint.Advise (MyIChatterInterface, ID);
...
// detach from connection point
ConnectionPoint.Unadvise (ID);
There's nothing really new here except
for the fact that the server must implement the connection point. More
specifically, the server must implement IConnectionPoint.Advise,
IConnectionPoint.Unadvise, provide a storage mechanism for the client's
interface pointers, and allow iteration of this list.
IConnectionPointContainer
Let's look back at our JazzChatRoom example. A chatter who has a microphone
and a speaker might want a more "personal" experience by talking to
other chatters who also have speakers. However, not everybody has microphones and
speakers so JazzChatRoom has to somehow provide a mechanism to enable two groups
of chatters: a normal group and a voice-enabled group. In other words, some
chatters enter the normal connection point while others enter the voice-enabled
connection point. We now have 2 types of chatters:
// normal chatter
IChatter = interface
procedure HeresAMessageFromFellowChatter (Message);
end;
// voice-enabled chatter
IVoiceEnabledChatter = interface
procedure HeresAVoiceMessageFromFellowChatter (VoiceMessage);
end;
And using the connection point mechanism that we just learned, our
JazzChatRoom now exposes 2 connection points like this:
IJazzChatRoom = interface
function NormalConnectionPoint : IConnectionPoint;
function VoiceEnabledConnectionPoint : IConnectionPoint;
procedure BroadcastMessage (Message);
procedure BroadcastVoiceMessage (VoiceMessage);
end;
The normal chatter would then connect through NormalConnectionPoint.Advise
whereas the voice-enabled chatter would connect through
VoiceEnabledConnectionPoint.Advise. The normal chatter broadcasts its message
using BroadcastMessage whereas the voice-enabled chatter uses
BroadcastVoiceMessage.
This example shows how a server might need to expose multiple connection
points to segregate clients into groups depending on what the client is capable
of (or what the client is interested in). Multiple connection points can be standardized into a "connection point
collection". In COM, this collection is called a connection point
container, a.k.a. IConnectionPointContainer.

Figure: A connection point
container
IConnectionPointContainer is nothing but a manager of multiple connection
points. A client that wants to attach to a particular connection point now has
to go through the connection point container, ask it for the desired connection point,
and then attach to the connection point. That means that JazzChatRoom would
simply expose an IConnectionPointContainer to clients:
IJazzChatRoom = interface
function ConnectionPointContainer : IConnectionPointContainer;
procedure BroadcastMessage (Message);
procedure BroadcastVoiceMessage (VoiceMessage);
end;
Normal clients now connect to JazzChatRoom like this:
// get cp container
ConnectionPointContainer = JazzChatRoom.ConnectionPointContainer;
// get normal cp
ConnectionPoint = ConnectionPointContainer.GimmeNormalConnectionPoint;
// attach to cp
ConnectionPoint.Advise (MyIChatterInterface, ID);
Voice-enabled clients connect in a similar manner:
// get cp container
ConnectionPointContainer = JazzChatRoom.ConnectionPointContainer;
// get voice-enabled cp
ConnectionPoint = ConnectionPointContainer.GimmeVoiceEnabledConnectionPoint;
// attach to cp
ConnectionPoint.Advise (MyIVoiceEnabledChatterInterface, ID);
Of course IConnectionPointContainer doesn't have the
GimmeNormalConnectionPoint nor GimmeVoiceEnabledConnectionPoint methods. What it
does have is a FindConnectionPoint method where you specify which connection
point you want. How exactly do you specify the connection point? By its name?
The answer is, not surprisingly, a GUID. Like every other thing we've seen in
COM, connection points are also named using GUIDs. More specifically, the
connection point's GUID is the same exact GUID that the client callback/event
interface is named.
Once more, here's exactly how normal clients connect to
JazzChatRoom:
// get cp container
ConnectionPointContainer = JazzChatRoom.ConnectionPointContainer;
// get normal cp
ConnectionPoint = ConnectionPointContainer.FindConnectionPoint (GUID of IChatter);
// attach to cp
ConnectionPoint.Advise (MyIChatterInterface, ID);
And here's exactly how voice-enabled clients connect:
// get cp container
ConnectionPointContainer = JazzChatRoom.ConnectionPointContainer;
// get voice-enabled cp
ConnectionPoint = ConnectionPointContainer.FindConnectionPoint (GUID of
IVoiceEnabledChatter);
// attach to cp
ConnectionPoint.Advise (MyIVoiceEnabledChatterInterface, ID);
IConnectionPointContainer has a couple of other methods that we'll not get
into here because they're not relevant to our discussion. However, I suggest that
you look at the other methods if you feel adventurous.
Some Terminology
The connection points mechanism is widely
documented in a lot of COM
literature. When you read them, you'll see a couple of terms that might throw
you off. For instance, a server object that exposes connection points is
sometimes called a connectable object or a source, i.e. it is the source of
events. The client that connects itself
to the source is sometimes called a sink, i.e. it is the receiver/sink of
events. The interface that the client (sink) exposes to
the server (source) is also called an outgoing interface - "outgoing" meaning
that its methods are called from the server in the "outward"
direction. Contrast that with the normal incoming interface that the
server exposes - "incoming" meaning that
the interface is used by the client to call "in" to the server.
Where Are We?
We've just engineered the basic connection point mechanism. A lot of
existing servers today extensively use connection points as a means to
"trigger events" or "call back" into the client. Hopefully,
this brief introduction will enable you to work with applications that implement
connection points.
Further Reading
- Understanding ActiveX and OLE by David
Chappell
- Inside DCOM by the Eddons
- Inside OLE by Kraig Brockshmidt
|