Reproducing Automatic Presence with the UCC API – Part 1

Most popular IM clients, including Communicator 2007, automatically modify the user’s status when they are inactive or away.  While piloting Office Communications Server we created a simple application that had the main features of Communicator (chat, A/V, presence, etc.) to show that we could incorporate that functionality into our in-house applications so that our agents only have to work with one piece of software.  The presence functionality is important to us, as we don’t want to route inbound calls from Speech Server to an agent who isn’t available.  As part of the pilot, I created a simple presence manager to monitor user activity and update availability when necessary.  After five minutes of inactivity, a state value of Inactive is published.  After 15 minutes, a state value of Away is published.  Finally, if the user locks the machine, a state value of Away is published.

Basically, there are two different things the manager needs to detect: the last time there was input (keyboard/mouse) to the machine, and when Windows is locked.  After a little digging, I found the easiest way to achieve our first goal was to P/Invoke the GetLastInputInfo function, which returns the time of the last keyboard/mouse activity.  Using a timer, we can call this method as often as necessary to check if our thresholds (five and 15 minutes) have been reached.  For the sake of simplicity, I removed the code that publishes the state to OCS.  We could also easily add logic to check if the user is on the phone, so the “In a Call” state isn’t overwritten. 

NOTE: I’m using the System.Threading.Timer class.

    internal sealed class PresenceManager : IDisposable

    {

        [DllImport(“user32.dll”)]

        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

 

        private Timer myLastInputTimer;

 

        public PresenceManager()

        {

            // initialize the timer to call back every two seconds

            myLastInputTimer = new Timer(CheckIdleTime, null, 2000, 2000);

        }

 

        /// <summary>

        /// Gets the current availability of the signed-in user.

        /// </summary>

        public Availability CurrentAvailability { get; private set; }

 

        /// <summary>

        /// Callback method for <see cref=”myLastInputTimer”/> to check the idle time

        /// of the computer and update the status if needed.

        /// </summary>

        /// <param name=”state”>The state, unused by this method.</param>

        private void CheckIdleTime(object state)

        {

            LASTINPUTINFO lastInputInfo = LASTINPUTINFO.Create();

            if (GetLastInputInfo(ref lastInputInfo))

            {

                // get the idle time by subtracting the tick count of the last input

                // event from the total tick count of the system

                int idleTicks = Environment.TickCount – lastInputInfo.dwTime;

                TimeSpan idleTime = TimeSpan.FromSeconds(idleTicks / 1000.0);

 

                Availability currentAvailability = this.CurrentAvailability;

                if (idleTime < TimeSpan.FromMinutes(5))

                {

                    if (currentAvailability != Availability.Available)

                    {

                        // publish available state to OCS here…

 

                        this.CurrentAvailability = Availability.Available;

                    }

                }

                else if (idleTime < TimeSpan.FromMinutes(15))

                {

                    if (currentAvailability != Availability.AvailableIdle

                        && currentAvailability != Availability.BusyIdle)

                    {

                        if (currentAvailability == Availability.Busy)

                        {

                            // publish busy-idle state to OCS here…

 

                            this.CurrentAvailability = Availability.BusyIdle;

                        }

                        else

                        {

                            // publish inactive state to OCS here…

 

                            this.CurrentAvailability = Availability.AvailableIdle;

                        }

                    }

                }

                else if (currentAvailability != Availability.Away)

                {

                    // publish away state to OCS here…

 

                    this.CurrentAvailability = Availability.Away;

                }

            }

        }

    }

 

