InfiniTec - Henning Krauses Blog

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

Simple push notification client

As I wrote in my earlier post, I recently published a component on CodePlex that simplifies using push notifications in your applications. Since I’ve not published a full fledged sample application, I will start with a series of blog posts. I will tag all related articles on this topic with “push notifications”, and you can get a list of all articles with this link:

I’ll start with a very simple application: A console application that creates one subscription get notifications about new mails arriving in the mailbox of the current user.

   1: using System;
   2: using System.Net;
   3: using InfiniTec.Exchange.Notifications;
   5: namespace ExchangeNotificationTestClient
   6: {
   7:     internal class Program
   8:     {
   9:         private static void Main()
  10:         {
  11:             // Ignore any certificate errors
  12:             ServicePointManager.ServerCertificateValidationCallback += 
  13:                 (sender, certificate, chain, sslPolicyErrors) => true;
  15:             // Setup the adapter which will be used to call into the Exchange WebService
  16:             var adapter = new ExchangeWebServicesAdapter(
  17:                 new Uri("https://casserver/ews/exchange.asmx"), 
  18:                 new NetworkCredential("administrator", "password", "contoso"));
  20:             // Create a new subscription collection to manage all the subscriptions
  21:             var subscriptionCollection = new SubscriptionCollection(adapter);
  23:             // Setup a listener that listens on port 80 on the local computer
  24:             using (var listener = new PushNotificationListener())
  25:             {
  26:                 // Register for a NewMail notification on the inbox of the administrator
  27:                 subscriptionCollection.Add(
  28:                     new[] {new FolderReference(WellKnownFolderId.Inbox)}, 
  29:                     EventTypes.NewMail);
  30:                 Console.Out.WriteLine("Starting Notification Service...");
  31:                 listener.Start();
  33:                 Console.Out.WriteLine("Creating subscription");
  35:                 foreach (var subscription in subscriptionCollection)
  36:                 {
  37:                     // Write a line to the console for each new mail received
  38:                     subscription.NewMail += (sender, e) => 
  39:                         Console.Out.WriteLine(string.Format("{0}: New Mail arrived in your inbox", e.Timestamp));
  40:                     subscription.Start(listener);
  41:                 }
  43:                 Console.Out.WriteLine("Waiting for notifications... Hit [Enter] to quit...");
  45:                 Console.ReadLine();
  46:             }
  47:         }
  48:     }
  49: }

There are four important classes used in this example:

  1. The ExchangeServiceAdapter (created in line 16) is used to actually perform the Web Services calls to the Exchange Server (CAS Role). It’s a rather simple implementation, as it does not support AutoDiscover. You have to specify the Exchange server manually. It does, however, support Exchange Impersonation. And if you want to subscribe to events on public folders, you’ll have to enable this feature by setting the ExchangeServiceAdapter.IsPublicFolderAccessEnabled to true. Your Exchange Server must have Service Pack 1 installed to use this feature.
  2. The PushNotificationListener (created in line 24): This class does all the necessary WCF plumbing to setup a host, receives the notifications and channels them to the right subscription.
  3. The SubscriptionCollection: (created in line 21) It’s not really necessary, but it makes is more easy to handle multiple subscriptions.
  4. The Subscription (created in line 27). You can either create a subscription by calling SubscriptionCollection.Add(), or create a subscription directly via “new Subscription()”.

Both, the PushNotificationListener and the Subscription need to be started to do some actual work. And that’s all you need to do to get a simple notification client up and running. However, there are some security settings to consider, because WCF doesn’t let you run around and open endpoints on your users machines. Additionally, the Windows Firewall needs to be configured correctly to let the notifications through. I’ll discuss this in a separate post.

Posted by Henning Krause on Tuesday, December 30, 2008 10:55 PM, last modified on Saturday, November 27, 2010 6:04 AM
Permalink | Post RSSRSS comment feed

Exchange 2007 Push notifications made easy

Exchange 2007 de-emphasized Exchange Event Sinks in favor of a new notification system which allows an application to receive notifications about changes made to items on a public folder or mailbox folder. There are two types of notifications: Pull and Push. With pull notifications, the calling application is responsible to get changes back from the server (it has to poll the CAS role for changes). Push notifications on the other side use Web Services to call into an external application. There are a restrictions where this does not work (for example firewalls blocking traffic), but otherwise they work really well.

