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

Developing Managed Event Sinks for Exchange Server Using Visual Basic.Net

0.00/5 (No votes)
2 Feb 2007 4  
Developing Managed Event Sinks for Exchange Server using VB.Net

Introduction

I use Event Sinks with Exchange Server 2003 frequently. Most of these were written in VB6 and I wanted to begin migrating them over to .Net and use Visual Studio 2005 as the development tool. In searching for coding help I encountered the article Developing Managed Event Sinks/Hooks for Exchange Server Store using C# by Logu Krishnan here at The Code Project. Although written in C# it was very illuminating as to the process required. The steps to do this project in VB.Net are not drastically different but they do require some differences. When I searched other areas for code samples, I never found any that did as good a job as Logu Krishnan had done with his article.

Exchange Store Events

This article focuses on Asynchronous Events. Specifically the events are OnSyncSave and OnSyncDelete. The article by Logu Krishnan previously referenced has an excellent description of Exchange Store Events.

The Purpose of the Sink

This sink is designed to be implemented against a mail enabled public folder. It fires each time a message is saved or deleted in the folder. The code extracts any attachments from the message, saves them to a target network location, copies the message to a sub folder named "Processed" and then deletes it from the original folder. I have not included the code for actually processing the attachment but it is fairly straightforward and I do a variety of tasks with the attachments based on the purpose of the folder. I have included detailed steps to create and implement the sink.

Create the Project

Create your project using the Windows Class Library template. I have named this project EventSinkNet.

Mark for COM Interop

Be sure to mark the project to Register for Comp Interop by clicking on Project from the menu, choosing EventSinkNet Properties, selecting the Compile tab on the left and clicking on Register for COM Interop at the bottom.

Copy Required Files to Project

  • Copy adodb.dll (file version 7.10.3077.0) from \Program Files\Microsoft.NET\Primary Interop.Assemblies to project directory.
  • Copy codex.dll (Microsoft CDO for Microsoft Exchange Library - file version 6.5.7650.29) from \Program Files\Common Files\Microsoft Shared\CDO\cdoex.dll. This was on my Exchange Server.
  • Copy exoledb.dll (Microsoft Exchange OLEDB Server - file version 6.5.7651.60) from \exchsrvr\bin\exoledb.dll). This was on my Exchange Server.
  • Copy exevtsnk.tlb (Type Library - Size 11,976 bytes) from \Program Files\Exchange SDK\SDK\Support\OLEDB\exevtsnk.tlb

Create Strong Name Keys

Use the Visual Studio Command Prompt and navigate to the project folder. Create the strong name keys with the following commands.

  1. sn.exe -k adodb.key
  2. sn.exe -k cdoex.key
  3. sn.exe -k exoledb.key
  4. sn.exe -k exevtsnk.key

Create Interop Assemblies

  • Open the Visual Studio 2005 Command Prompt and navigate to the project folder.
  • Key in the following command to create the exoledb assembly
    tlbimp exoledb.dll /keyfile:exoledb.key /out:interop.exoledb.dll

    The output from the command may be reflect the following error but can be ignored

  • Key in the following command to create the cdoex assembly

    tlbimp cdoex.dll /keyfile:cdoex.key /out:interop.cdoex.dll

    The output from the command should reflect the following

  • Key in the following command to create the exevtsnk assembly

    tlbimp exevtsnk.dll /keyfile:exevtsnk.key /out:interop.exevtsnk.dll

    The output from the command may be reflect the following error but can be ignored

Add References to Your Project

Add references to the four dll's that are in your project directory.

Add a reference to System.EnterpriseServices

Your reference section in you Solution Explorer should appear as follows

Modify the AssemblyInfo.vb File

Add the following to the top of the file

Imports System.EnterpriseServices

Insert the following lines

[Assembly: AssemblyKeyFile(
    "EventSinkNet.snk")]
[Assembly: AssemblyKeyName(
    "EventSinkNet")]
