Click here to Skip to main content
15,879,326 members
Articles / Programming Languages / Visual Basic
Article

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

Rate me:
Please Sign up or sign in to vote.
4.27/5 (4 votes)
2 Feb 20074 min read 131.3K   249   30   40
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.

Image 1

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.

Image 2

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

    Image 3

  • 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

    Image 4

  • 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

    Image 5

Add References to Your Project

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

Image 6

Add a reference to System.EnterpriseServices

Image 7

Your reference section in you Solution Explorer should appear as follows

Image 8

Modify the AssemblyInfo.vb File

Add the following to the top of the file

Imports System.EnterpriseServices

Insert the following lines

VB
[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.

VB
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.

Image 9

Image 10

Image 11

Image 12

Image 13

Image 14

Image 15

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.

Image 16

Image 17

Image 18

Image 19

Image 20

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.

Image 21

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


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to get public folder path? Pin
baburman18-May-10 9:07
baburman18-May-10 9:07 
AnswerRe: How to get public folder path? Pin
Andy McNiece18-May-10 14:26
Andy McNiece18-May-10 14:26 
GeneralRe: How to get public folder path? Pin
baburman18-May-10 22:31
baburman18-May-10 22:31 
GeneralRe: How to get public folder path? Pin
Andy McNiece19-May-10 3:55
Andy McNiece19-May-10 3:55 
GeneralRe: How to get public folder path? Pin
baburman19-May-10 21:18
baburman19-May-10 21:18 
GeneralRe: How to get public folder path? Pin
baburman28-May-10 1:47
baburman28-May-10 1:47 
GeneralRe: How to get public folder path? Pin
Andy McNiece28-May-10 4:06
Andy McNiece28-May-10 4:06 
GeneralRe: How to get public folder path? Pin
baburman28-May-10 7:04
baburman28-May-10 7:04 
GeneralRe: How to get public folder path? Pin
baburman29-May-10 20:12
baburman29-May-10 20:12 
GeneralDetails of modified occurance of recurring meeting is not getting in EventSink [modified] Pin
ranjitcherian29-Jun-09 3:17
ranjitcherian29-Jun-09 3:17 
GeneralAttach events to any user mailbox Pin
eyanson13-Oct-08 8:06
eyanson13-Oct-08 8:06 
Hi, in your coude, you attach the script to PUBLIC FOLDERS, but how do I attach script to any mailbox present in the exchange?

I trow whith this command but the event don´t fired because SystemMailbox don´t update during mail sending.

regevent.vbs add "onsave;ondelete" ExchangeServerExtension.Monitor "file://./backofficestorage/ADMIN/%userdnsdomain%/mbx/SystemMailbox{34901307-1b80-4de4-ae34-0766f00bbc86}/StoreEvents/GlobalEvents/testsink" -m deep


thanks
GeneralRe: Attach events to any user mailbox Pin
eyanson13-Oct-08 10:41
eyanson13-Oct-08 10:41 
QuestionPermission for access to message Pin
MIRo2k25-Jun-08 22:49
MIRo2k25-Jun-08 22:49 
AnswerRe: Permission for access to message Pin
Andy McNiece2-Jul-08 5:31
Andy McNiece2-Jul-08 5:31 
GeneralRe: Permission for access to message Pin
MIRo2k2-Jul-08 5:59
MIRo2k2-Jul-08 5:59 
QuestionIAsyncNotify Interface - returning S_PENDING to process item on another thread Pin
Andrew from BDC16-Mar-08 23:42
Andrew from BDC16-Mar-08 23:42 
GeneralEventsink stops Pin
Dabu125-Jan-08 1:35
Dabu125-Jan-08 1:35 
GeneralRe: Eventsink stops Pin
Andy McNiece27-Jan-08 6:49
Andy McNiece27-Jan-08 6:49 
QuestionAccess denied, error open record Pin
jch000131-Oct-07 5:01
jch000131-Oct-07 5:01 
Generalevents wont fire Pin
Bjkiz11-Aug-07 7:23
Bjkiz11-Aug-07 7:23 
GeneralRe: events wont fire Pin
Andy McNiece13-Aug-07 6:31
Andy McNiece13-Aug-07 6:31 
GeneralRe: events wont fire Pin
Bjkiz13-Aug-07 7:04
Bjkiz13-Aug-07 7:04 
GeneralRe: events wont fire Pin
Andy McNiece13-Aug-07 7:18
Andy McNiece13-Aug-07 7:18 
QuestionCannot register using cscript RegEvent.vbs Pin
Aximili31-Jul-07 17:01
professionalAximili31-Jul-07 17:01 
AnswerRe: Cannot register using cscript RegEvent.vbs Pin
Andy McNiece1-Aug-07 4:13
Andy McNiece1-Aug-07 4:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.