How to: Search for a contact or distribution group in Lync SDK

How to

Learn how to search for Lync users and distribution groups by using Microsoft Lync 2013 SDK to programmatically add a contact and group feature to an application.

_Applies to:  Lync 2013 | Lync Server 2013 _

In this article
Prerequisites
Search for a contact
Search for a distribution group
Code example: Distribution group list
Additional resources

Prerequisites

The prerequisites for searching for contacts and distribution groups are as follow:

  • Microsoft Lync 2013 must be installed and running on the development computer.

  • You must have sign-in credentials for Microsoft Lync Server 2013.

  • Microsoft Lync 2013 SDK must be installed on the development computer.

Search for a contact

When a user wants to communicate with a Lync 2013 user or users in a distribution group, the user selects from their contact list. When the user or group is not in the contact list, the user must do a contact search and then add the desired contacts to the contact list before starting a conversation. The Lync 2013 API gives you search capabilities and lets you set the search providers to be used. For information about choosing search providers, see Advanced contact search.

Searching for a contact or a collection of contacts whose display name matches a supplied search key is a simple asynchronous operation that involves calling the BeginSearch and corresponding EndSearch methods on the Microsoft.Lync.Model.ContactManager object.

The following illustration shows the order in which you call contact search methods and handle search events.

OCOM_Search

To select a set of search providers

  1. Read the SignInConfiguration.SignedInFromIntranet property which returns true if the user is signed in to Lync 2013 inside of an organization intranet.

  2. Call the ContactManager.GetSearchProviderStatus method for each search provider type that can be out of synchronization.

  3. If the method called in the previous step returns SearchProviderStatusType.SyncSucceeded, add the search provider type enumerator to a List<Microsoft.Lync.Model.SearchProviders> structure.

  4. Add each of the search provider types that is always in synchronization to the List.

The following example loads a list of search providers whose current state is synchronized.

/// <summary>
        /// Loads a list of search providers who are synched with Lync 2013 server address book  
        /// </summary>
        private void LoadSearchProviders()
        {
            //Did the user sign in to Lync from inside of an organization firewall?
            if (lyncClient.SignInConfiguration.SignedInFromIntranet == true)
            {
                if (contactMgr.GetSearchProviderStatus(SearchProviders.ExchangeService) == SearchProviderStatusType.SyncSucceeded)
                {
                    myActiveSearchProviders.Add(SearchProviders.ExchangeService);
                }
                if (contactMgr.GetSearchProviderStatus(SearchProviders.Expert) == SearchProviderStatusType.SyncSucceeded)
                {
                    myActiveSearchProviders.Add(SearchProviders.Expert);
                }
                if (contactMgr.GetSearchProviderStatus(SearchProviders.GlobalAddressList) == SearchProviderStatusType.SyncSucceeded)
                {
                    myActiveSearchProviders.Add(SearchProviders.GlobalAddressList);
                }
            }
            else
            {
                if (contactMgr.GetSearchProviderStatus(SearchProviders.ExchangeService) == SearchProviderStatusType.SyncSucceededForExternalOnly)
                {
                    myActiveSearchProviders.Add(SearchProviders.ExchangeService);
                }
                if (contactMgr.GetSearchProviderStatus(SearchProviders.Expert) == SearchProviderStatusType.SyncSucceededForExternalOnly)
                {
                    myActiveSearchProviders.Add(SearchProviders.Expert);
                }
                if (contactMgr.GetSearchProviderStatus(SearchProviders.GlobalAddressList) == SearchProviderStatusType.SyncSucceededForExternalOnly)
                {
                    myActiveSearchProviders.Add(SearchProviders.GlobalAddressList);
                }
            }

            //The following search providers are always fully synched and their sync state does not change.
            myActiveSearchProviders.Add(SearchProviders.Default);
            myActiveSearchProviders.Add(SearchProviders.OtherContacts);
            myActiveSearchProviders.Add(SearchProviders.PersonalContacts);
            myActiveSearchProviders.Add(SearchProviders.WindowsAddressBook);
        }

Warning

A search operation can return a large number of contact and distribution group results, depending on the length of the search string. For example, a search string of “Sm” returns all users whose search field content includes the string “Sm” whereas “John Smith” returns only contacts whose name is John Smith.

To help users refine their search, display each set of results in a UI list so the user can review the results. If the search results contain less relevant items, let the user enter a more specific search string and perform the search operation again. Once the user has entered a sufficiently specific search string that returns the right set of contacts, the user approves the results for subscription and the contacts are added to the subscription with a bulk add as shown in figure 1.