[Assembly: ApplicationActivation(ActivationOption.Server)>
[Assembly: ApplicationName(
    "EventSinkDLL")>

The Code

The sink acts on each message after it is saved to the store. It opens each message and extracts each attachment to a file on a network share. It then copies the message to a subfolder named "Processed" and deletes the original message.

Option Explicit On
Option Strict On

'Add project references to the System.EnterpriseServices, 

'ADODB, Interop.Exoledb, and SignedExevtsnk .NET components.

Imports System.IO
Imports System.EnterpriseServices
Imports Exoledb = Interop.Exoledb
Imports ExevtsnkLib = Interop.Exevtsnk
Imports CDO = Interop.cdoex
Imports ADODB

Namespace EvSink
    Public Class ASyncEvents
        Inherits ServicedComponent
        Implements Exoledb.IExStoreAsyncEvents

        ' Logfile path.

        Private Const LOGFILE As String = "C:\\evtlog.txt"
        Private Const WORKPATH As String = "\\apps00\
            shared$\SSWork\"

        Public Sub OnDelete(ByVal pEventInfo As interop
            .exoledb.IExStoreEventInfo, _
            ByVal bstrURLItem As String, ByVal lFlags 
            As Integer) _
            Implements interop.exoledb.IExStoreAsyncEvents
            .OnDelete
            ' do something here 
        End Sub

        Public Sub OnSave(ByVal pEventInfo As interop
            .exoledb.IExStoreEventInfo, _
            ByVal bstrURLItem As String, ByVal lFlags 
            As Integer) _
            Implements interop.exoledb.IExStoreAsyncEvents
            .OnSave
            Dim sr As StreamWriter
            Dim msg As CDO.IMessage = New CDO.Message
            Dim atch As CDO.IBodyPart

            Dim rec As ADODB.Record
            Dim i As Integer
            Dim sURLSuffix As String
            Dim sURLPrefix As String
            Dim sURLItemTo As String
            Dim sAtch As String

            ' Open the log file, append text to file.
            sr = File.AppendText(LOGFILE)
            sr.WriteLine("In onsave")
            
            ' Parse out the components of the URL
            i = InStrRev(bstrURLItem, "/", -1, 
                CompareMethod.Text)
            sURLSuffix = Right(bstrURLItem, Len(
                bstrURLItem) - i)
            sURLPrefix = Left(bstrURLItem, i)
            sURLItemTo = sURLPrefix & "processed/" & 
                         sURLSuffix

            ' Get the message
            Try
                msg.DataSource.Open(bstrURLItem, _
                             Nothing, _
                             ConnectModeEnum.adModeRead, _
                             RecordCreateOptionsEnum
                                .adFailIfNotExists, _
                             RecordOpenOptionsEnum
                                .adOpenSource, _
                             Nothing, Nothing)
                sr.WriteLine("Opened message")
                ' process it
                If msg.Attachments.Count > 0 Then
                    For Each atch In msg.Attachments
                        sAtch = atch.FileName
                        atch.SaveToFile(WORKPATH & sAtch)
                        sr.WriteLine("Saved as: " & sAtch)
                    Next

                End If
                ' save it to processed folder
                msg.DataSource.SaveTo(sURLItemTo)
                sr.WriteLine("Saved to: " & sURLItemTo)
                atch = Nothing
                msg = Nothing

                ' delete it
                rec = New ADODB.Record
                rec.Open(sURLPrefix, , ConnectModeEnum
                    .adModeReadWrite)
                rec.DeleteRecord(sURLSuffix)
                sr.WriteLine("Deleted record")
                rec.Close()
            Catch ex As Exception
                sr.WriteLine("Record Delete Exception 
                    message: " & ex.Message)
            End Try
            
            sr.Close()
        End Sub
    End Class
End Namespace

Create a Key Pair for the DLL

sn -k EventSinkNet.snk

Compile and Copy

I had to manually copy the adodb.dll to my Debug folder. Now copy the files from your Debug or Release folder to a folder on the Exchange Server.

Register the Assembly

On the Exchange Server, open a command prompt in the folder containing the sink. Run the regasm.exe command as shown below. The command should indicate "Types registered successfully".

Regasm.exe EventSinkNet.dll /codebase 

Create the Component Service

Open the Administrative Tools/Component Services to install the dll. I create an empty application named "EventSinkNet" and clicked through the default options. I assigned a user to the Application Identity account to run the sink.

Install the Component

I then installed the component by select the Component Folder under the new component named "EventSinkNet" and choosing the "New" option under the properties. I selected "Import component(s) that are already registered" and located the component named EventSinkNet.EvSink.ASyncEvents.

Register the Sink

The sink can be registered to any Exchange folder. A script file like the one below can be used to register the sink or you can use Exchange Explorer with comes with the Exchange SDK.

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