Using Active Messaging and the Active Server to Create Workgroup-Enabled Applications via the Web

Dan Fay
Technical Evangelist, Microsoft Developer Relations Group
Thomas Rizzo
Product Manager, Microsoft Exchange Server

July 1997

Click to open or copy the files in the Exchange Techdesk sample application.

Overview

Every once in a while a product takes a while to mature, though it is released with great fanfare and expectations. Like a good wine, Microsoft® Exchange Server is rounding out its flavor by adding to its great messaging infrastructure the tools needed to create powerful groupware applications. Microsoft Exchange Server 5.0 not only supports standard Internet protocols for sending and receiving mail messages (POP3/SMTP) and supporting news-readers (NNTP), it now enables Web browser clients through the HTTP protocol to access its full set of messaging and groupware functionality via Active Server Pages.

This article describes how to use Active Server pages to create powerful groupware applications using Microsoft Exchange Server. Developing applications that use Microsoft Exchange Server gives the developer many advantages. For example, developers can take advantage of the Microsoft Exchange semi-structured database engine, built-in directory service, and access control lists (ACLs). This saves application developers from having to worry about defining these advanced features and logic into their products.

The best way to understand a new technology is to walk through an application to see how easy it is to create. Our sample application is a technical support application that allows a user to log on and submit a support request, which will then be worked on by a technician—all via a Web browser. All request tickets are stored in a Microsoft Exchange public folder and accessed using a new component of Microsoft Exchange called Active Messaging.

Active Messaging helps you build messaging and workgroup applications more simply. This technology, formally known as OLE Messaging, has undergone more than a name change. It has been enhanced to work with Microsoft Internet Information Server (IIS) 3.0 as a server-side component. For those developers and Web designers who aren’t familiar with OLE Messaging, it should be noted that the Active Messaging object model refers to information stores, messaging objects, attachments, and addresses. But don’t worry—the object model is quite easy to understand once you wrap your brain around it. By understanding the hierarchical structure of the model, you can easily retrieve and add data to Microsoft Exchange Server. Active Messaging abstracts the powerful features of Microsoft Exchange and makes them approachable even to the novice programmer.

Active Messaging calls can be made from both the client and the server application. In this article and sample, we focus on making Active Messaging calls via Microsoft Internet Information Server 3.0 and the Active Server. So all the calls and execution will take place on the server and HTML is sent down to the client application on the browser. The benefit of this is that any browser-enabled client can run this workgroup application.

Some other Microsoft Exchange Server workgroup applications will use the combination of the high-fidelity/rich-view Microsoft Outlook™ client and an HTML-based browser, by creating Microsoft Outlook forms and views and then extending them to the Web via Active Messaging. Currently, you need to create the two applications independently, one as a Microsoft Outlook form, the other as a pure HTML form. However, this will be handled automatically in the future and will only require the creation of a single form. The key thing to note is that both Microsoft Outlook and Active Messaging utilize Microsoft Exchange Server as their information store. This allows Microsoft Outlook clients to post items for Web browser users to read and, vice-versa, Web browsers can post items for Microsoft Outlook clients to read.

Introduction to Active Messaging

Benefits

IIS/ASP integration

Since Active Messaging is an Active Server component, you can instantiate Active Messaging objects through Active Server Pages. This allows Active Messaging to inherit the benefits of Active Server Pages, which include automatic compilation of scripts, server-based execution, and the ability to script using any Active scripting language, including Microsoft Visual Basic® Scripting Edition (VBScript) and Jscript™. Automatic compilation of scripts frees the developer from having to wait to recompile and post the application back to the Web server. Server-based execution also allows applications to run faster, since the workload is primarily placed on the server where most of the computational horsepower resides. Finally, the ability to write the application in any Active scripting language leverages developers' programming language knowledge and allows programming flexibility. These combined benefits make it easy and fast to build powerful Web-based solutions.

Active Messaging object model

Before we get down to the fun part of examining source code, we’ll first examine the Active Messaging object model. Active Messaging allows you access the messaging functionality and the information stored, such as e-mail messages or calendaring information, in Microsoft Exchange Server. The semi-structured information store of Microsoft Exchange Server is a feature that many developers take for granted when developing Microsoft Exchange solutions. The Microsoft Exchange information store allows the creation of new properties on any object without having to first modify or even create a new schema for that object. This means that new properties can be added to any message at any time. You’ll see an example of this in the Techdesk application when we create new custom properties on the help ticket message object.