To search for a contact by using a list of search providers

  1. Obtain a user’s partial or full name as a string to be used as a search key.

  2. Call the ContactManager.BeginSearch method.

  3. In a lambda expression or a callback method passed in the second argument of BeginSearch, get the results of the search by calling the ContactManager.EndSearch method.

    A Microsoft.Lync.Model.SearchResults object is returned.

  4. Read the SearchResults.ContactsCount property, and then continue this procedure if the property value returns one or more contacts.

    The following example iterates the contact collection returned by the SearchResults.Contacts property.

    /// <summary>
            /// Search for a contact or group
            /// </summary>
            /// <param name="searchName">string. Name of contact or group to search for</param>
            /// <param name="numResults">uint. Number of results to return.</param>
            void SearchForGroupOrContact(string searchName, uint numResults)
            {
                try
                {
                    // Initiate search for entity based on name.
    
                    if (lyncClient.State == ClientState.SignedIn)
                    {
                        SearchFields searchFields = lyncClient.ContactManager.GetSearchFields();
                        object[] asyncState = { lyncClient.ContactManager, searchName };
                        foreach (var myActiveSearchProvider in myActiveSearchProviders)
                        {
                            lyncClient.ContactManager.BeginSearch(searchName
                                , myActiveSearchProvider
                                , searchFields
                                , SearchOptions.Default
                                , numResults
                                , (ar) => 
                                {
                                   SearchResults searchResults = _LyncClient.ContactManager.EndSearch(ar);
                                   if (searchResults.Contacts.Count > 0)
                                   {
                                      Console.WriteLine(
                                        searchResults.Contacts.Count.ToString() + 
                                        " found");
    
                                      foreach (Contact contact in searchResults.Contacts)
                                      {
                                         Console.WriteLine(
                                         contact.GetContactInformation(ContactInformationType.DisplayName).ToString());
                                      }
                                   }
                                }
                                , asyncState);
                        }
                    }
                }
    
                catch (Exception ex) { MessageBox.Show("Error:    " + ex.Message); }
            }
    

You can make a single call into the ContactManager.BeginSearch that sets multiple search providers by adding SearchProviders enumerators together in the method argument as follows:

                        lyncClient.ContactManager.BeginSearch(searchName
                            , SearchProviders.ExchangeService | SearchProviders.GlobalAddressList
                            , searchFields
                            , SearchOptions.Default
                            , numResults
                            , SearchResultsCallback
                            , asyncState);

To search for a contact by using a default search provider

  1. Obtain a user’s partial or full name as a string to be used as a search key.

  2. Call the ContactManager.BeginSearch method.

  3. In a lambda expression or a callback method passed in the second argument of BeginSearch, get the results of the search by calling the ContactManager.EndSearch method.

    A Microsoft.Lync.Model.SearchResults object is returned.

  4. Read the SearchResults.ContactsCount property, and then continue this procedure if the property value returns one or more contacts.

    The following example iterates the contact collection returned by the SearchResults.Contacts property.

            /// <summary>
            /// Searches for any user whose display name contains the supplied
            /// search key
            /// </summary>
            /// <param name="searchKey">string. User name or partial name to search on</param>
            private void SearchForContacts(string searchKey)
            {
                Console.WriteLine(
                    "Searching for contacts on " + 
                    searchKey);
    
                _LyncClient.ContactManager.BeginSearch(
                    searchKey, 
                    (ar) => 
                    {
                        SearchResults searchResults = _LyncClient.ContactManager.EndSearch(ar);
                        if (searchResults.Contacts.Count > 0)
                        {
                            Console.WriteLine(
                                searchResults.Contacts.Count.ToString() + 
                                " found");
    
                            foreach (Contact contact in searchResults.Contacts)
                            {
                                Console.WriteLine(
                                    contact.GetContactInformation(ContactInformationType.DisplayName).ToString());
                            }
                        }
                    }, 
                    null);
            }
    

Search for a distribution group

Searching for groups is very similar to searching for contacts because you call the same two methods as in the previous section but you read the SearchResults.Groups property in the search results.

A distribution group can contain other distribution groups in addition to a collection of contacts. For this reason, you may consider using a recursion algorithm to successively expand and list nested groups and their members.

Warning

A distribution group that contains a set of deeply nested distribution groups can quickly use up computer resources if automatically expanded to the deepest level. It is better to display the contacts in the top level distribution group plus the unexpanded distribution groups at that level. Let a user select an individual distribution group to expand to the next level and then display the users and groups at that level.