Compared to the WebDAV notifications that where available in earlier versions of Exchange, they have the advantage that the external application gets specific information about the event: Which item has been modified, created, deleted?

However, it’s rather complicated to completely implement a listener for these notifications. Therefore, I have created a wrapper which uses WCF to listen for incoming notifications.

The whole package is available from CodePlex (, and there is a list of features. Only the source and a small help file for now, but I will add samples over time.

If you have feedback, please use CodePlex Discussions or send me a mail by using the contact form.

Posted by Henning Krause on Tuesday, December 30, 2008 1:51 PM, last modified on Monday, November 29, 2010 6:03 PM
Permalink | Post RSSRSS comment feed

EventSink Registration WHERE Problems

A reader recently asked how he could register an Exchange 2000/2003/2007 EventSink so that it would only fire for items with these properties:

  • Not hidden
  • An appointment item.
  • The appointment is not private/personal/confidential.
  • The appointment is labeled blue/business/label (this is stored on property{00062002-0000-0000-C000-000000000046}/0x8214 which is of type int. The required value is 2).

He tried registering the EventSink with the following restriction for the RegEvent.vbs script:

"WHERE $DAV:ishidden$ = FALSE AND $DAV:contentclass$ = 'urn:content-classes:appointment' AND $$ = 0 AND ${00062002-0000-0000-C000-000000000046}/0x8214$ = 2"

This didn’t work. The problem is that Exchange is not very smart about guessing the types of properties which are not in it’s schema. It treats them all as strings. So, while a simple comparison is sufficient for the sensivity property, the appointment color property must be cast to int.

The correct where clause is this:

"WHERE $DAV:ishidden$ = FALSE AND $DAV:contentclass$ = 'urn:content-classes:appointment' AND $$ = 0 AND Cast(${00062002-0000-0000-C000-000000000046}/0x8214$ as int) = 2"

Posted by Henning Krause on Friday, October 24, 2008 5:22 PM, last modified on Friday, October 24, 2008 5:22 PM
Permalink | Post RSSRSS comment feed

Exchange Eventsink Foundation

Although Eventsinks are being removed from the next version of Exchange (see Exchange Developer Roadmap - WebDAV and StoreEvents gone), they are widely used and sometimes the only option. Especially when working with tasks, contacts and appointments.

But writing Eventsinks has always been a pain because all this interop stuff, COM+ registration issues and more. Attached to this post you can find a small framework which simplifies writing event sinks (both synchronous and asynchronous ones).

The InfiniTec.Exchange.Eventing assembly contains several base classes from which you inherit your Eventsink. The only thing you have to do is to overwrite some methods (OnItemChanged, OnItemCreated, OnItemCreated) and you get relevant information about the event which happened in a compact object (EventInfo class). Below is a class diagram of the InfiniTec.Exchange.Eventing namespace:


Depending whether you want to create an asynchronous or synchronous Eventsink, you derive your sink from either SynchronousEventSink or AsynchronousEventSink. In you derived class, you simply override the notifications you want to catch. One thing all Eventsinks have in common is the registration process – Implemented by the OnRegisteringEvent. By returning false from this method you can prevent the registration on a certain url.

The synchronous Eventsink is called two times for each event: The first execution is called the Begin Phase; during this stage, the item is writable and you can even check for changes by loading the original item (via EventInfo.OpenItemLocally()). The seconds stage is called Commit Phase; the item is read-only now. If you need to share information about a particular event between these to stages, set the EventInfo.UserState property. The object graph is serialized using the BinarySerializer during the begin phase, so each instance used here must be serializable. This is a feature which was not possible with the interop files generated with tlbimp, because that tool did not generate the proper interop code. Since I’ve incorporated the complete interop code in this assembly, I fixed the signature of the affected interface.

Correctly deciphering the flags passed to the Evensink was an art of it’s own, so I’ve cleaned up those flags enumerations as well and routed them different methods (OnItemCreated with its CreationMode parameter and OnItemDeleted with its DeletionMode parameter).

At last, you don’t need the the regevent.vbs script any longer because I’ve included an EventSinkInstaller which registers your EventSink on a certain folder.

Here is a sample event sink:

   1: using System;
   2: using System.Runtime.InteropServices;
   3: using ADODB;
   4: using InfiniTec.Exchange.Eventing;
   6: namespace TestSink
   7: {
   8:     [ComVisible(true)]
   9:     [Guid("FD0D03A3-9FD2-432b-B331-E7C4D412827F")]
  10:     [ProgId("TestSink.TestSink")]
  11:     public class TestSink: SynchronousEventSink
  12:     {
  13:         protected override void OnItemCreating(EventInfo info, CreationMode creationMode)
  14:         {
  15:             // Do something of interest here - you have read/write  access to the item
  16:             Record item = info.Item;
  18:             // you can save an object here (it must be serializable) and reuse it during the OnItemCreated method
  19:             info.UserState = "test";
  22:             base.OnItemCreating(info, creationMode);
  23:         }
  25:         protected override void OnItemCreated(EventInfo info, CreationMode creationMode)
  26:         {
  27:             base.OnItemCreated(info, creationMode);
  29:             // Do something of interest here - the item is readonly now.
  31:             // will be "test"
  32:             string s = (string) info.UserState;
  33:         }
  36:         protected override void OnItemDeleted(EventInfo info, DeletionMode deletionMode)
  37:         {
  38:             base.OnItemDeleted(info, deletionMode);
  39:         }
  41:         protected override void OnItemUpdated(EventInfo info)
  42:         {
  43:             base.OnItemUpdated(info);
  44:         }
  46:         protected override void OnErrorOccured(EventInfo eventInfo, Exception ex)
  47:         {
  48:             base.OnErrorOccured(eventInfo, ex);
  49:         }
  51:         protected override void OnInitialize()
  52:         {
  53:             base.OnInitialize();
  54:         }
  56:         protected override bool OnRegisteringEvent(EventInfo info)
  57:         {
  58:             return base.OnRegisteringEvent(info);
  59:         }
  61:         protected override void OnItemCreationAborted(EventInfo info, CreationMode creationMode)
  62:         {
  63:             base.OnItemCreationAborted(info, creationMode);
  64:         }
  66:         protected override void OnItemDeleting(EventInfo info, DeletionMode deletionMode)
  67:         {
  68:             base.OnItemDeleting(info, deletionMode);
  69:         }
  71:         protected override void OnItemDeletionAborted(EventInfo info, DeletionMode deletionMode)
  72:         {
  73:             base.OnItemDeletionAborted(info, deletionMode);
  74:         }
  76:         protected override void OnItemSilentlySaving(EventInfo info)
  77:         {
  78:             base.OnItemSilentlySaving(info);
  79:         }
  81:         protected override void OnItemUpdateAborted(EventInfo info)
  82:         {
  83:             base.OnItemUpdateAborted(info);
  84:         }
  86:         protected override void OnItemUpdating(EventInfo info)
  87:         {
  88:             base.OnItemUpdating(info);
  89:         }
  90:     }
  91: }

As I did here, you should always add those three attributes to the class, albeit with different values for the Guid and ProgId attribute. And you should add these lines to your assemblyinfo file:

   1: [assembly: ApplicationActivation(ActivationOption.Server)]
   2: [assembly: ApplicationName("TestSink")]
   3: [assembly: ApplicationAccessControl(false, AccessChecksLevel = AccessChecksLevelOption.Application)]

Be sure to customize the ApplicationName attribute, though.

Now that you have created an Eventsink, you should add an installer so you can deploy the solution more easily:

   1: using System.Collections;
   2: using System.ComponentModel;
   3: using System.Configuration.Install;
   4: using InfiniTec.Exchange.Eventing;
   6: namespace TestSink
   7: {
   8:     [RunInstaller(true)]
   9:     public class TestInstaller: Installer
  10:     {
  11:         public override void Commit(IDictionary savedState)
  12:         {
  13:             Initialize();
  14:             base.Commit(savedState);
  15:         }
  17:         public override void Install(IDictionary stateSaver)
  18:         {
  19:             Initialize();
  20:             base.Install(stateSaver);
  21:         }
  23:         public override void Rollback(IDictionary savedState)
  24:         {
  25:             Initialize();
  26:             base.Rollback(savedState);
  27:         }
  29:         public override void Uninstall(IDictionary savedState)
  30:         {
  31:             Initialize();
  33:             base.Uninstall(savedState);
  34:         }
  36:         private void Initialize()
  37:         {
  38:             var url = Context.Parameters["url"] + "/eventsink.evt";
  40:             Context.LogMessage("Binding Url: " + url);
  41:             string criteria = Context.Parameters["Criteria"] ?? string.Empty;
  42:             criteria = criteria.Replace("$", "\"");
  44:             Installers.Add(new EventSinkInstaller<TestSink>
  45:                                {
  46:                                    Criteria = criteria,
  47:                                    EventMethods = EventMethods.SynchronousEvents,
  48:                                    Scope = MatchScope.Shallow,
  49:                                    Url = url
  50:                                });
  51:         }
  52:     }
  53: }

The reason why the Initialize() method is called in each of the methods is that the installer requires the url to the path on which the sink is registered. Unfortunately, the Context property is set after the constructor is run. What you basically need to do is  to add an EventSinkInstaller the list of installers executed. The EventSinkInstaller has a type parameter which you can use to specify the type of sink registered. The installer will use this type parameter to determine the correct ProgId for the Eventsink. This example registers a synchronous event with a MatchScope of Shallow on the folder specified via the “url” parameter. Additionally, the constraint for the Eventsink is taken from the “constraint” parameter. This approach is superior to the regevent.vbs script because it lets you as the developer decide which parameters are fixed (the scope and type of the EventSink in this case) and which are variable, making the whole registration purpose less prone to errors.


The InfiniTec.Exchange.Eventing assembly also defines a TraceSource which you can use for debugging. The TestSink application attached to this article contains the following application.config to activate the trace component:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.diagnostics>
   4:         <trace autoflush="true" />
   5:         <sources>
   6:             <source name=""
   7:               switchName="verbosity"
   8:               switchType="System.Diagnostics.SourceSwitch">
   9:         <listeners>
  10:           <add name="listener"
  11:            type="System.Diagnostics.TextWriterTraceListener"
  12:            initializeData="c:\temp\TextWriterOutput.log" />
  13:         </listeners>
  14:       </source>
  15:         </sources>
  16:         <switches>
  17:             <add name="verbosity" value="All"/>
  18:         </switches>
  19:     </system.diagnostics>
  20: </configuration>


You may use this code (InfiniTec.Exchange.Eventing and InfiniTec.Common) in your application, regardless whether it is personal or business, free of charge.


Tags: , ,

Technorati: , ,

Posted by Henning Krause on Tuesday, July 22, 2008 5:16 PM, last modified on Tuesday, July 22, 2008 5:18 PM
Permalink | Post RSSRSS comment feed

Exchange 2007 store event sinks registration woes

In this article, I wrote about how to write managed event sinks for Exchange 2003 and how to register them with the regsvcs tool on the Exchange server. Normally this goes like this:

    1 c:\Windows\Microsoft.NET\Framework\v2.0.50727\RegSvcs.exe -fc ManagedEventSink.dll

This command will register the necessary COM types and create a new COM+ application on the server. If you try this on a 64-bit Exchange 2007, you'll encounter an error like this:

Microsoft (R) .NET Framework Services Installation Utility Version 2.0.50727.42
Copyright (c) Microsoft Corporation.  All rights reserved.

Type library exporter warning: Referenced type is defined in managed component, which is imported from a type library that could not be loaded because it was not registered (type: 'Microsoft.Interop.Exoledb.IExStoreSyncEvents'; component: 'C:\EventSink\Microsoft.Interop.Exoledb.dll').
Type library exporter warning: Referenced type is defined in managed component, which is imported from a type library that could not be loaded because it was not registered (type: 'Microsoft.Interop.Exoledb.ICreateRegistration'; component: 'C:\EventSink\Microsoft.Interop.Exoledb.dll').
Installed Assembly:
        Assembly: C:\EventSink\ManagedEventSink.dll
        Application: Managed EventSink Example
        TypeLib: C:\EventSink\ManagedEventSink.tlb

This error occurs because Exchange 2007 is a 64bit application, and so is the ExOleDb provider which is used for store event sinks. To create the COM+ application, you should call the RegSvcs tool from the 64bit framework directory, which is located under C:\Windows\Microsoft.NET\Framework64\v2.0.50727.

Once you have created the COM+ application, you can proceed normally by configuring it (as described here and here) and register the eventsink in the store.

Storewide event sinks

To register a store-wide event sink, the GUID of the mailbox store is needed for the registration. In Exchange 2000/2003, this info was most easily obtained from Active Directory Users and Computers:

  1. Open AD User & Computers
  2. Enable the “Advanced Features” item In the “View" Menu
  3. Navigate to the hidden folder “Microsoft Exchange System Objects”
  4. Each mailbox store has a SystemMailbox object associated with it in this folder. Select the mailbox from the store you want to install the sink on. The name of the System Mailbox contains the relevant GUID for the registration.
  5. Copy the GUID from the name of the mailbox to the clipboard.

Although Exchange 2007 lacks the AD Users & Computers integration, this still works; albeit the fancy icons are gone, as shown in this example:


But with Exchange 2007, there is another option to quickly get the GUID: PowerShell. Just open the Exchange Management Shell and enter this command:

   1: Get-MailboxDatabase | Select-Object -Property StorageGroupName,Guid

This will return a list of all mailbox stores and their corresponding GUIDs.

The event can now be registered on the store of choice:

   1: regevent.cmd add "onsyncsave;onsyncdelete" EventSink.ProgId file://./backofficestorage/ADMIN/%primarysmtpdomain%/MBX/SystemMailbox%mailboxguid%/StoreEvents/GlobalEvents/EventSinkName.evt -m ANY -f "WHERE $DAV:ishidden$ = false AND $DAV:isfolder$ = false"

Replace the EventSink.ProgId with the ProgId of your sink, the %primarysmtpdomain% with your primary smtp domain and the %mailboxguid% with the GUID of the store you want to register the sink on.

Posted by Henning Krause on Monday, August 20, 2007 12:00 AM, last modified on Wednesday, June 11, 2008 7:42 AM
Permalink | Post RSSRSS comment feed

Iterating through all mailboxes in an Exchange 2000/2003 organization

Description [Updated]


  • 2008-07-23: Corrected intra-site links.

One question that came up lately in the newsgroups is: How can I find messages containing certain keywords in the mailboxes of all users in my organization?

This scenario is not directly supported by Exchange. All one can do is to search each mailbox individually.

You must follow these steps to do the search over all mailboxes:

  1. Enumerate the users which have a mailbox. Essentially, these are the users appearing on the global address list. See How to get the Global Address List programatically for more information on how to do this.
  2. Build the mailbox url which can be used to access the mailbox via WebDAV or ExOleDB. See Get the WebDAV url for an Exchange 2000/2003 mailbox on how to do this. If you are using the ExOleDB provider or want to use the administrative virtual root instead, see the remarks section for more information.
  3. Once you have the url for the mailbox you can start accessing it. If you must support different languages, see Getting Well-Known Mailbox Folder URLs on MSDN to get the url of the default folders.
  4. If you are using WebDAV and have Form-based-authentication enabled on your server, you must do a manual logon to the mailbox. See Access the Exchange store via WebDAV with Form-Based-Authentication turned on.



Depending on how you want to access the mailboxes, you need different permissions:

  • If you are using the normal urls (e.g. http://myserver/exchange/username), you need access permissions to all mailboxes on the MAPI level. See HOWTO: Grant access to all mailboxes on a mailbox store to a special account on how to do this. If you have more than one mailbox store, you should grant the necessary permissions on each mailbox store. To simplify this process, you could also grant the "Send as" and "Receive as" permission on the Administrative Groups container via ADSIEdit.msc instead of each mailbox store.
  • You can also use the administrative virtual root. This method is used by the Exchange Systems manager, and it is available via WebDAV and ExOleDB. The normal MAPI permissions are completely ignored when using this method, but an administrative account is required to use this method (See Working with Store Permissions in Microsoft Exchange 2000 and 2003 on Technet for more information on this topic).

Mailbox urls

  • If you are using WebDAV to access the store, you can simply build the mailbox url based on the article Get the WebDAV url for an Exchange 2000/2003 mailbox. To use the administrative root instead, modify the url from http://myserver/exchange/mailboxname to http://myserver/exadmin/admin/<dsndomainname>/mbx/<mailboxname>. You must replace the <dnsdomainname> with the primary smtp domain name of your organization.
  • If you are using ExOleDB, you must modify the address from http://myserver/exchange/mailboxname to file://./backofficestorage/<dnsdomainname>/mbx/<mailboxname>. To use the administrative virtual root, change this url to file://./backofficestorage/admin/<dnsdomainname>/mbx/<mailboxname>.

Posted by Henning Krause on Thursday, April 13, 2006 12:00 AM, last modified on Wednesday, July 23, 2008 10:59 PM
Permalink | Post RSSRSS comment feed

Get the WebDAV url for an Exchange 2000/2003 mailbox

More Information

For Exchange 2000 and Exchange 2003 prior Service Pack 1, the building of an URL suitable for WebDAV requests against a users mailbox is a rather complicated thing. A solution for this is descrbied under the section Solution.

From Exchange 2003 SP 1 onwards, you can just use his SMTP address to get access to his mailbox. For example, to access the mailbox of John Doe ( you would use the url



To build the webaddress, you must do the following:

  1. If you only have a domain\username, not the distinguished name of the user, you must first get the latter one. See this aricle how to retrieve the name.
  2. Get the directory entry with the distinguished name of the user
  3. From that object, retrieve the property homemdb. This property contains the distinguished name of the mailbox store.
  4. Retrieve the directory entry of the mailbox store.
  5. From this object, retrieve the property named msExchOwningServer. This is the distinguished name of the Exchange server that hosts the mailbox.
  6. Get the root of the http virtual server on the exchange server. The distinguished name is CN=http, CN=Protocols, <distinguished name of the exchange server>.
  7. Search for all http virtual server (LDAP filter: (objectClass=protocolCfgHttpServer), Scope: OneLevel, retrieve these properties: msExchServerBindings, msExchDefaultDomain).
  8. From this list, retrieve the default SMTP server. It's the one without the attribute msExchDefaultDomain.
  9. Get the default SMTP domain of the organization:
    1. Open the node CN=Default Policy, CN=Recipient Policies, CN=<Name of your organization>, CN=Microsoft Exchange, CN=Services,<Distinguished name of the configuration naming context>.
    2. Retrieve the property gatewayProxy. This is a multi-valued property that contains the default addresses of the organization.
    3. Check each of the entries in that list. If it starts with SMTP:, you have found it.
  10. Get the property proxyAddresses from the directory object of the user. This is a multi-valued properties that contains all addresses that are assigned to the user. Each entry has the following consists of a protocol identifier and an address entry. For example:
    If the protocol identifier is all uppercase, it is the default entry for that protocol.
  11. Iterate through the list of assigned addresses and search for the SMTP address which domain part matches with the default SMTP retrieved above.
  12. Select the correct HTTP Server:
    • If you found an email address corresponding to the default SMTP policy, extract the alias from the found address. For the above example the alias would be john.doe.  The correct HTTP server to use is the default HTTP Server
    • If there is no smtp address matching the default policy, iterate through the list of proxy addresses again, and do the following, if it is an SMTP address:
      1. Extract the alias and the domain from the address.

      2. Iterate through the list of virtual HTTP server and check the property msExchDefaultDomain of each entry. If it matches the domain of the users address domain, you have found the correct http server.

  13. Get the property msExchServerBindings from the selected HTTP server node. This entry has the following structure:
    The first and the last part of the string can be empty, like in this example:
  14. Depending on the content of this field, you can now build the desired url:
    • If the servername of the msExchServerBindings property is not empty, build the OWA url like this:
    • If the servername is empty, but an ipaddress is present, build the OWA url like this:
    • If both, the servername and the ipaddress fields are empty, get the ipaddress for the exchange server:
      1. From the node of the exchange server, retrieve the property networkaddress.
      2. Iterate through this property and retrieve the value which starts with ncacn_ip_tcp:.
      3. Now, build the url with the above scheme, using the just found ip address.

Tags: , , , , ,

Technorati: , , , , ,

Posted by Henning Krause on Friday, December 31, 2004 12:00 AM, last modified on Tuesday, July 26, 2011 9:57 PM
Permalink | Post RSSRSS comment feed