InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

Searching the Global Address List - C# Edition

A long time ago, I wrote an article on How to get the Global Address List programatically. The theory behind that article is still valid, but it only features a VBScript example. Since someone from the Microsoft Exchange Forum had trouble converting it to C#, I fired up Visual Studio and hacked something together. The result is somewhat more complete than the VBScript example, because it allows access to all address lists, not just the default global address list (GAL).

Here we go:

using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;

namespace AddressListSample
{
    public class ActiveDirectoryConnection
    {
        public DirectoryEntry GetLdapDirectoryEntry(string path)
        {
            return GetDirectoryEntry(path, "LDAP");
        }

        public DirectoryEntry GetGCDirectoryEntry(string path)
        {
            return GetDirectoryEntry(path, "GC");
        }

        private DirectoryEntry GetDirectoryEntry(string path, string protocol)
        {
            var ldapPath = string.IsNullOrEmpty(path) ? string.Format("{0}:", protocol) : string.Format("{0}://{1}", protocol, path);
            return new DirectoryEntry(ldapPath);
        }
    }

    public class ExchangeAddressListService
    {
        private readonly ActiveDirectoryConnection _Connection;

        public ExchangeAddressListService(ActiveDirectoryConnection connection)
        {
            if (connection == null) throw new ArgumentNullException("connection");
            _Connection = connection;
        }

        public IEnumerable<AddressList> GetGlobalAddressLists()
        {
            return GetAddressLists("CN=All Global Address Lists");
        }

        public IEnumerable<AddressList> GetAllAddressLists()
        {
            return GetAddressLists("CN=All Address Lists");
        } 
        public IEnumerable<AddressList> GetSystemAddressLists()
        {
            return GetAddressLists("CN=All System Address Lists");
        }

        private IEnumerable<AddressList> GetAddressLists(string containerName)
        {
            string exchangeRootPath;
            using (var root = _Connection.GetLdapDirectoryEntry("RootDSE"))
            {
                exchangeRootPath = string.Format("CN=Microsoft Exchange, CN=Services, {0}", root.Properties["configurationNamingContext"].Value);
            }
            string companyRoot;
            using (var exchangeRoot = _Connection.GetLdapDirectoryEntry(exchangeRootPath))
            using (var searcher = new DirectorySearcher(exchangeRoot, "(objectclass=msExchOrganizationContainer)"))
            {
                companyRoot = (string) searcher.FindOne().Properties["distinguishedName"][0];
            }

            var globalAddressListPath = string.Format(containerName + ",CN=Address Lists Container, {0}", companyRoot);
            var addressListContainer = _Connection.GetLdapDirectoryEntry(globalAddressListPath);

            using (var searcher = new DirectorySearcher(addressListContainer, "(objectClass=addressBookContainer)"))
            {
                searcher.SearchScope = SearchScope.OneLevel;
                using (var searchResultCollection = searcher.FindAll())
                {
                    foreach (SearchResult addressBook in searchResultCollection)
                    {
                        yield return
                            new AddressList((string) addressBook.Properties["distinguishedName"][0], _Connection);
                    }
                }
            }
        }
    }

    public class AddressList
    {
        private readonly ActiveDirectoryConnection _Connection;
        private readonly string _Path;

        private DirectoryEntry _DirectoryEntry;

        internal AddressList(string path, ActiveDirectoryConnection connection)
        {
            _Path = path;
            _Connection = connection;
        }

        private DirectoryEntry DirectoryEntry
        {
            get
            {
                if (_DirectoryEntry == null)
                {
                    _DirectoryEntry = _Connection.GetLdapDirectoryEntry(_Path);
                }
                return _DirectoryEntry;
            }
        }

        public string Name
        {
            get { return (string) DirectoryEntry.Properties["name"].Value; }
        }

        public IEnumerable<SearchResult> GetMembers(params string[] propertiesToLoad)
        {
            var rootDse = _Connection.GetGCDirectoryEntry(string.Empty);
            var searchRoot = rootDse.Children.Cast<DirectoryEntry>().First();
            using (var searcher = new DirectorySearcher(searchRoot, string.Format("(showInAddressBook={0})", _Path)))
            {
                if (propertiesToLoad != null)
                {
                    searcher.PropertiesToLoad.AddRange(propertiesToLoad);
                }
                searcher.SearchScope = SearchScope.Subtree;
                searcher.PageSize = 512;
                do
                {
                    using (var result = searcher.FindAll())
                    {
                        foreach (SearchResult searchResult in result)
                        {
                            yield return searchResult;
                        }
                        if (result.Count < 512) break;
                    }
                } while (true);
            }
        }
    }