To search for a distribution group

  1. Obtain the partial or full name of a distribution group as a string to be used as a search key.

  2. Call the ContactManager.BeginSearch method.

    Tip

    If you want to run an advanced group search, call the ContactManager.BeginSearch method and specify the search provider and a set of distribution group property fields to search against.

    In either a lambda expression or a callback method passed in the second argument of BeginSearch, get the results of the search by calling the ContactManager.EndSearch method. A Microsoft.Lync.Model.SearchResults object is returned.

  3. Read the SearchResults.GroupsCount property, and then continue this procedure if the property value returns 1 or more distribution groups.

    The following example iterates the group collection returned by the SearchResults.Groups property.

            /// <summary>
            /// Searches for any distribution group whose name contains the supplied
            /// search key
            /// </summary>
            /// <param name="searchKey">string. Group name or partial name to search on.</param>
            private void SearchForGroups(string searchKey)
            {
                Console.WriteLine(
                    "Searching for groups on " + 
                    searchKey);
    
                _LyncClient.ContactManager.BeginSearch(
                    searchKey,
                    (ar) =>
                    {
                        SearchResults searchResults = _LyncClient.ContactManager.EndSearch(ar);
                        if (searchResults.Groups.Count > 0)
                        {
                            Console.WriteLine(
                                searchResults.Groups.Count.ToString() + 
                                " found");
    
                            foreach (Group group in searchResults.Groups)
                            {
                                Console.WriteLine(System.Environment.NewLine + group.Name);
                                DistributionGroup dGroup = group as DistributionGroup;
                                DGLister.Program.DGExpander(dGroup);
                            }
                        }
                        else
                        {
                            Console.WriteLine(
                                "No groups found for search on " + 
                                searchKey);
                        }
                    },
                    null);
    
            }
    

To expand a distribution group

  1. Read the DistributionGroup.IsExpanded property.

    If the returned value is false, the distribution group must be expanded before you can access its contacts.

  2. Call the DistributionGroup.BeginExpand method.

  3. In the lambda expression or callback method passed in the first argument of the BeginExpand method, call the DistributionGroup.EndExpand method.

  4. Iterate on the distribution group as a Microsoft.Lync.Model.Contact collection to get individual contacts.

  5. Read the Count property of the DistributionGroup.NestedGroups property.

  6. If the count value returned in the previous step is greater than zero, iterate on the value returned by the DistributionGroup.NestedGroups to get the nested distribution groups.

    The following example expands a distribution group if not already expanded, prints the display name of each group contact, and then recursively expands any nested distribution groups.

            /// <summary>
            /// Expands any groups found in search results 
            /// </summary>
            /// <param name="workMaterial"></param>
            static void DGExpander(DistributionGroup DGGroup)
            {
    
                //Only distribution groups can be expanded.
                //_Group is a class field that references the Microsoft.Lync.Model.Group.Group instance
                //encapsulated by the GroupModel class.
                if (DGGroup.Type != GroupType.DistributionGroup)
                {
                    return;
                }
    
                //If the distribution group has not been expanded yet,
                //it must be expanded before group members are visible
                if (DGGroup.IsExpanded == false)
                {
                    Console.WriteLine(
                        " -- Expanding " + 
                        DGGroup.Name + 
                        " -- ");
    
                    DGGroup.BeginExpand(
                        (ar) => 
                        {
                            try
                            {
                                DGGroup.EndExpand(ar);
                            }
                            catch (LyncClientException)
                            {
                                Console.WriteLine(
                                    "Lync Client Exception on expand " + 
                                    DGGroup.Name);
                            }
                            DGLister.Program.PrintDGDetail(DGGroup);
                        },
                        null);
                }
                else
                {
                    DGLister.Program.PrintDGDetail(DGGroup);
                }
            }
    

Code example: Distribution group list

The following example is a command-line program that accepts two string parameters: search object type and search string. If the search object type is “Group,” the example queries for distribution groups whose name contains the search string. The returned groups are parsed and the names of the contacts within the groups are written to the console. If a distribution group has nested distribution groups, those groups are parsed for contacts.

If the search object is “Contact,” the example queries for users whose display name contains the search string.

using System;
using System.Collections.Generic;
using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Group;

namespace DGLister
{
    class Program
    {
        private LyncClient _LyncClient;
        
        static void Main(string[] args)
        {
            Program program = new Program(args);
            Console.ReadLine();
        }
        private Program(string[] args)
        {
            try
            {
                _LyncClient = LyncClient.GetClient();

                if (_LyncClient.State != ClientState.SignedIn)
                {
                    Console.WriteLine("Lync client is not signed in.");
                    Console.ReadLine();
                }
                else
                {
                    if (args.Length > 0)
                    {
                        if (args[0].ToString().Contains("Group"))
                        {
                            SearchForGroups(args[1]);
                        }
                        if (args[0].ToString().Contains("Contact"))
                        {
                            SearchForContacts(args[1]);
                        }
                    }
                }
            }
            catch (ClientNotFoundException) { Console.WriteLine("Lync client was not found on startup"); }

        }

