Store Event Sink Troubleshooting guide

David Rousset – Microsoft France

EMEA Developer Support Enginner

Table of content

Store Event Sink Troubleshooting guide 1

Table of content 2

1. Introduction 2

2. Registrations problems 2

3. Execution problems 3

Prerequisites: how to identify the process associated to your sink? 3

Case 1: we’re suspecting the code of the event sink to be bad designed 5

Creating the Diagnostic Sink using Visual Basic 6.0 5

Setting up the Diagnostic Sink 8

Creating a COM+ Event Sink Application 8

Registering the Event Sink using Exchange Explorer 8

Advanced Debugging using WinDBG 11

Case 2: the code normally works but the machine got problems 16

Check the COM+ application settings 16

Check the registration items inside the SystemMailbox special folders 16

Get the Exchange Application logs for review 17

Verify that the sink is really loaded using Process Explorer 18

Check you don’t have any problem accessing to the registry or to the file system inside your event sink 18

4. Links to documentation 19

1.  Introduction

Here are the most common issues I’ve faced during the last years while helping our customer in Europe around Exchange 2000/2003 store event sinks. Most of the time, these notes help me to resolve, let’s say, 70% of the incoming issues on event sinks. Another 25% are often linked to the code itself having a problem or not doing what it’s supposed to do because of a base design. At last, the remaining 5% were linked to a more severe problem because of a bug from one of our product or a very instable Exchange configuration. I then truly hope that you, the reader, will fall into the 70% part of the event sink’s issues and that this document will be able to help you.

2.  Registrations problems

Let’s start here by the first kind of issue I’ve been working on: the registrations problems. By registrations problems, I mean everything linked to the stage following the event sink installation on the server (through a MSI like package hopefully). The registration is indeed the information that will tell to the Exchange server what code it should call for a specific mailbox and/or group of mailbox on a specific action (OnSave, OnDelete, etc.). If you can’t create the famous registration item through the regevents.vbs script or through Exchange Explorer, this chapter is for you.

Here are the most common issues on this topic:

1.  First, check that you can do a CreateObject call on the name of the Event Sink inside a simple VBS Script as:

Set test = CreateObject(“MyEventSink.Class”)

If it’s failing with an error 429 (or another type of error), this means that there is a problem inside the store event sink’s constructor. This is often occurring with .NET event sinks. For instance, some customers are trying to access to some configuration parts stored inside a registry key or inside a file. If this file or registry key doesn’t exist, the constructor will fail and then Exchange won’t be able to create the registration item. The call to the constructor of the sink must succeed to be able to register it. Please then review carefully what the event sink need to access during his construction phase before trying to register it.

I would recommend you for this phase to remove the .NET Event Sink from COM+ temporally to get rid out of potential issue linked to COM+ itself. For that, here is a procedure that I’m suggesting you:

A – Delete the COM+ application where you’ve tried to install your .NET Event Sink

B – Use the following command line to register in the COM world your sink:

"Regasm /tlb:YourDotNetEventSink.tlb YourDotNetEventSink.dll /codebase" in the directory where those files are installed.

à This will insert the appropriate information in the registry to make your .NET Assembly visible to COM.

Note: regasm.exe et regsvcs.exe are 2 tools that you’ll find inside the Microsoft.NET directory of your %systemroot% folder.

Once done, try again to do the CreateObject() call. If it’s still failing, the debugging process on the constructor should be easier that when the component is in COM+. If it’s now working as expected, the problem may come from the COM+ configuration part. Main point to check is then the identity of the COM+ application hosting the sink. The user account used may not have the right to read the file you wish in the constructor phase, etc.

2.  Use Exchange Explorer to register the store event sink rather than the VBS script. Indeed, Exchange Explorer is doing a better job than the regevents.vbs script to do the registration process. Please check the procedure described below in the “REGISTERING THE EVENT SINK” section if you don’t know how to register a sink using Exchange Explorer.

3.  Last step is to configure the COM+ component properties and select the Security TAB and ensure that the Security Level is set to: “Perform access checks only at the process level” and that the Authorization is not set to “Enforce access checks for this application”

Again, if it’s a .NET Event Sink, I would recommend you to use to command line to install it directly in COM+:

"Regsvcs /fc /tlb:YourDotNetEventSink.tlb YourDotNetEventSink.dll"

3.  Execution problems

We often need to determine if the problem comes from the code itself or from the Exchange server. If the sink never worked at all on any machine, first phase is to use a working sink in which we trust instead. If the sink usually works on other machines and not on a special one, then concentrate first on the machine itself and not on the code.

Prerequisites: how to identify the process associated to your sink?

Please note that as the event sink is hosted by COM+, its code then lives inside a process named DLLHOST.exe . You can easily find the proper process and its PID inside COM+. It will make the filtering more effecient for the troubleshooting of the problem you’ve got with your event sink. You’ll find below a screenshot explaining how to do that with the Component Services MMC.

