techvanguards.com
Last updated on 1/25/2016

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
Copyright (c) 1999-2011 Binh Ly. All Rights Reserved.