Managing event subscriptions in UCC

The event model when interacting with COM interfaces is different from the .NET event model.  It is more similar to the Observer Pattern used in Java with “listeners” than delegates in .NET.  To listen for events raised by objects in the UCC API, you must advise the object of your implemented event “sink”.  The event handler methods are defined in the various interfaces in the UCC API that begin with an underscore, such as _IUccPlatformEvents. 

When you call Advise, you get a “cookie” that is used to call Unadvise at a later time.  Unadvise should always be called to prevent memory leaks, but what do we do with all the cookies in the meantime?  The documentation has a good example of registering for UCC events, but it uses a custom two-dimensional Dictionary2D, defined at the bottom of the sample, which basically uses a Dictionary of Dictionaries.  The outer dictionary stores an inner dictionary for each type of _IUccXXEvents interface subscribed to, and the inner dictionary stores the cookie for each object that raises the events listed in that interface.

While using a Dictionary2D is certainly one way to go, I decided I wanted a more lightweight cookie jar.  I took advantage of the Pair structure defined in Wintellect’s PowerCollections library to store the cookies in a single Dictionary.

        private static Dictionary<Pair<Type, object>, int> cookies = new Dictionary<Pair<Type, object>, int>();

 

        /// <summary>

        /// Advises the specified <paramref name=”source”/> that the specified

        /// <paramref name=”sink”/> is a registered event sink for events

        /// defined in <typeparamref name=”T”/>.

        /// </summary>

        /// <typeparam name=”T”>The interface defining the event handlers that

        /// are implemented by the <paramref name=”sink”/></typeparam>

        /// <param name=”source”>The source of the events.</param>

        /// <param name=”sink”>The event sink.</param>

        public static void Advise<T>(object source, T sink)

        {

            Pair<Type, object> key = new Pair<Type, object>(typeof(T), source);

 

            // Prevent the same sink from advising on a certain object/type combination more than once.

            // Otherwise, our event handler will be called multiple times, which can cause problems.

            if (!cookies.ContainsKey(key))

            {

                IConnectionPointContainer container = (IConnectionPointContainer)source;

                IConnectionPoint cp;

                int cookie;

                Guid guid = typeof(T).GUID;

 

                container.FindConnectionPoint(ref guid, out cp);

                cp.Advise(sink, out cookie);

 

                cookies.Add(key, cookie);

            }

        }

 

        public static void Unadvise<T>(object source)

        {

            // get the cached cookie for the source

            Pair<Type, object> key = new Pair<Type, object>(typeof(T), source);

            int cookie;

            if (cookies.TryGetValue(key, out cookie))

            {

                IConnectionPointContainer container = (IConnectionPointContainer)source;

                IConnectionPoint cp;

                Guid guid = typeof(T).GUID;

 

                container.FindConnectionPoint(ref guid, out cp);

                cp.Unadvise(cookie);

 

                // remove the cookie from the cached cookies

                cookies.Remove(key);

            }

        }

 

This dictionary uses the combination of the type of interface and the source of the event as its key.  We could also throw an exception if the key already exists, rather than simply doing nothing.  Adding synchronization is the last step we have to do.  It’s simple enough that I won’t cover it here.  When we’re done advising for the event is simple: Utilities.Advise<_IUccSessionEvents>(uccSession, this);

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s