    internal class Program
    {
        private static void Main()
        {
            var connection = new ActiveDirectoryConnection();
            var service = new ExchangeAddressListService(connection);
            foreach (var addressList in service.GetGlobalAddressLists())
            {
                Console.Out.WriteLine("addressList.Name = {0}", addressList.Name);
                foreach (var searchResult in addressList.GetMembers())
                {
                    Console.Out.WriteLine("\t{0}", searchResult.Properties["name"][0]);
                }
            }

            foreach (var addressList in service.GetAllAddressLists())
            {
                Console.Out.WriteLine("addressList.Name = {0}", addressList.Name);
                foreach (var searchResult in addressList.GetMembers())
                {
                    Console.Out.WriteLine("\t{0}", searchResult.Properties["name"][0]);
                }
            }

            foreach (var addressList in service.GetSystemAddressLists())
            {
                Console.Out.WriteLine("addressList.Name = {0}", addressList.Name);
                foreach (var searchResult in addressList.GetMembers())
                {
                    Console.Out.WriteLine("\t{0}", searchResult.Properties["name"][0]);
                }
            }
        }
    }
}

The sample wraps the whole logic up into two classes: ExchangeAddressListService and AddressList. The first serves as some kind of entry point and the latter allows access to the members of a list.

Hope this helps…


Posted by Henning Krause on Tuesday, October 25, 2011 8:22 PM, last modified on Tuesday, October 25, 2011 8:22 PM
Permalink | Post RSSRSS comment feed

Event 1503: OMA DirectoryNotFoundException

I just stumbled across this error message:

Event Type:    Fehler
Event Source:    MSExchangeOMA
Event Category:    (1000)
Event Id:    1503
Date:        11/26/2008
Time:        14:11:35
User:        Not Applicable
Server:    servername
Description:
Method not found: System.String System.DirectoryServices.DirectoryEntry.get_Password().

Source: Microsoft.Exchange.OMA.Preferencing

Stacktrace:
   
   at Microsoft.Exchange.OMA.Preferencing.AdUserObject.get_globalWirelessEnable()
   at Microsoft.Exchange.OMA.Preferencing.AdUserObject.get_UserIsWirelesslyEnabled()
   at Microsoft.Exchange.OMA.Preferencing.OmaUserInfo.get_UserIsWirelesslyEnabled()
   at Microsoft.Exchange.OMA.UserInterface.Global.Session_Start(Object sender, EventArgs e)

Meldung: Eine Ausnahme vom Typ Microsoft.Exchange.OMA.DataProviderInterface.ProviderException wurde ausgelöst.
Ereignismeldung: 
Benutzermeldung: Systemfehler at der Verarbeitung Ihrer Anforderung. Versuchen Sie es erneut. Wenden Sie sich an Ihren Administrator, wenn das Problem wiederholt auftritt.
Quelle: Microsoft.Exchange.OMA.UserInterface
Stacktrace:
   at Microsoft.Exchange.OMA.UserInterface.Global.Session_Start(Object sender, EventArgs e)
   at System.Web.SessionState.SessionStateModule.RaiseOnStart(EventArgs e)
   at System.Web.SessionState.SessionStateModule.CompleteAcquireState()
   at System.Web.SessionState.SessionStateModule.BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData)
   at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

OMA apparently tries to read the password from a System.DirectoryServices.DirectoryEntry instance. When taking a look at the documentation, one soon finds that the Password property is write-only in .NET 2.0, but was read/write in .NET 1.1.

Solution

Configure the WebSite to run with .NET 1.1. You may need to install it on the server first. If other applications are running on the IIS, be sure to move them to another application pool, if they need .NET 2.0.


Posted by Henning Krause on Wednesday, November 26, 2008 5:43 PM, last modified on Wednesday, November 26, 2008 5:43 PM
Permalink | Post RSSRSS comment feed

Getting Mailbox Information On Exchange 2003 using WMI

A recent question in the Microsoft development forum for Exchange was how to get information about mailboxes on Exchange 2003 from an ASP.NET application.

The simplest solution is to use WMI in this case. Exchange exposes certain mailbox statistics via the Exchange_Mailbox WMI class. The following code can be used to iterate through all mailboxes and print some of the properties:

   1: using System;
   2: using System.Management;
   3:  
   4: namespace MailboxSizer
   5: {
   6:     class Program
   7:     {
   8:         static void Main(string[] args)
   9:         {
  10:             var scope = new ManagementScope("\\\\.\\root\\MicrosoftExchangeV2");
  11:             var query = new ObjectQuery("select * from Exchange_Mailbox");
  12:             var searcher = new ManagementObjectSearcher(scope, query);
  13:             foreach (ManagementObject mailbox in searcher.Get())
  14:             {
  15:                 Console.WriteLine("Display name: {0}", mailbox["MailboxDisplayName"]);
  16:                 Console.WriteLine("Size: {0} bytes", mailbox["Size"]);
  17:                 Console.WriteLine("Storage Limit: {0}", (StorageLimitInfo) (uint) mailbox["StorageLimitInfo"]);
  18:  
  19:                 Console.WriteLine();
  20:             }
  21:             Console.ReadLine();
  22:         }
  23:     }
  24:  
  25:     [Flags]
  26:     internal enum StorageLimitInfo
  27:     {
  28:         BelowLimit = 1,
  29:         IssueWarning = 2,
  30:         ProhibitSend = 4,
  31:         NoChecking = 8,
  32:         MailboxDisabled = 16
  33:     }
  34: }

You need to reference the System.Management assembly for this code to work.


Posted by Henning Krause on Monday, May 19, 2008 8:00 PM, last modified on Tuesday, May 20, 2008 11:51 AM
Permalink | Post RSSRSS comment feed