George Durzi

in

April 2008 - Posts

Contact Name Resolution using the Office Communicator SDK

In my continuing efforts to bring you useful nuggets from the Office Communicator SDK, I bring you IMessengerContactResolution.

I realized that I needed something like this when I wasn't able to resolve contacts given their SMTP address by using the IMessenger::GetContact method. The IMessenger::FindContact methods wasn't helpful because it actually invoked the Communicator's UI to find a contact.

I came across IMessengerContactResolution::ResolveName in the SDK, but saw that the ResolveName method was marked as Not Implemented. Well, it is ... I didn't find out until someone on the UC team sent me a code snippet that used it. Moral of the story: give it a shot even if the SDK says it's not implemented.

Scenario

The scenario I was working with is that a user could drag a contact out of Communicator into my application. What you get in the drag arguments happens to be the contact's primary SMTP address, which may or may not match their Sip Uri, e.g. john.doe@contoso.com vs. jdoe@contoso.com. A lot of organizations do this, and there's really no standard way to "discover" a user's Sip given their SMTP and vice versa.

My ResolveContact method tries to resolve its input string a couple of different ways. As usual, when programming against the Office Communicator SDK, you unfortunately have to control logic flow using try/catch statements. I feel dirty every time I do that, but hey, my hands are tied :)

ResolveContact

In my application, I carry around a class-level variable called _resolver to perform contact resolution:

private IMessengerContactResolution _resolver;

When I handle the Communicator sign-in event, I set up _resolver by casting my main communicator object to IMessengerContactResolution:

_resolver = _communicator as IMessengerContactResolution;

This is pretty common when writing applications that use the Office Communicator SDK, you of course are responsible for cleaning up those objects which I do when I handle the Communicator sign-out event.

Here's the code for my ResolveContact method:

void ResolveContact(string dropString, out string sipUri, out string displayName)
        {
            try
            {
                sipUri = null;
                displayName = null;

                try
                {
                    // Scenario 1: User enters a Sip Uri
                    //  If so, GetContactDetails will return a display name
                    //
		    // Code for this method not included, 
		    //  It simply called IMessenger::GetContact
                    GetContactDetails(dropString, out displayName);
                }
                catch
                {
                    sipUri = null;
                }

                if (String.IsNullOrEmpty(displayName)) // Not a match based on Sip Uri
                {
                    try
                    {
                        // Scenario 2: User enters an SMTP address
                        //  Try to resolve the SMTP address into a Sip Uri
                        //
                        sipUri = _resolver.ResolveContact(ADDRESS_TYPE.ADDRESS_TYPE_SMTP,
                            CONTACT_RESOLUTION_TYPE.CONTACT_RESOLUTION_CACHED_ONLY, 
		            dropString);
                    }
                    catch
                    {
                        sipUri = null;
                    }

		    // Not a match based on SMTP Address
                    if (String.IsNullOrEmpty(sipUri))
                    {
                        try
                        {
                            // Scenario 3: User enters a contact's display name
                            //  Try to resolve the Display Name into a Sip Uri
                            //
                            sipUri = _resolver.ResolveContact(
				ADDRESS_TYPE.ADDRESS_TYPE_DISPLAY_NAME,
                                CONTACT_RESOLUTION_TYPE.CONTACT_RESOLUTION_CACHED_ONLY, 
				dropString);
                        }
                        catch
                        {
                            sipUri = null;
                        }
                    }

	            // Success - get the contact's Display Name
                    if (!String.IsNullOrEmpty(sipUri)) 
                    {
                        GetContactDetails(sipUri, out displayName);
                    }
                }
                else
                {
                    sipUri = dropString; // Scenario 1
                }
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }
Ad-hoc phone number dialing using the Office Communicator SDK

I'm gonna chalk this up to one of things that's painfully obvious after the fact... I'm using the IMessengerAdvanced::StartConversation method of the Office Communicator automation SDK to dial ad-hoc phone numbers, e.g. simply dialing Clarity's front desk at +13128633100 as opposed to selecting a contact in Communicator and dialing one of its listed phone numbers.

Communicator would attempt to dial the number but the call to StartConversation would throw a not-so-helpful COMException.

vConversationData

The SDK documentation describes the StartConversation method, but is vague on its parameters. The trick here is to correctly populate the vConversationData parameter.

Here's what the SDK says about the vConversationData parameter:

A VARIANT value to hold a XML binary large object (BLOB) used to send data dependent on the chosen conversation type. For focus-based conference call, this parameter is used to pass in the conference URI as the content of a <ConfURI> element. For PSTN calls, the parameter contains an array of TEL URIs, as a <TelURIs> element.

Pretty misleading, because we won't be BLOB'ing anything!

Formatting Phone Numbers

Before being passed to the StartConversation method, phone numbers have to be prefixed with tel: to explicitly specify that this is a phone number.

As part of configuration Office Communications Server for voice, you can add regular expressions to normalize the way Communicator dials phone numbers, e.g. when I simply dial 39XX, Communicator knows that this is my Clarity extension and dials it as +131286339XX.

What this means is that if you have these rules in place, you don't need to worry much about normalizing the phone number before passing it to the StartConversation method.

Of course, if you specify a phone number that doesn't match any of your normalizing regular expressions, Communicator won't be able to make the phone call. Communicator handles this gracefully, you don't need to handle an exception in your code.

IMessengerAdvanced::StartConversation

Here's the obvious part:

The way the vConversation data document was phrased, I was trying stuff like:

<TelURIs><TelURI>tel:+13128633100</TelUri></TelURIs>

XML Blob, I think not ...