The Active Messaging object model is a hierarchical model—all of the top-level objects are derived from the Session object. The Session object corresponds to any valid Messaging Application Programming Interface (MAPI) session. For example, a MAPI session takes place when a user logs into his or her Microsoft Exchange Server mailbox from an e-mail client. The main children of the Session object are the InfoStores and the AddressList collections. The InfoStores collection contains the various places where users can store information. Examples of InfoStores objects include mailboxes stored on a Microsoft Exchange Server, messages stored in a file on the user’s computer, or mail messages synchronized from the server mailbox down to the client’s offline information store. A specific InfoStore object, such as a server mailbox, contains a Folders collection, which in turn contains a Messages collection. All message objects have properties associated with them. In the case of a typical e-mail message, these properties are the subject of the message, the date and time sent, and the text of the message. Figure 1 should help you visualize the Active Messaging object model and the relationships among its objects.

Figure 1. The Active Messaging object model collections and objects

The most important piece of information about the message object is the message type. This property tells applications what type of information and properties may be associated with the message. The typical e-mail message is assigned the IPM.Note message class (IPM stands for Interpersonal Message). A message class is very similar to a C++ class. Specific message classes have specific properties and functionality, such as the IPM.Task message class. You wouldn’t expect an object of type IPM.Task to support the same functionality as an object of the IPM.Appointment class. Likewise, you wouldn’t try to use integer functions to compare two CString objects in C++. For more information on the different types of message classes, read the Active Messaging documentation available in the MSDN Library (Platform SDK, Database and Messaging Services).

Below is a simple VBScript example using various objects in Active Messaging:

'Simple example that shows how to send a message from a Web browser. 

Set objOMSession = Server.CreateObject("MAPI.Session")

bstrProfileInfo = "myserver" & chr(10) & "administrator"

'                            ^^ Exchange Server            ^^User to logon as

'Log on to the Exchange Server.

objOMSession.Logon "", "", False, True, 0, True, bstrProfileInfo 

'Add a message to the Outbox.

Set objmessage = objOMSession.Outbox.Messages.Add

'Create a recipient object for that message.

Set objonerecip = objmessage.Recipients.Add

'Assign an e-mail address to mail to for that recipient object.

objonerecip.Name = “thomriz@microsoft.com
'Try to resolve the recipient’s name as a valid e-mail address.

objonerecip.Resolve

'Create the subject for the message.

objmessage.Subject = “Some Active Messaging fun”

'Add the text for the message body.

objmessage.Text = “Active Messaging builds cool Web apps!”

'Send the message without showing a dialog to the user.

objmessage.Send showDialog=False

AMHTML—rendering objects

Imagine trying to create all the HTML statements that you would need to display an inbox that contains 1500 messages. That’s a lot of HTML to hand-code! Now imagine that you wanted different views on these messages, such as grouping by sender name, by the topic of the message, or by the importance of the message. Active Messaging HTML (AMHTML) objects make it simple to do these tasks. By using the AMHTML objects, you can easily render items and have different views automatically rendered to HTML in about five lines of VBScript code. Figure 2 shows the relationship between AMHTML objects and collections.

Figure 2. The relationship between AMHTML objects and collections

Imagine, five lines of code that can generate thousands of lines of HTML!

To begin using AMHTML, you must first create a rendering application by calling set objRenderApp = Server.CreateObject("AMHTML.Application"). Once you create the rendering application, you can call the CreateRenderer method of the rendering application to create a specific type of rendering object. There are two classes of rendering objects: ObjectRenderer and ContainerRenderer. The ObjectRenderer class can render selected properties of any Active Messaging object. This is a generic renderer. The ContainerRenderer can render the rows of an address book or folder and its contents as an HTML table. In the Techdesk application, a ContainerRenderer object is created to render the trouble tickets as an HTML table. The following code demonstrates creating rendering objects:

'Create Rendering Application.

set objRenderApp = Server.CreateObject("AMHTML.Application")

'ContainerRenderer is a constant with the value of 3, which is the identifier
'for the ContainerRenderer class.

Set objrenderer = objRenderApp.CreateRenderer(ContainerRenderer) 

Every rendering object, whether it is a ContainerRenderer or an ObjectRenderer, requires that the datasource and the currentview properties be set. The datasource and currentview properties are discussed in more detail in the Techdesk implementation.