        /// <summary>
        /// Searches for any user whose display name contains the supplied
        /// search key
        /// </summary>
        /// <param name="searchKey">string. User name or partial name to search on</param>
        private void SearchForContacts(string searchKey)
        {
            Console.WriteLine(
                "Searching for contacts on " + 
                searchKey);

            _LyncClient.ContactManager.BeginSearch(
                searchKey, 
                (ar) => 
                {
                    SearchResults searchResults = _LyncClient.ContactManager.EndSearch(ar);
                    if (searchResults.Contacts.Count > 0)
                    {
                        Console.WriteLine(
                            searchResults.Contacts.Count.ToString() + 
                            " found");

                        foreach (Contact contact in searchResults.Contacts)
                        {
                            Console.WriteLine(
                                contact.GetContactInformation(ContactInformationType.DisplayName).ToString());
                        }
                    }
                }, 
                null);
        }

        /// <summary>
        /// Searches for any distribution group whose name contains the supplied
        /// search key
        /// </summary>
        /// <param name="searchKey">string. Group name or partial name to search on.</param>
        private void SearchForGroups(string searchKey)
        {
            Console.WriteLine(
                "Searching for groups on " + 
                searchKey);

            _LyncClient.ContactManager.BeginSearch(
                searchKey,
                (ar) =>
                {
                    SearchResults searchResults = _LyncClient.ContactManager.EndSearch(ar);
                    if (searchResults.Groups.Count > 0)
                    {
                        Console.WriteLine(
                            searchResults.Groups.Count.ToString() + 
                            " found");

                        foreach (Group group in searchResults.Groups)
                        {
                            Console.WriteLine(System.Environment.NewLine + group.Name);
                            DistributionGroup dGroup = group as DistributionGroup;
                            DGLister.Program.DGExpander(dGroup);
                        }
                    }
                    else
                    {
                        Console.WriteLine(
                            "No groups found for search on " + 
                            searchKey);
                    }
                },
                null);

        }
        /// <summary>
        /// Expands any groups found in search results 
        /// </summary>
        /// <param name="workMaterial"></param>
        static void DGExpander(DistributionGroup DGGroup)
        {

            //Only distribution groups can be expanded.
            //_Group is a class field that references the Microsoft.Lync.Model.Group.Group instance
            //encapsulated by the GroupModel class.
            if (DGGroup.Type != GroupType.DistributionGroup)
            {
                return;
            }

            //If the distribution group has not been expanded yet,
            //it must be expanded before group members are visible
            if (DGGroup.IsExpanded == false)
            {
                Console.WriteLine(
                    " -- Expanding " + 
                    DGGroup.Name + 
                    " -- ");

                DGGroup.BeginExpand(
                    (ar) => 
                    {
                        try
                        {
                            DGGroup.EndExpand(ar);
                        }
                        catch (LyncClientException)
                        {
                            Console.WriteLine(
                                "Lync Client Exception on expand " + 
                                DGGroup.Name);
                        }
                        DGLister.Program.PrintDGDetail(DGGroup);
                    },
                    null);
            }
            else
            {
                DGLister.Program.PrintDGDetail(DGGroup);
            }
        }

        /// <summary>
        /// Iterates on the contact collection in a distribution group and then
        /// prints the contact name + URI or recursively calls DGExpander when nested
        /// distribution groups are found.
        /// </summary>
        /// <param name="DGGroup"></param>
        static void PrintDGDetail(DistributionGroup DGGroup)
        {
            Console.WriteLine(
                System.Environment.NewLine + 
                " -- Getting contacts for " + 
                DGGroup.Name + " -- ");

            if (DGGroup.Count > 0)
            {
                foreach (Contact contact in DGGroup)
                {
                    Console.WriteLine(
                        "\t" + 
                        contact.GetContactInformation(ContactInformationType.DisplayName).ToString());
                }
            }
            if (DGGroup.NestedGroups.Count > 0)
            {
                foreach (Group group in DGGroup.NestedGroups)
                {
                    DistributionGroup dGroup = group as DistributionGroup;
                    Console.WriteLine(
                        System.Environment.NewLine + 
                        "\t\t #### Recursively calling DGExpander for " + 
                        group.Name + 
                        " ####");

                    DGLister.Program.DGExpander(dGroup);
                }
            }
        }

    }
}

Additional resources