First, navigate up to the “COM+ applications” where all packages are listed. Click on the “Status” button and identify the package associated with your store event sink.

Write down the PID (2240 in this case) and you’ll see that you’ll be able to find the associated process in any tools listing them like the task manager for instance:

Case 1: we’re suspecting the code of the event sink to be bad designed

Let’s then use a sink we know it always works. And let use Exchange Explorer to remove any potential problem with the registration part.

Creating the Diagnostic Sink using Visual Basic 6.0

This is a very simple store event sink that is just supposed to write inside a file a simple log as soon as the event is triggered. Create a new Active X/DLL project in VB 6.0 containing:

-  Name the project Diagnostic

-  Add a class module named Sink.cls with the following code

Option Explicit

Implements Exoledb.IExStoreAsyncEvents

Implements Exoledb.IExStoreSyncEvents

Private Sub IExStoreAsyncEvents_OnDelete(ByVal pEventInfo As Exoledb.IExStoreEventInfo, ByVal bstrURLItem As String, ByVal lFlags As Long)

LogEventInfo "OnDelete", pEventInfo, bstrURLItem, lFlags

End Sub

Private Sub IExStoreAsyncEvents_OnSave(ByVal pEventInfo As Exoledb.IExStoreEventInfo, ByVal bstrURLItem As String, ByVal lFlags As Long)

LogEventInfo "OnSave", pEventInfo, bstrURLItem, lFlags

End Sub

Private Sub IExStoreSyncEvents_OnSyncDelete(ByVal pEventInfo As Exoledb.IExStoreEventInfo, ByVal bstrURLItem As String, ByVal lFlags As Long)

LogEventInfo "OnSyncDelete", pEventInfo, bstrURLItem, lFlags

End Sub

Private Sub IExStoreSyncEvents_OnSyncSave(ByVal pEventInfo As Exoledb.IExStoreEventInfo, ByVal bstrURLItem As String, ByVal lFlags As Long)

LogEventInfo "OnSyncSave", pEventInfo, bstrURLItem, lFlags

End Sub

-  Add a global module named basLogFunctions.bas with the following code:

'********************************************************

' From this point on, credit goes to Steve Griffin

' I stole these from a sample he worked on

'********************************************************

Option Explicit

Public Sub LogEventInfo( _

EventName As String, _

ByVal pEventInfo As Exoledb.IExStoreEventInfo, _

ByVal bstrURLItem As String, _

lFlags As Long _

)

Dim ado_rec As Record

Dim DispInfo As Exoledb.IExStoreDispEventInfo

Dim pField As ADODB.Field

Dim I As Integer

LogFile EventName

LogFile "Item URL: " & bstrURLItem

LogFile GLOBAL_ReturnEXOLEDBFlags(lFlags)

LogFile "------"

LogFile ""

End Sub

Public Sub LogFile(MyString As String)

Dim fso As Scripting.FileSystemObject

Dim txtfile As TextStream

Set fso = CreateObject("Scripting.FileSystemObject")

Set txtfile = fso.OpenTextFile("C:\DiagnosticSink.log", ForAppending, True)

txtfile.WriteLine (MyString)

txtfile.Close

End Sub

Private Sub LogFields(MyFields As ADODB.Fields)

Dim pField As ADODB.Field

Dim I As Integer

LogFile "Number of fields = " & MyFields.Count

For Each pField In MyFields

On Error GoTo ErrorHandler

LogFile I & " : " & pField.Name

If Not (pField.Type = adVarBinary) Then

LogFile vbTab & pField.Value

Else

LogFile vbTab & "ERR: Variant Binary value cannot be printed"

End If

GoTo Continue

ErrorHandler:

LogFile vbTab & Err.Number & vbTab & Err.Description

Continue:

I = I + 1

Next pField

End Sub

Private Function GLOBAL_ReturnEXOLEDBFlags(lFlags As Long) As String

Dim strBuff As String

strBuff = " Flags (" & "0x" & Hex(lFlags) & "):"

If (lFlags And EVT_NEW_ITEM) > 0 Then

strBuff = strBuff & " EVT_NEW_ITEM "

End If

If (lFlags And EVT_IS_COLLECTION) > 0 Then

strBuff = strBuff & " EVT_IS_COLLECTION "

End If

If (lFlags And EVT_REPLICATED_ITEM) > 0 Then

strBuff = strBuff & " EVT_REPLICATED_ITEM "

End If

If (lFlags And EVT_IS_DELIVERED) > 0 Then

strBuff = strBuff & " EVT_IS_DELIVERED "

End If

If (lFlags And EVT_SOFTDELETE) > 0 Then

strBuff = strBuff & " EVT_SOFTDELETE "

End If

If (lFlags And EVT_HARDDELETE) > 0 Then

strBuff = strBuff & " EVT_HARDDELETE "

End If

