Title: COMException error 0x5011 (S_ADS_ERRORSOCCURRED) while enumerating the PrincipalSearchResult collection returned from System.DirectoryServices.AccountManagetment UserPrincipal:: GetGroups method

T

he COMException error 0x5011 (S_ADS_ERRORSOCCURRED) can be thrown when you are enumerating the PrincipalSearchResult collection returned by UserPrincipal::GetGroups method if the UserPrincipal object is initialized with a PrincipalContext of a user in one domain and is attempting to retrieve group membership of a user from a different domain. The error is generated by the underlying ADSI search results enumeration. The error occurs when the Attribute Scoped Query (ASQ ) control receives a referral to a foreign domain during the data collection phase of the query. The error is not considered fatal, since the referral completes successfully, and the membership information is returned. The System.DirectoryServices.AccountManagement namespace propagates the error in the form of an exception to the calling program. At this point, the caller must decide how to handle the exception.

All of the group information is returned to the caller via the PrincipalSearchResult collection. The exception is thrown as the last item in the collection is enumerated.

There are three solutions/workarounds for this behavior.

 1. Use standard ADSI group enumeration using a recursive alogrythm. Recursively walking group membership can be a very expensive operation depending on how the groups are nested and the domain topology.

You can find more about enumerating group membership using ADSI at the following link “Enumerating Members of a Group

 2. Trap the exception during group enumeration. Check the PrincipalContext against the user for which groups are being enumerated and ignore the exception if the PrincipalContext was initialized to a different domain than the domain of the user for whom groups are being retrieved.

 3. Using S.DS.Protocols (in .Net 2.0 and above) takeing advantag of the ASQ control. S.DS.Protocols namespace uses LDAP API directly under the hood and works well with ASQ.

The following is a code sample illustrating how to use the ASQ control from the S.DS.Protocols

Namespace:

static void SDS_P_Test()
{
            string hostOrDomainName = "shaolint.com";
            string ldapSearchFilter = "(objectClass=*)";
            string targetGroupObject = "CN=USERA,OU=MYOU,DC=SHAOLINT,DC=COM";       
            NetworkCredential netCred = new NetworkCredential("UserTest", "Password!");
            LdapConnection connection = new LdapConnection(new LdapDirectoryIdentifier(hostOrDomainName), netCred);

            try
            {
Console.WriteLine("\nPerforming an attribute scoped query"); 
SearchRequest searchRequest = new SearchRequest(targetGroupObject, ldapSearchFilter, SearchScope.Base, null); 
                AsqRequestControl asqRequest = new AsqRequestControl("memberOf");
                searchRequest.Controls.Add(asqRequest);
                SearchOptionsControl searchOptions = new SearchOptionsControl(SearchOption.PhantomRoot);
                searchRequest.Controls.Add(searchOptions); 
SearchResponse searchResponse = (SearchResponse)connection.SendRequest(searchRequest);                             
                if (searchResponse.Controls.Length != 1 || !(searchResponse.Controls[0] is AsqResponseControl))
                {
                    Console.WriteLine("The server cannot return ASQ results");
                    return;
       }
       // cast the diretory control into a AsqResponseControl object.
       AsqResponseControl asqResponse = (AsqResponseControl)searchResponse.Controls[0];
       Console.WriteLine("\nSearch Response Entries:{0}", searchResponse.Entries.Count);
       // list the entries in this page
       foreach (SearchResultEntry entry in searchResponse.Entries)
       {
           Console.WriteLine("{0}:{1}", searchResponse.Entries.IndexOf(entry) + 1,
entry.Attributes["name"][0].ToString());
       }
    }
    catch (Exception e)
    {
                Console.WriteLine("\nUnexpected exception occured:\n\t{0}: {1}", e.GetType().Name, e.Message);
           }
 }

You can read more about ASQ with S.DS.Protocols in the MSDN article msdn.microsoft.com/en-us/library/bb332056.aspx under the section Creating an Attribute Scoped Query (ASQ)