Exchange Techdesk Sample

Enough talking about the object model, let’s dive into the code that drives the objects. The sample application we use performs the basic functions used in writing a typical Active Messaging application: logging into an authenticated MAPI session, retrieving Microsoft Exchange directory information, posting items to a public folder, rendering views of messages in HTML, and finally sending e-mail to a user from the Web.

There are three main parts to this ASP-based application:

  1. The authenticated MAPI session is set up in the Global.asa.

  2. The submitting user’s details are retrieved and the help request is submitted (posted) to a Techdesk Public Folder.

  3. The technician is then able to retrieve the next work item and process it. In processing the request, we access the data by opening the Public Folder where the data is maintained, retrieve the message collection, and then render and display those messages (work items). In order to provide the technician with details about the user’s machine setup, we retrieve the user's desktop information from a Microsoft Systems Management Server (SMS) database via ActiveX™ Data Objects (ADO). The technician then adds his comments to the open issue and closes it when resolved. Resolving the issue generates an e-mail message back to the user, with the problem and resolution details.

Granted, this application doesn’t have all the bells and whistles you would expect a technical support application to have. However, its goal is to show how easy it is to integrate and create messaging-based workgroup applications.

Programming Steps to Build the Exchange Techdesk Application

Create server object—MAPI.Session and AMHTML object

First things first: to interact with the Exchange server, you first need to establish the Messaging object on the server. This is done by instantiating a MAPI Session by calling Create Server Object (MAPI.Session).

Set objOMSession = Server.CreateObject("MAPI.Session")

Logging on—anonymous vs. authenticated

Now we’re ready to roll, but in order to post the support request in the correct user context, we must authenticate the user submitting the request. We could have logged on as an anonymous user and posted the data without verifying identity, but what fun would that have been?

Actually, in some applications verification of the user’s identity may not be needed. Since Exchange 5.0 has added the setting of access rights for anonymous users, you can treat anonymous access as you would a regular user by setting permissions for all anonymous users on specific folders. In our case, we only allow the anonymous user to browse the data, but not create or modify anything in the folder.

The following code authenticates the user by checking the HTTP server variables:

'Set BAuthenticateUser to false, assume user is unauthenticated.

BAuthenticateUser = False

'Retrieve from the server variables the authentication type. 

bstrAT = Request.ServerVariables("AUTH_TYPE")

'If there is no basic or NTLM in the auth_type, request that they log on to the domain.

    If InStr(1, "_BasicNTLM", bstrAT, vbTextCompare) < 2 Then

        Response.Buffer = TRUE

        Response.Status = ("401 Unauthorized")

        Response.AddHeader "WWW.Authenticate", "Basic"

        Response.End

    Else

'The user is already authenticated.

        BAuthenticateUser = True

    End If

The authentication code is called after the user has entered his or her mailbox name. We need to check the identity of users before we allow them to submit a Techdesk ticket. The authentication dialog box will appear and prompt users for a user name and password. If they enter the wrong password, they will receive an error message from the server.

Directory information

Active Messaging allows access to address entry objects for specific users. In the Techdesk scenario, we want to query the Microsoft Exchange directory for specific user information, such as the user’s phone number, office location, and department. We will then take this information and prepopulate the HTML Techdesk ticket form with this information rather than require the user to enter it each time.

The simplest way to get an address entry object is to create a new message addressed to the user whose directory information you need. You can then resolve the recipient’s name to the corresponding Exchange address entry. From that, you can easily retrieve the details on the user.

'Retrieving the directory information of a user,
'assuming objonerecip is established from the previous code sample.
'Set a new object equal to the recipient’s address entry object.

Set myaddentry = objonerecip.AddressEntry

'Set a new object to the address entry objects fields (directory information).

Set myfields = myaddentry.Fields

'The numbers in parens are the hardcoded ids for dept, name, etc.
'These ids are available in the Active Messaging SDK.

Set mydept = myfields.Item(974651422)

set myname = myfields.Item(805371934)

set mytitle = myfields.Item(974585886)

set myworkphone = myfields.Item(973602846)

set myoffice = myfields.Item(974716958)

Posting messages into public folders

Now that the user has completed the support request we post the Techdesk ticket into a public folder. You need to find the public folder before any messages can be posted into it. To find the public folder, the correct information store must be set. We do not want to look for public folder information in a user’s server mailbox! The public folder information store has the name “Public Folders”. The following code searches through all the available information stores until it finds the public folders store. By changing the name of the search string in the routine, we could easily search for an individual’s private mailbox stored on the Microsoft Exchange Server.

