Building Event Handlers in Visual Studio .NET


To write event handlers in .NET, you have to build a .NET component that is callable from COM. This is a bit harder than using COM from .NET because you have to do some special work to make sure your .NET component is exposed correctly to COM and you also need to set up your Visual Studio .NET project to enable COM interoperability.

Many of the programming interfaces today are built on COM, so COM interoperability is an important topic. You need this capability to build event handlers, COM add-ins, and smart tags. However, once you learn how to build one of these types of applications, building the other types is easy because they all require the same initial steps to set up COM interoperability.

To show you how to build a .NET COM “compatible component, we'll build an event handler in .NET for Exchange or SharePoint Portal Server (SPS) 2001. The great thing about using .NET to build your components is that you get all the advanced features of .NET, such as Visual Basic .NET, C#, the .NET Framework, and the common language runtime (CLR).

The first thing you do when you build any .NET project that will correspond to a COM DLL is to use the class library project. The choice of programming language is up to you. For this example, I'll use Visual Basic .NET. We'll look at a C# example later in this chapter.

Next you add a reference to whatever COM libraries you need to work with in your application. For an event handler, we need to add a reference to EXOLEDB and implement the interfaces for asynchronous or synchronous events contained there. As you learned in Chapter 15, when you add the reference, Visual Studio .NET imports that library and creates a .NET-compatible assembly for interoperability. Figure 17-5 shows adding a reference to the EXOLEDB type library.

click to expand
Figure 17-5: Adding a reference to the EXOLEDB type library

Next we can start coding our application. We'll be working with interfaces from EXOLEDB, so we'll want to use the import mechanism to make it easy to reference those in our application. This step is not necessary, but it will simplify our variable and type declarations. We also want to import the System.Runtime.InteropServices namespace because it includes useful classes for interacting with COM. The import statements are shown here:

 Imports Exoledb Imports System.Runtime.InteropServices 

Next we need to declare our class. When Visual Basic .NET created our project, it created a default class for us called Class1 . We can change the name of this class to whatever name we want. For this example, we'll change the class name to ExchangeEventSink . Next we need to tell the class that it implements whatever event interfaces we will listen for. In this case, we'll implement asynchronous events, so we will add the following line of code:

 Implements Exoledb.IExStoreAsyncEvents 

The next step is to make sure that our .NET component can interoperate with COM. To do this, we need to add some attributes to the class that the COM interop services will use. The three main attributes we need to add are ProgID , GuidAttribute , and COMVisible . The ProgID attribute lets you specify the COM ProgID that your .NET component will use.

GuidAttribute lets you specify the GUID to be used for your class ID. The easiest way to generate this GUID is to use the Create GUID command from the Tools menu. You should select the fourth option, which is to create the GUID in the Registry Format (which is {xxxxxxxxx-xx xxx}). Click the Copy button, and then click the Exit button. When you paste the GUID, remove the curly braces. Neither this attribute nor the ProgID attribute are required for COM interoperability, but they make it easier for you to control the ProgID and GUID used for your component.

The final attribute, COMIsVisible , allows you to either make all your public classes visible to COM (by putting this attribute in your AssemblyInfo.vb file) or make individual classes visible. We'll need only this individual class to be visible, so we will use this attribute only with our one class.

Finally, because COM will call our class, we need to create a default constructor for the class that takes no arguments.

If you need to do some work when COM creates your object, you can modify the default constructor for your class. We're not doing any work on the construction, so we'll just leave the default constructor.

When you add all these attributes and the default constructor, your code should look something like this:

 <ProgId("DotNetES.EventSink"), _     Guid("7E303074-88B9-4be8-8C16-AC0CD8195CA4"), _     ComVisible(True)> _ Public Class ExchangeEventSink     Implements Exoledb.IExStoreAsyncEvents          Public Sub New()          End Sub End Class 

