Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Developing Managed Event Sinks/Hooks for Exchange Server Store using C#

0.00/5 (No votes)
17 Dec 2003 5  
This Article explains the Exchange Event Sinks and the creation of Event Sink Store Component using C#. Article also discusses about registering the event sink component to the component services and binding the event sink to an exchange user.

Introduction

When I had a project to develop a Managed Event Sink for Exchange Server Store, I hunted for some information over the web; surprisingly I could not get links that would really help me to get started immediately! Later, it took some time to get an outline on how to create managed event sinks, and at last, when I cracked it, I thought I shall share it :-) Hence, this article.

This article is targeted at intermediate users who have already developed event sinks in the COM World using VB/VC++ and others. If you are new to developing Event Sinks, I suggest that you read �Event Sinks� in the Exchange SDK documentation. The Exchange SDK has an excellent description on Event sinks. Anyways, I shall give a basic introduction to the Event Sinks.

So, what does an Event Sink Really mean?

Event Sink is a piece of code that gets triggered on predetermined events. A more classic jargon I can give as an example is �Hooks�, i.e. we hook and eye to an event [e.g.: a keyboard or event or a mouse event], and when the event occurs our custom code executes first and later the control is passed back to the original event if required.

Exchange Store Events

A similar concept is provided by Exchange Server, where in you can hook[!] or create event sinks for the events occurring at Exchange Server. Some of the events that can be hooked to are

1. Synchronous Events � Events that get triggered before an item [Mail, appointments, documents, tasks etc] is committed to the exchange server. These events pauses the exchange store thread until the event sink finishes executing. No other process can access the item during this event sink execution period as, event sink has the exclusive control over the items. Following are the events that are classified as Synchronous events.

a. OnSyncSave � fires when the item is saved to exchange, but before the changes are committed.

b. OnSyncDelete � fires when the item is deleted from exchange, but before the delete operation is committed.

2.Asynchronous Events � Events that get fired after an item is committed to the exchange server. These Async events do not block the exchange store thread. Following are the Asynchronous events.

a. OnSave � Fires after the item is saved to exchange and changes are committed

b. OnDelete � Fires after the item is deleted from the exchange and changes are committed.

3.System Events� Events that get fired based on some system wide actions on exchange server, the following are the system events.

a. OnMDBStartUp � This fires up when the Exchange Database is started.

b. OnMDBShutdown � This fires up when the Exchange Database is shut down.

c. OnTimer � Executes a piece of code at predefined intervals. This is a very useful event, which runs irrespective of specific events.

Synchronous and Asynchronous events are tied to a specific item or folder in the exchange store.

All these events are exposed in the Exchange CDOEX library [cdoex.dll] as interfaces. Fig 1.1 shows the object browser window of the CDOEX library.

So What? What Can I Build?

Some of the applications that can be developed using Event Sink are,

  1. Notification Subsystems
  2. Global Timer applications
  3. Workflow based applications
  4. Automatic Categorization subsystems
  5. Store maintenance for administrators

Let's Code Now...

Fire up your VS.NET and choose new C# Class library project and name the project, hmm... let�s call it as �MyEventSink�.

On the Solution explorer, right click the project name and choose Properties, on the Project Properties page choose configuration properties choose build and set Register for COM Interop to True.

Now, Copy the below files to the MyEventSink bin directory

  • exoledb.dll from exchange server bin directory (\program files\exchsrvr\bin)
  • cdoex.dll - \program files\common files\Microsoft Shared\CDO
  • msado15.dll - \Program Files\Common Files\System\ADO

Open up the VS.NET Command Prompt and navigate to MyEventSink bin folder, and create strong name keys for the above libraries. Key-in the following commands

> Sn �k exoledb.key
> Sn �k cdoex.key
> Sn �k msado.key

We need to create interop assemblies of the above library, in order to, create the interop assemblies we shall use the tlbimp tool. Key-in the following commands to create 3 interop assemblies.

tlbimp exoledb.dll /keyfile:exoledb.key /out:interop.exoledb.dll /namespace:CDO
tlbimp cdoex.dll /keyfile:cdoex.key /out:interop.cdoex.dll /namespace:CDO
tlbimp msado15.dll /keyfile:msado.key /out:interop.adodb.dll /namespace:ADODB

Copy these interop dll files to the debug folder. Switch back to VS.NET and add references to the above created interop DLL files. Modify the following attributes on the AssemblyInfo.cs

Under General Information section, modify