'The infostores collection is a child of the Session object.

Set objInfoStoresColl = objOMSession.InfoStores

'Initalize our count variable.

iInfostorescollindex = 1

'Initialize the objInfoStore object to the first information store. 

Set objInfoStore = objInfoStoresColl.Item(iInfostorescollindex)

'Loop through the stores until the Public Folder store is found or until
'we have exceeded the number of stores.

While objInfoStore.Name <> "Public Folders"

   If iInfostorescollindex < objInfoStoresColl.Count Then

          iInfostorescollindex = iInfostorescollindex + 1

   End If

    Set objInfoStore = objInfoStoresColl.Item(iInfostorescollindex)

Wend

Now that we have found the correct information store, we need to find the Techdesk folder in the public folder hierarchy tree. The following code uses the entryid that Microsoft Exchange Server provides to access the root of the public folders as a starting point. Then, the tree is recursed until the correct folder is found. Note that this code assumes that the Techdesk folder is a direct child of “All Public Folders”.

'This is the entryid for the Root public folder

            bstrPublicRootID = objInfoStore.Fields.Item( &H66310102 ).Value

'objInfoStore.ID is the entryid property for the public folder
'information store.

            Set myrootfolder = objOMSession.GetFolder(bstrPublicRootID, objInfoStore.ID)

'Now get the folders collection below the root folder.

            Set myfoldercollect = myrootfolder.Folders

'Retrieve the first folder.

Set recursefolder = myfoldercollect.GetFirst()

'Recurse it until we get the folder we are looking for.

While recursefolder.Name <> "Techdesk"

    Set recursefolder = myfoldercollect.GetNext()

Wend

The message can now be posted into the public folder. This code for posting the message will look very similar to the code that was used to send a private message to a user. The only difference is that a custom message class is being used here with custom properties added to that class.

'Add a message into the public folder.

Set oMessage = recursefolder.Messages.Add 

'Set the message properties -- some of these are sent from the Techdesk ticket
'form over the HTTP QueryString.

    oMessage.Subject = Request("Subject")

    oMessage.Sent = True    

    oMessage.Unread = True

    oMessage.TimeSent = Now

    oMessage.TimeReceived = Now

'This is the custom message class.

    oMessage.Type = "IPM.Task.Help Request"

    oMessage.Importance = Request("Priority")

    oMessage.Fields.Add "From User", 8, Request.Form("Contact_E-mail")

    oMessage.Fields.Add "Description", 8, Request("Description")

    oMessage.Fields.Add "Problem Type", 8, Request("Type")

    oMessage.Fields.Add "Product", 8, Request("Product")

    oMessage.Fields.Add "Phone", 8, Request("Phone")

    oMessage.Fields.Add "User Location", 8, Request("Location")

    oMessage.Fields.Add "Flag", 8, "Opened"

'Set the conversation properties.

    oMessage.ConversationTopic = oMessage.Subject

'Post the message.

    oMessage.Submitted = FALSE

    oMessage.Update 

'Destroy the object.

    Set oMessage = Nothing

Views and rendering

In order to display the Techdesk information posted into the public folder to a technician, we created a couple of views on the data. To do this, we utilize the rendering object discussed earlier. Views can be created in two ways. The first is to create the views programmatically using Active Messaging. The second is to create the views on the folder using either the Microsoft Exchange client or the Microsoft Outlook client. The second method is the easiest and recommended way to create views on a public folder. Any view created in either of these clients is accessible from Active Messaging. We used the Microsoft Outlook client to create our three views on the Techdesk public folder: by date (Techdesk), by customer (From), and by status (Status).

Creating AMHTML objects and setting the datasource for the view is a fairly straightforward process. For the datasource, any valid container object can be used. The Techdesk public folder messages collection is the datasource for our rendering object since we want to display all the messages in the Techdesk public folder as HTML.

We expose the views via the HTML button hotlinks and post this information back to the posthelp.asp with the correct view to show. If there is no view passed into posthelp.asp, the by date (Techdesk) view is set to be the default view. One small change is made to the way the views handle their third column, the subject field. We change the way the HTML is rendered so that a hyperlink is created for that column that will jump to the message.asp to display that particular message. You can see how this is done in the code below.