Next we can implement our functionality for our event sink, as we did in Visual Basic 6.0. The easiest way to do this is to select the functions in the IExStoreAsyncEvents interface from the drop-down menus at the top of the Integrated Development Environment (IDE) in Visual Studio .NET. Here is the complete code that implements the OnSave and OnDelete events using Visual Basic .NET:

 Imports System.Runtime.InteropServices Imports Exoledb      <ProgId("DotNetES.EventSink"), _     Guid("7E303074-88B9-4be8-8C16-AC0CD8195CA4"), _     ComVisible(True)> _ Public Class ExchangeEventSink     Implements Exoledb.IExStoreAsyncEvents          Dim strToAddress As String = _         "thomriz@thomrizex2kdom.extest.microsoft.com"     Dim strFromAddress As String = _         "thomriz@thomrizex2kdom.extest.microsoft.com"          Public Sub OnDelete(ByVal pEventInfo As Exoledb.IExStoreEventInfo, _                        ByVal bstrURLItem As String, ByVal lFlags As Integer) _                        Implements Exoledb.IExStoreAsyncEvents.OnDelete         'Create a simple email to send          'System.Web.Mail is covered in Chapter 20         Dim oMailMessage As New System.Web.Mail.MailMessage()         oMailMessage.To = strToAddress         oMailMessage.From = strFromAddress         oMailMessage.Subject = "OnDelete Event Sink!"         oMailMessage.Body = "This event was fired at " & Now() _                           & " Url: " & bstrURLItem         Dim oSMTPMail As System.Web.Mail.SmtpMail         oSMTPMail.Send(oMailMessage)     End Sub          Public Sub OnSave(ByVal pEventInfo As Exoledb.IExStoreEventInfo, _                       ByVal bstrURLItem As String, ByVal lFlags As Integer) _                       Implements Exoledb.IExStoreAsyncEvents.OnSave         'Create a simple email to send         On Error Resume Next         Dim oMailMessage As New System.Web.Mail.MailMessage()         Dim pNewEventInfo As Exoledb.IExStoreDispEventInfo         Dim oRecord As ADODB.Record         pNewEventInfo = CType(pEventInfo, Exoledb.IExStoreDispEventInfo)         oRecord = pNewEventInfo.EventRecord         oMailMessage.To = strToAddress         oMailMessage.From = strFromAddress         oMailMessage.Subject = "OnSave Event Sink!"         Dim strMessageText As String = "Errors: " & Err.Number _             & Err.Description & " This event was fired at " & Now() _             & " Url: " & bstrURLItem & vbLf & "ADO Record Subject: " _             & oRecord.Fields("urn:schemas:httpmail:subject").Value _             & vbLf & "Flags: " & lFlags         oMailMessage.Body = strMessageText         Dim oSMTPMail As System.Web.Mail.SmtpMail         oSMTPMail.Send(oMailMessage)     End Sub End Class 

All the code does is send a simple e-mail message to a user when the event happens. The key code that you want to look at is in OnSave . You will notice the use of IExStoreDispEventInfo . This is the dispatch interface to get the event item as an ADO Record object as well as provide other Exchange event functions. In Visual Basic 6.0, you can just Dim a variable as this type and pass it the pEventInfo variable, which is an IExStoreEventInfo object, and Visual Basic 6.0 will automatically convert the types. With Visual Basic .NET, you need to do an explicit type conversion using the CType function to convert between the two types. Once you do that, the dispatch interfaces work the same as they do in Visual Basic 6.0.

The next step is to make sure that when we build our application, Visual Studio .NET registers our assembly with COM. To do this, we right-click on our project in Solution Explorer and choose Properties. Then select the Configuration Properties folder. Under the Build section, make sure that Register For COM Interop is enabled. The interface for performing these steps is shown in Figure 17-6.

click to expand
Figure 17-6: Registering for COM interoperability in Visual Studio .NET

Finally, if you want to debug your application, you need to tell Visual Studio .NET what application to start in order to start debugging. Unlike with Visual Basic 6.0, you cannot just wait for your component to be created. When you work with event sinks, you need to cause some action to occur that will trigger the sink. Outlook will probably be the primary interface to create or delete items that will fire your sink, so you need to register Outlook as the application to launch to start debugging. To do this, right-click on the project in Solution Explorer and choose Properties again. Expand the Configuration Properties folder and click on Debugging. In the Start Action area, click Start External Program and type the path to Outlook on your system. This is shown in Figure 17-7.

click to expand
Figure 17-7: Setting Outlook as the program to launch to start debugging

Building More Than One Handler in a Single File