[assembly: AssemblyTitle("MyEventSink")]
[assembly: AssemblyDescription("My Event Sink - Logu")] 

at version information section, create a new GUID and add

[assembly: Guid("44E6847A-0012-42af-A317-1E1A9F0C853D")]

[Tip: You can create a new GUID by clicking Tools->Create GUID]

at sign information section, modify

[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("MyEventSink.key")]
[assembly: AssemblyKeyName("MyEventSink")] 

Now, Choose Project Properties and set the �Wrapper assembly key file� to MyEventSink.key and �Wrapper assembly Key Name� to �My Event Sink�

Start the VS.NET Command Prompt and change directory to your project directory, and create a key, key-in the following,

> sn �k MyEventSink.key

Switch back to VS.NET IDE, and change the file name of class1.cs to a new name like �ExchEventSink.cs�, double click the .cs file to open.

Add,

using CDO;
using ADODB;

modify the class definition code to resemble like below,

[Guid("16369924-5F32-4E26-AE89-8308B5C162E2")]
public class ExchEventSink: ServicedComponent , IExStoreAsyncEvents
{
public string ClassID = "F92EFC3A-FDD8-4225-B005-13FD3D5D54D1";
public string InterfaceId = "704A413F-F8FE-476B-9206-69AB6300D752";
public string EventsId = "DCC71BD5-6627-4FBF-BB5A-DB8FEA1EB177";
 

if you observe the above code, you can notice that we are implementing the IExStoreAsyncEvents interface, which implements the asynchronous events methods namely onsave and ondelete. We shall implement the same now, add the following to your code [check the attached zip file for more information]

          public void OnSave(IExStoreEventInfo pEventInfo, 
              string bstrURLItem, int lFlags)

            {
                  try
                  {
                        if(System.Convert.ToBoolean(lFlags))
                        {
                              CDO.Message iMessage=new 
                                  CDO.MessageClass();
 
                              string sFrom; string sDate;
            try
                 {
                    iMessage.DataSource.Open(bstrURLItem,null,
                           ADODB.ConnectModeEnum.adModeRead,
                           ADODB.RecordCreateOptionsEnum.adFailIfNotExists,
                           ADODB.RecordOpenOptionsEnum.adOpenSource,"","");
                    FileStream fs = new FileStream(
                        @"c:\temp\MyEventSink.log",FileMode.OpenOrCreate);
                    fs.Write(Encoding.ASCII.GetBytes(bstrURLItem),0,
                        bstrURLItem.Length);
                    sFrom = iMessage.From;
                    sDate = iMessage.ReceivedTime.ToString();
                    fs.Write(Encoding.ASCII.GetBytes(sFrom),0,sFrom.Length);
                    fs.Write(Encoding.ASCII.GetBytes(sDate),0,sDate.Length);
                    fs.Close();
                  }
      catch (Exception ex)
                  {
                     throw (ex);
                  }
            }
    }
                  catch (Exception ex)
                  {
                        throw (ex);
                  }
   }
 
public void OnDelete(IExStoreEventInfo pEventInfo, 
       string bstrURLItem, int lFlags)
 {
    try
      { }
    catch(Exception ex)
      {throw (ex);}
      }
  #endregion

In the above code, we are processing an exchange item on onsave method, and we create a LOG file. This is a simple code example; modify it to your requirements. [Check Exchange SDK on the lflags as this is very important, www.microsoft.com/exchange ]

Compile the class, you have your event sink component ready. Now, Open Component Services, under COM+ applications create new empty application and name it as �MyEventSink�, then, expand, components under MyEventSink and click �import components that are already registered�

And choose �MyEventSink.ExchEventSink� from the populated list.

Now, the event sink component is registered to the server.

We are done on our development part. Now, you can bind the component to any folder of exchange store, there are multiple ways to do this, I prefer the following,

RegEvent.vbs - I�ve attached the VBS file along with the download zip, this script creates the event registration for the specified folder. The following command binds the event sink to my inbox folder,

I�ve included the vbs file along with the zip file, you can also get more information about this at [ www.microsoft.com/exchange ]

Exchange Explorer � this is a tool you get with Exchange SDK [check www.microsoft.com/exchange ]

Alternatively, you can build your own event registration [that�s a separate article by itself :-) ]

At last, we are done... We have created our own Managed Exchange Store Event Sink. You can also implement the Synchronous Events and the System Events as same as we have implemented the Asynchronous events.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here