Now we just need to define the LASTINPUTINFO structure that we pass in GetLastInputInfo.  I nested it inside the manager:

 

        /// <summary>

        /// Structure used by the <see cref=”GetLastInputInfo”/> method.

        /// </summary>

        [StructLayout(LayoutKind.Sequential)]

        private struct LASTINPUTINFO

        {

            private static readonly int size = Marshal.SizeOf(typeof(LASTINPUTINFO));

            /// <summary>

            /// Creates a new <see cref=”LASTINPUTINFO”/> initialized and ready to

            /// be used by the <see cref=”GetLastInputInfo”/> method.

            /// </summary>

            /// <returns></returns>

            public static LASTINPUTINFO Create()

            {

                LASTINPUTINFO lii = new LASTINPUTINFO();

                lii.cbSize = size;

                return lii;

            }

 

            /// <summary>

            /// The size of this instance, required by the

            /// <see cref=”GetLastInputInfo”/> method.

            /// </summary>

            [MarshalAs(UnmanagedType.U4)]

            public int cbSize;

            /// <summary>

            /// The system tick-count at the last recorded user input, set

            /// by the <see cref=”GetLastInputInfo”/> method.

            /// </summary>

            [MarshalAs(UnmanagedType.U4)]

            public int dwTime;

        }

 

GetLastInputInfo requires the cbSize field to be set before the method is called, so I added a Create method to return an instance ready to be uses.  The timer calls back on a thread retrieved from the thread pool, but we can publish to OCS from any thread.  I found that two seconds was a good interval that updated the state to Available in a reasonable time.  In the second post I’ll cover detecting when the machine is locked/unlocked using another P/Invoke operation.

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);

 

 

Book Review: CLR via C#, Second Edition

I recently volunteered to write a book review for Neumont’s quarterly newsletter, so I figured I’d post it here as well:

I don’t read many technical books. In fact, CLR via C#, Second Edition is the first one I’ve read completely from start to finish. It was recommended to me by my coworkers as one of the most useful reference books to have on hand. The book’s author, Jeffrey Richter, is co-founder of the consulting firm Wintellect, and has consulted for Microsoft on various projects, including development of the .NET Framework. He covers all of the core parts of the CLR (Common Language Runtime), on which the framework operates, while providing examples all along the way. The book itself is focused on detailing how the CLR functions, rather than explaining all of the features of C#. Instead, C# is used simply to illustrate how to the CLR works.

Richter covers the organization of the Type system into assemblies and modules, explaining the metadata and IL that is generated when code is compiled. He devotes several chapters to all aspects of classes, including fields, methods, properties, events, and more. The rest of the book covers all of the main parts of the .NET Framework, including generics, exception handling, garbage collection, reflection, and threading. In every chapter Richter offers advice about best practices to help developers avoid common programming mistakes and write fast, efficient code.

Unfortunately, Richter recently announced that he won’t be updating the book to account for the releases of .NET 3.0 and 3.5. His reason is that .NET 3.0 and 3.5 still run on the same version of the CLR, which is the main focus of the book, but there are places in the book that would benefit from an update. For example, in his chapter on threading Richter explains the problems in the ReaderWriterLock class, and why it should never be used. .NET 3.5 includes a new ReaderWriterLockSlim class that fixes those problems. It would be worth mentioning the new class in the book. At least Richter plans to release a future edition when a new version of the CLR is released.

Despite this drawback, it is very easy to see why so many people think this is one of the most important books for every .NET developer. Having this knowledge of the CLR has certainly helped me write better code, and I still refer to it all the time. If you haven’t read this book yet, and have any plans at all to develop with the .NET framework, this book should be the first one you read.

Greetings

Welcome to yet another technical blog.  My teammates at ExtendHealth, Inc. finally convinced me to start my own blog, and I figured it’d also be useful for me to record the various things I’ve done.  I’ll be detailing problems and solutions I encounter as I work on projects here at Extend, and my thoughts on working with various technologies.

At the moment I’m involved in a project utilizing the UCC API, a client-side API used to communicate with Office Communications Server directly from our own applications.  The API isn’t as straightforward and user-friendly as I’d like, and the documentation is lacking in some areas (I’ll go into more detail in a future post).  Many of my upcoming posts will be related to working with this API, and I hope to provide several usable code samples.

I hope my posts prove useful to others, and answer questions that can’t be found elsewhere on the Internet.  Take a look at the links on the side to see my teammate’s blogs.