With Exchange Server, you can implement only one type of event handler (synchronous, asynchronous, or system) in a single COM component. With Visual Basic 6.0, you can implement only a single class in a single file. In other words, if you need multiple event handlers, you must implement them in separate component libraries. With Visual Basic .NET, you can implement multiple classes in a single file. With the power you have in Visual Basic .NET with multiple classes and the ability to control COM interoperability and interfaces, you can create a single file with all the different event handlers you need. For COM interoperability, all you do is specify a different ProgID and GUID for your new classes. The following code is included with the asynchronous event handler in the same file but implements synchronous event handling:

 <ProgId("DotNetES.SyncEventSink"), _     Guid("4D40EE77-6C7E-462e-8868-9893E1A1CDFB"), _     ComVisible(True)> _ Public Class ExchangeSyncEventSink     Implements Exoledb.IExStoreSyncEvents          Dim strToAddress As String = _         "thomriz@thomrizex2kdom.extest.microsoft.com"     Dim strFromAddress As String = _         "thomriz@thomrizex2kdom.extest.microsoft.com"          Public Sub OnSyncDelete(ByVal pEventInfo As Exoledb.IExStoreEventInfo, _                             ByVal bstrURLItem As String, _                             ByVal lFlags As Integer) _                             Implements Exoledb.IExStoreSyncEvents.OnSyncDelete          End Sub          Public Sub OnSyncSave(ByVal pEventInfo As Exoledb.IExStoreEventInfo, _                           ByVal bstrURLItem As String, _                           ByVal lFlags As Integer) _                           Implements Exoledb.IExStoreSyncEvents.OnSyncSave         'Create a simple email to send         On Error Resume Next         Dim oMailMessage As New System.Web.Mail.MailMessage()         Dim pNewEventInfo As Exoledb.IExStoreDispEventInfo         Dim oRecord As ADODB.Record         pNewEventInfo = CType(pEventInfo, Exoledb.IExStoreDispEventInfo)         oRecord = pNewEventInfo.EventRecord         oMailMessage.To = strToAddress         oMailMessage.From = strFromAddress         oMailMessage.Subject = "OnSyncSave Event Sink!"         Dim strMessageText As String = "Errors: " & Err.Number _             & Err.Description & " This event was fired at " & Now() _             & " Url: " & bstrURLItem & vbLf & "ADO Record Subject: " _             & oRecord.Fields("urn:schemas:httpmail:subject").Value _             & vbLf & "Flags: " & lFlags         oMailMessage.Body = strMessageText         Dim oSMTPMail As System.Web.Mail.SmtpMail         oSMTPMail.Send(oMailMessage)              'If you wanted to abort the transaction,         'you can use the following code         'pNewEventInfo.AbortChange()     End Sub End Class 

Finishing Touches

Once you have compiled and built your new COM components and have had Visual Studio .NET register them, all you need to do is to register these new components in COM+, just like you would with your Visual Basic 6.0 components. Figure 17-8 shows registering one of our new .NET components with COM+.

click to expand
Figure 17-8: Registering a .NET COM interoperability component with COM+

The last step is to create a registration in Exchange, as you would for a Visual Basic 6.0 component, to tell Exchange what component to call when an event occurs. You can do this using the Event Registration Wizard in Exchange Explorer included in the Exchange SDK.

To test your application, all you do is try to create, modify, or delete items in the folder where you registered for events.

A C# Example

To give you an idea of what the same type of event handler would look like in C#, I created a C# example for the code. You will notice similar semantics in C#, such as specifying the GUID and ProgID as well registering for COM interoperability as part of the project. The only major difference between the Visual Basic code and the C# code is the semantic differences between the languages in terms of type-casting and statement declaration.

 using System; using System.Runtime.InteropServices; using Exoledb; using System.Web.Mail;      namespace ExEventSinkCSharp {          /// <summary>     /// This sample shows implementing an Event Sink using C#     /// </summary>     ///     [GuidAttribute("38F89F18-5293-4774-8F72-41C40733DB1E"),     ProgIdAttribute("ExEventSink.CSharp")]     public class ExEventSink : Object, Exoledb.IExStoreAsyncEvents     {         string strToAddress = "thomriz@thomrizex2kdom.extest.microsoft.com";         string strFromAddress = "thomriz@thomrizex2kdom.extest.microsoft.com";              public ExEventSink()         {             //             // TODO: Add constructor logic here             //         }              public void OnSave(Exoledb.IExStoreEventInfo pEventInfo,                            string bstrURLItem, int lFlags)         {             /// Implements OnSave             ///             /// Send a simple email to the user             ///             System.Web.Mail.MailMessage oMailMessage =                 new System.Web.Mail.MailMessage();             oMailMessage.To = strToAddress;             oMailMessage.From = strFromAddress;             oMailMessage.Subject = "OnSave Event Fired (C#)";             /// Get the EventInfo and convert it so we can use it             ///             Exoledb.IExStoreDispEventInfo pNewEventInfo =                 (Exoledb.IExStoreDispEventInfo)pEventInfo;             /// Get something off the new variable             ///             ADODB.Record oRecord = new ADODB.Record();             oRecord = (ADODB.Record)pNewEventInfo.EventRecord;                  oMailMessage.Body = "This event was fired "                 + DateTime.Now.ToString() + " URL to item: "                 + oRecord.Fields["DAV:href"].Value + " Flags: " + lFlags;             System.Web.Mail.SmtpMail.Send(oMailMessage);         }              public void OnDelete(Exoledb.IExStoreEventInfo pEventInfo,                              string bstrURLItem, int lFlags)         {             /// Implements OnDelete         }     } } 



Programming Microsoft Outlook and Microsoft Exchange 2003
Programming MicrosoftВ® OutlookВ® and Microsoft Exchange 2003, Third Edition (Pro-Developer)
ISBN: 0735614644
EAN: 2147483647
Year: 2003
Pages: 227
Authors: Thomas Rizzo

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net