If (lFlags And EVT_INITNEW) > 0 Then

strBuff = strBuff & " EVT_INITNEW "

End If

If (lFlags And EVT_MOVE) > 0 Then

strBuff = strBuff & " EVT_MOVE "

End If

If (lFlags And EVT_COPY) > 0 Then

strBuff = strBuff & " EVT_COPY "

End If

If (lFlags And EVT_DRAFT_CREATE) > 0 Then

strBuff = strBuff & " EVT_DRAFT_CREATE "

End If

If (lFlags And EVT_DRAFT_SAVE) > 0 Then

strBuff = strBuff & " EVT_DRAFT_SAVE "

End If

If (lFlags And EVT_DRAFT_CHECKIN) > 0 Then

strBuff = strBuff & " EVT_DRAFT_CHECKIN "

End If

If (lFlags And EVT_LOCK_TRANSIENT) > 0 Then

strBuff = strBuff & " EVT_LOCK_TRANSIENT "

End If

If (lFlags And EVT_LOCKTYPE_READ) > 0 Then

strBuff = strBuff & " EVT_LOCKTYPE_READ "

End If

If (lFlags And EVT_LOCKTYPE_WRITE) > 0 Then

strBuff = strBuff & " EVT_LOCKTYPE_WRITE "

End If

If (lFlags And EVT_LOCKTYPE_READWRITE) > 0 Then

strBuff = strBuff & " EVT_LOCKTYPE_READWRITE "

End If

If (lFlags And EVT_LOCKTYPE_CHECKOUT) > 0 Then

strBuff = strBuff & " EVT_LOCKTYPE_CHECKOUT "

End If

If (lFlags And EVT_LOCKSCOPE_SHARED) > 0 Then

strBuff = strBuff & " EVT_LOCKSCOPE_SHARED "

End If

If (lFlags And EVT_LOCKSCOPE_EXCLUSIVE) > 0 Then

strBuff = strBuff & " EVT_LOCKSCOPE_EXCLUSIVE "

End If

If (lFlags And EVT_UNLOCK_CHECKIN_ABORT) > 0 Then

strBuff = strBuff & " EVT_UNLOCK_CHECKIN_ABORT "

End If

If (lFlags And EVT_UNLOCK_CHECKIN_KEEP_LOCKED) > 0 Then

strBuff = strBuff & " EVT_UNLOCK_CHECKIN_KEEP_LOCKED "

End If

If (lFlags And EVT_LOCKDEPTH_DEEP) > 0 Then

strBuff = strBuff & " EVT_LOCKDEPTH_DEEP "

End If

If (lFlags And EVT_SYNC_BEGIN) > 0 Then

strBuff = strBuff & " EVT_SYNC_BEGIN "

End If

If (lFlags And EVT_SYNC_COMMITTED) > 0 Then

strBuff = strBuff & " EVT_SYNC_COMMITTED "

End If

If (lFlags And EVT_SYNC_ABORTED) > 0 Then

strBuff = strBuff & " EVT_SYNC_ABORTED "

End If

If (lFlags And EVT_INVALID_SOURCE_URL) > 0 Then

strBuff = strBuff & " EVT_INVALID_SOURCE_URL "

End If

If (lFlags And EVT_INVALID_URL) > 0 Then

strBuff = strBuff & " EVT_INVALID_URL "

End If

GLOBAL_ReturnEXOLEDBFlags = strBuff

End Function

-  Add references to “Microsoft Scripting Runtime”, “Microsoft ActiveX Data Objects 2.x Library”, “EXOLEDB Type Library”.

-  Compile your DLL and set the binary compatibility in the project properties

Setting up the Diagnostic Sink

- Copy Diagnostic.dll onto the Exchange server.

- Register the DLL with regsvr32.exe

Creating a COM+ Event Sink Application

- Click Start, point to Programs, then Administrative Tools, and click Component Services.

- In Component Services, select COM+ Applications under Console Root\Component Services\My Computer.

- Right-click COM+ Applications, point to New, and click Application.

- Click Next, then click Create an empty application.

- Name the application, verify that Server application is selected, and then click Next.

- On the Set Application Identity page, click This User. Browse for an appropriate account, one that has the necessary permissions to perform the tasks in the event sink, and enter the password for this account. It is recommended that you not select the Interactive User option because the Exchange server will not normally have a user logged on interactively. Click Next and then Finish.

- Open the new application, and right-click Components in the tree view.

- Click New and then click Component.

- Click Import component(s) that are already registered.

- When the list displays, select Diagnostic.Sink.

- Click Finish, and click OK to close Component Services.

Registering the Event Sink using Exchange Explorer

- Run Exchange Explorer.

- Connect to the folder where you want to register the sink.

- From the File menu, choose Add Event Registration.

Follow the wizard as follows:

-  Choose a name: Diag

- Select the category: Asynchronous or Synchronous

-  Pick the event(s): Check all checkboxes