The following code shows how to create a rendering object, set its datasource, and set the view on that data:

'Create Rendering Application.

set objRenderApp = Server.CreateObject("AMHTML.Application")

'ContainerRenderer is a constant with the value of 3, which is the identifier
'for the ContainerRenderer class.

Set objrenderer = objRenderApp.CreateRenderer(ContainerRenderer) 

'Set oMsgCol equal to the collection of messages in the Techdesk folder.

Set oMsgCol = objFolder.Messages

'Set the datasource for the rendering object to the messages in the Techdesk
'public folder.

objrenderer.DataSource = oMsgCol

'Select the third column (Subject).

set objcolumn = objcolumns.Item(3)

'Change the Renderer so it renders the subject field as an a href with
'the entry id of the message as the hyperlink.

objcolumn.RenderUsing = "<a href='/newhelp/message.asp?entryid=%obj%'>%value%</a>"

'Set the current view equal to the Techdesk view.

objrenderer.CurrentView = “Techdesk”

Now, once this information is set, we have to call the Render method of the rendering object in order to have it generate the HTML and send it to our Web browser.

'This script is embedded in the HTML where the messages should be rendered as
'HTML.

<% objRenderer.Render 1,0,0,Response %>

Whoosh! HTML is generated for all the objects in the folder according to the view we selected. The same methods can be used to generate HTML views of folders, address entries, and a host of other objects used in Microsoft Exchange Server.

Active Data Objects

When a technician clicks on the hyperlink to view a particular Techdesk ticket, an HTML form is displayed that is prepopulated with information that the user entered. However, for a technician to really troubleshoot what is wrong with a user’s machine, the technician has to see what the user might have installed on the machine. For this, we use ADO (Active Data Objects) to retrieve Systems Management Server data from a Microsoft Access database, though this could be from any ODBC database. The following code shows how to create the ADO object and query a database using ADO. As you can see, multiple server objects (ADO, Active Messaging) can be created in a single Active Server Page.

'Start the ADO Connection.
'objuser is the name of the user who submitted the request.

Set Conn = Server.CreateObject("ADODB.Connection")

Conn.Open "Techdesk"

Set RS = Conn.Execute("Select SystemChipType, SystemChipSpeed, SystemChipCount,

     SystemOS, SystemRAM FROM tblMachine WHERE Userid like '" & objuser & "';")

Set RSIP = Conn.Execute("Select CompName, IPAddress FROM tblNetwork WHERE Userid

                        like '" & objuser & "';")

Set RSSoft = Conn.Execute("Select SoftwareName, SoftwareVersion FROM tblSoftware WHERE Userid like '" & objuser &"';")

Resolution of a support request

When the technician resolves the request, the Techdesk application will send an e-mail message to the user letting him or her know the issue has been resolved, together with a URL pointer to information on the request. The user then can see the details, as well as comment on the service he or she received.

In order to send the message, we create a new message in the outbox and add the user name to the recipient object and then resolve the name. We add the current time and appropriate information to the body of the text and then send the message to the user. The code for this functionality is very similar to the first example in this article.

Logging off the MAPI session

Finally, to close out our application, we need to make sure we end the MAPI session we started at the beginning of the session. We actually close this session in the Global.asa when the Session_OnEnd routine is called, which logs off the user and cleans up the individual MAPI session.

' While calling the Session_OnEnd event, Active Server Pages doesn’t call
'the code in the right security context. Workaround: current security context
'is stored in the Session object and then gets restored in Session_onEnd 
'event handler.
'This is the security context that is needed.

hImp = Session("hImp")

    If Not IsEmpty(hImp) Then

        fRevert = objRenderApp.Impersonate(hImp)

    End If

If (fRevert) Then

'Set the object to nothing.

        objRenderApp.Impersonate(0)

    End if

Summary

Active Messaging makes it easy to integrate Microsoft Exchange discussion databases and workgroup applications into your Web-based environment. Users, Webmasters, and developers can write ASP applications that use Active Messaging to access functionality in Microsoft Exchange Server. These same ASP applications can also access SQL data by using the ADO Active Server components. Give Active Messaging a try and see how it can help you combine your Web technologies with the power and reliability of Microsoft Exchange Server.

Active Messaging is available for the first time in Exchange Server 5.0. For a trial copy of Microsoft Exchange Server or to learn more about Active Messaging, please visit the Microsoft Exchange Server Community Site at http://www.exchangeserver.com.