Exchange Server 2007 SP1, Outlook Web Access Customization: Part 3, NewHelpTicket.ASPX
28 September 09 08:40 PM

I’ve seen a few cases come through where customer’s are trying to implement the customization features that were introduced with Exchange Server 2007 SP1.  There’s a few key areas that seem to cause trouble so I figured it would be worth walking through a simple scenario that covered many of the different aspects. (Link to Part 1 of the series, Link to Part 2 of the series)

It’s been a long time coming, but I’m finally getting around to completing this series of posts.  I had originally intended to use the standard Visual Studio generated proxy classes for this sample, but with the introduction of the EWS Managed API the code for this scenario becomes much easier to write.  The Managed API is currently available as a Release Candidate.  After you install the API, you will have to copy the DLL file from the install location (C:\Program Files\Microsoft\Exchange\Web Services\1.0) to a location where it can be found by your ASP.Net code.  For this sample you can place it in: C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\bin

Now down to business.  This form is basically a very simple ASP.NET form to use the EWS Managed API to send the email as the user.  For demonstration purposes, I have simply sent the resulting form back to the originating user using the “ea” value from the QueryString.

Another interesting point is the code to impersonate the logged on user.  When implementing custom forms, you should be mindful of the identity that is being used.  In this example, the form is running in the same IIS Application Pool as OWA.  This means that it will have access to the session info.  I’ve used the identity of the logged on user available from the Page object and swapped the formatting of this value so that it can be used as a PrincipalName to impersonate the user.   The reason that I used the identity from the Request instead of relying on the Primary SMTP address in the Querystring is that the client can alter the QueryString.

NewHelpTicket.ASPX:

(Note:  This sample code is intended only to show the concepts involved and is not production ready!  If you have questions, feel free to submit them via the comments or contact me through the “Email” link above.)

<%@ Page Language="C#" AutoEventWireup="true" EnableSessionState="True"%>
<%@ Assembly Name="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.Net.Security" %>
<%@ Import Namespace="Microsoft.Exchange.WebServices.Data" %>
<%@ Import Namespace="System.Security.Cryptography.X509Certificates" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    
    protected void Page_Load(object sender, EventArgs e)
    {   // The following is a simple method for closing a window with client-side code on a button.
        bClose.Attributes.Add("onclick", "window.close();");
    }

    RemoteCertificateValidationCallback OriginalValidator;
    
    // This method is only needed if your EWS endpoint has a certificate that is not trusted.
    protected void DisableSSLVerification()
    {   //Save the original validator so that it can be restored
        OriginalValidator =
            ServicePointManager.ServerCertificateValidationCallback;
            
        //Replace with a dummy always-true validator
        ServicePointManager.ServerCertificateValidationCallback =
            delegate(Object obj, 
            X509Certificate certificate, 
            X509Chain chain, 
            SslPolicyErrors errors)
            {
                return true;
            };
    }

    protected void RestoreVerification()
    {
        //Restore the original validator
        ServicePointManager.ServerCertificateValidationCallback = OriginalValidator;
    }

    protected string GenerateXmlPayload()
    {
        //Leverage XmlDocument to put together some well formed xml to save
        //the form data.
        XmlDocument x = new XmlDocument();
        x.LoadXml("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<HelpTicket/>");
        XmlNode xTitle = x.CreateElement("Title");
        xTitle.InnerText = tbTitle.Text;
        XmlNode xDetails = x.CreateElement("Details");
        xDetails.InnerText = tbDetails.Text;
        XmlNode xDate = x.CreateElement("Date");
        xDate.InnerText = System.DateTime.Now.ToString();
        XmlNode xUser = x.CreateElement("User");
        xUser.InnerText = Page.User.Identity.Name; 
        x.DocumentElement.AppendChild(xTitle);
        x.DocumentElement.AppendChild(xDetails);
        x.DocumentElement.AppendChild(xDate);
        x.DocumentElement.AppendChild(xUser);
        return (x.OuterXml);
    }
    
    protected void bSubmit_Click(object sender, EventArgs e)
    {
        try
        {
            //Use Page.User.Identity (this is the logged in user) to generate
            //a principal name for the EWS impersonation.
            string[] parts = Page.User.Identity.Name.Split("\\".ToCharArray());
            string PrincipalName = parts[1] + "@" + parts[0];
            ExchangeService exchange = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
            exchange.ImpersonatedUserId =
                new ImpersonatedUserId(ConnectingIdType.PrincipalName, PrincipalName);
            //Set EWS endpoint to local host (may not work for all setups)
            exchange.Url = new Uri("https://localhost/ews/exchange.asmx");
            Microsoft.Exchange.WebServices.Data.EmailMessage message =
                new Microsoft.Exchange.WebServices.Data.EmailMessage(exchange);
            message.Subject = "HELPTICKET: " + tbTitle.Text;
            message.Body = "This is a HelpTicket Form";
            //Add XML Payload as file attachment.
            message.Attachments.AddFileAttachment("FormData.xml",
                System.Text.Encoding.Default.GetBytes(GenerateXmlPayload()));
            message.ToRecipients.Add(Request.QueryString["ea"]);
            //Set ItemClass so that our custom viewing form will be used.
            message.ItemClass = "IPM.Note.HelpTicket";
            //DisableSSLVerification(); //Uncomment these lines if your CAS server's certificate is not trusted
            message.SendAndSaveCopy();
            //RestoreVerification();    //Uncomment these lines if your CAS server's certificate is not trusted

            //Set fields to ReadOnly and hide/show appropriate buttons            
            bSubmit.Visible  = false;
            bClose.Visible = true;
            tbTitle.ReadOnly = true;
            tbDetails.ReadOnly = true;
        }
        catch (Exception ex)
        {
            //If we get an exception, dump it to the user
            lDiag.Text = ex.ToString();
        }
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>New Helpdesk Ticket</title>
</head>
<body style="background-color:#E0E0F0;">
    <form id="form1" runat="server">
    <div>
    <h2 style="text-align:center;">Create a new Helpdesk Ticket</h2>
        <table style="width:100%;border-style:groove;background-color:White;">
            <tr>
                <td style="width:150px;">
                    Issue Title</td>
                <td style="width:auto;">
                    <asp:TextBox ID="tbTitle" runat="server" Width="99%"></asp:TextBox></td>
            </tr>
            <tr>
                <td>
                    Details</td>
                <td>
                    <asp:TextBox ID="tbDetails" runat="server" Width="99%" Rows="10" 
                        TextMode="MultiLine"></asp:TextBox></td>
            </tr>
        </table>
        <br />
        <asp:Button ID="bSubmit" runat="server" Text="Submit" style="width:100%" 
            onclick="bSubmit_Click" />
        <asp:Button ID="bClose" runat="server" 
            Text="Ticket Submitted, Click to Close Window" style="width:100%" 
            Visible="False" />
    
    </div>
    <asp:Label ID="lDiag" runat="server" Text="" Font-Italic="True"></asp:Label>
    </form>
</body>
</html>

With the customizations in place from Part 1 and Part 2, and the NewHelpTicket.ASPX saved in C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\forms\HelpTicket, you should get the following form when you click the “New Helpdesk Ticket” link from the “New” menu in Outlook Web Access.

image

In Part 4 of this series, I will present sample ViewHelpTicket.ASPX that can unpack the attached XML and display it in a format similar to the New Helpdesk Ticket form.

Postedby Rick_H | 2 Comments    
Filed under: , , ,
Exchange Server 2007 SP1, Outlook Web Access Customization: Part 2, UIExtensions.xml
24 March 09 07:49 PM

I’ve seen a few cases come through where customer’s are trying to implement the customization features that were introduced with Exchange Server 2007 SP1.  There’s a few key areas that seem to cause trouble so I figured it would be worth walking through a simple scenario that covered many of the different aspects. (Link to Part 1 of the series)

We continue our effort to implement our HelpTicket Outlook Web Access form by adding an element to the UIExtensions.xml file in the C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\forms\Customization directory.  This file allows you to make a few different types of customizations (all covered in more detail here), but the one that we are interested in is the inclusion of a new element in the New dropdown in the OWA Interface like so:

image

 

The following example UIExtensions.xml file only contains the minimum tags needed for our scenario, but there is a “.template” version of the file in the Customization directory that has samples for the other customization types as well.


<?
xmlversion="1.0" encoding="UTF-8" ?>
<
OWAUICustomizations>
  <
NewItemMenuEntries>
    <
NewItemMenuEntry ItemType="IPM.Note.HelpTicket" Icon="ticket.gif">
          <
stringlanguage="en-us" text="New Helpdesk Ticket"/>
    </
NewItemMenuEntry>
  </
NewItemMenuEntries>
</
OWAUICustomizations>

Note:  the Icon attribute of the NewItemMenuEntry tag specifies the icon to utilize and Exchange looks for this file in the directory where the UIExtensions.xml file is located. To be consistent with the other icons this should be a 16x16 square image that can be displayed in a browser, such as a .gif, .jpg, or .png.

Place the UIExtensions.xml and your ticket.gif  files into C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\forms\Customization and restart IIS with “iisreset /noforce” and you should have a new element in the “New” dropdown in OWA.

Just like for the Registry.xml file, the Event viewer is the best place to start if you are troubleshooting a problem with UIExtensions.xml.  OWA does not write an entry for a successful read of UIExtensions.xml, but you will see an error EventId 72 with a source of MSExchange OWA if there is an error parsing this file, similar to this:

image  

If you have completed the steps from Part 1 and also put in place a dummy NewHelpTicket.ASPX file (this can just be a text or html file saved in the directory referenced in Part 1), then you should be able to launch the form by clicking the “New Help Ticket” item in the New dropdown in OWA.

In Part 3 of this series we will look at building the NewHelpTicket.ASPX file and using Exchange Web Services to create a new mail item to represent our helpticket.

Postedby Rick_H | 8 Comments    
Filed under: , ,
Using Powershell’s new-webserviceproxy with Exchange Web Services
19 March 09 05:55 PM

The other day I was working on a case where the customer was using VBScript and CDO 1.21 to make some changes in some mailboxes, and I couldn’t help but feel that we were working in the past.  Don’t get me wrong, there’s a lot of tried and tested VBScript and CDO 1.21 code out there, but I found myself wondering what the “new” way to do this would be.

I started looking at using Powershell to automate a .Net HttpWebRequest to post to an EWS endpoint, and just build the SOAP requests myself (or leverage an XmlDocument), but while I was researching this I stumbled upon something slightly more interesting.

The version of Powershell that is included with the Windows 7 Beta includes a new cmdlet called new-webserviceproxy and it does exactly what it sounds like.  You can point it at a wsdl file, and it auto-generates a proxy in a way that seems very similar to the Visual Studio auto-generated proxies.

The following is the result of a good deal of trial and error, but what you end up with is a output that displays some basic information about all of the emails in your inbox.

   1:  <# Untested sample code, use at your own risk! #>
   2:   
   3:  ## Setup the Exchange Service Binding
   4:  $uri = "https://exchangeserver/ews/exchange.asmx"
   5:  $exchangeservicebinding = new-webserviceproxy -NameSpace "EWS" -URI $uri -UseDefaultCredential 
   6:  $exchangeservicebinding.RequestServerVersionValue = new-object EWS.RequestServerVersion
   7:  $exchangeservicebinding.RequestServerVersionValue.Version = [EWS.ExchangeVersionType]::Exchange2007_SP1
   8:  $exchangeservicebinding.Url = $uri
   9:   
  10:  ## Create and Populate the Parent Folder ID Collection
  11:  [EWS.DistinguishedFolderIdType]$parentfolderid = new-object EWS.DistinguishedFolderIdType
  12:  $parentfolderid.Id = [EWS.DistinguishedFolderIdNameType]::inbox
  13:  [EWS.BaseFolderIdType[]]$parentfolderids = $parentfolderid
  14:   
  15:  ## Create an ItemShape and set it to return All Properties
  16:  [EWS.ItemResponseShapeType]$itemshape = new-object EWS.ItemResponseShapeType
  17:  $itemshape.BaseShape = [EWS.DefaultShapeNamesType]::AllProperties
  18:   
  19:  ## Create the FindItemType object and populate with the Parent Folder Ids and Item Shape
  20:  [EWS.FindItemType]$finditemtype = new-object EWS.FindItemType
  21:  $finditemtype.ParentFolderIds = $parentfolderids
  22:  $finditemtype.ItemShape = $itemshape
  23:   
  24:  ## Make the call to the webservice
  25:  [EWS.FindItemResponseType]$finditemresponses = $exchangeservicebinding.FindItem($finditemtype)
  26:   
  27:  ## Loop through the returned messages and print some basic info
  28:  [EWS.MessageType]$messagetype
  29:  foreach($messagetype in $finditemresponses.ResponseMessages.Items[0].RootFolder.Item.Items)
  30:  {
  31:      Write-Host "From: " $messagetype.From.Item.Name
  32:      Write-Host "Subject: " $messagetype.Subject
  33:      Write-Host "Received: " $messagetype.DateTimeReceived.Date
  34:      Write-Host "-------------------------------------------------------"
  35:      Write-Host "ItemId: " $messagetype.ItemId.Id
  36:      Write-Host "======================================================="
  37:      Write-Host
  38:  }

The above code obviously has no error handling, but hopefully it shows a basic example of how this new cmdlet can be used.

If you want to dig in further, make use of the get-member cmdlet to examine objects of the types that are generated by the proxy, and you can also inspect the object using format-custom (alias fc) as follows (This can be done for any of the object types in the above code):

   1:  $exchangeservicebinding | get-member
   2:  $exchangeservicebinding | fc
 
Note: I did notice some strange errors if I run the above code multiple times within the same powershell session. I’ll update this post if I figure it out why, but if you receive errors about missing DLLs or if powershell claims that it “Can’t convert from type “EWS.FindItemType” to “EWS.FindItemType” then try starting a new PowerShell session.
Postedby Rick_H | 1 Comments    
What are the Supported Ways To Customize Outlook Web Access for Exchange Server 2007 SP1?
04 February 09 05:57 PM

We seem to get a fair amount of questions and cases where the supportability of certain OWA customizations isn’t clear.  In the past, many organizations have found ingenuous ways to modify the OWA files on the server in order to make them behave in different ways.  Unfortunately, changing these files takes you into uncharted territory.  The OWA web application undergoes extensive testing, and when Microsoft makes any changed to these files, it has to go through testing again.  If you modify a javascript or aspx file, then you are running OWA in an untested and unsupported configuration.

Now, many companies have gone outside of the “supported” realm and made these types of changes anyway, and hopefully they do their own testing to make sure that they aren’t introducing errors.  Unfortunately this isn’t quite enough.  Prior to Exchange Server 2007 SP1 RU5, the update installer would not update files that had been altered.  This meant that if you made a change to a file such as logon.aspx, the file wouldn’t get replaced with the latest version.  For many installations, this behavior simply meant that the customizations would “stick” through the update.  The note above about being in an untested configuration still applies, and you have the added consequence of negating any testing that you did when you initially made the change.  Things can get worse though.  There were some changes made to OWA with SP1 RU3 that depend on an updated logon.aspx file.  If you had made changes to that file prior to SP1 RU3 and you apply RU3 or RU4, OWA ceases to function.  This behavior is noted in the release notes for SP1 RU3 and SP1 RU4, and documented in KB 956582Exchange Server 2007 SP1 RU5 changed this behavior to always replace files even if they have been changed, so that an update doesn’t cause OWA to stop working, but this means that modified files can be replaced.

So, what are the supported ways to customize OWA?  The short answer is:  “If you can find documentation about how to do it on MSDN or Technet, then it is supported.”

Here’s some information on the supported scenarios:

Outlook Web Access Customization Architecture – Overview of how OWA Customization works.

Outlook Web Access User Interface Customization – You can add elements to the navigation bar and the “New” dropdown.

Outlook Web Access Forms Registry – You can register new custom forms to be used for specific item classes (Note: this only works for the default mail folder view in OWA, and won’t work for other folders like the Calendar).

How to Create a Theme for Outlook Web Access – You can customize the look and feel of the OWA client, including the logon and logoff pages*. 

* A special note for branding of the logon and logoff pages:  These changes are limited to the image files and the logon.css file in the Base theme directory.  This is necessary because without a logged in session, the server has no way to check what theme or experience (Basic or Premium) it should use.  Updates to Exchange create a new version-based folder for themes and the modified files should be copied forward. You should plan to test logon and logoff screen customizations after an update to make sure that the non-modified files haven’t changed in a way that causes a problem with your customizations.

Postedby Rick_H | 0 Comments    
Filed under: , ,
Exchange Server 2007 SP1, Outlook Web Access Customization: Part 1, registry.xml
28 January 09 09:20 PM

I’ve seen a few cases come through where customer’s are trying to implement the customization features that were introduced with Exchange Server 2007 SP1.  There’s a few key areas that seem to cause trouble so I figured it would be worth walking through a simple scenario that covered many of the different aspects.

The simple (and contrived) scenario that I’m going to run through is the use of a custom message type to represent a helpdesk request.  This example will cover composing, sending and viewing this custom type, and we’re going to redirect the Reply and Forward actions to the viewing form to effectively disable that functionality.  We’re also not going to handle the different states such as “DRAFT” but these concepts are extensions of the scenario that I’ll cover here.

The first order of business is to decide on the item class that you want to use for your custom items.  For this sample I’ll use IPM.Note.HelpTicket as the item class.  We’ll use this class for the forms registry (Registry.XML), the UI Extension definition (UIExtensions.xml), and we’ll have to set it properly when we create and send the new message.

In reality, the only folders that actually support custom form registration are folders that utilize the default Mail Folder view in Outlook Web Access.  We’re working on updating the documentation on MSDN to more accurately reflect this limitation.  The custom forms registry is an extension of the internal workings of OWA, so there may be some other scenarios that work or partially work, but those scenarios are not tested or supported.

So, let’s get started.  The Registry.XML file is split into two sections.  The first section identifies the class, actions, and states the mapping applies to, as well as the target form that should be used.  The second section is basically a hook that is required so that the first section will be applied by OWA.

The following example Registry.XML file creates a for the IPM.Note.HelpTicket class and maps the New action to the NewHelpTicket.ASPX form, and it maps the Open, Preview, Reply, ReplyAll and Forward actions to the ViewHelpTicket.ASPX form.

<Registry xmlns="http://schemas.microsoft.com/exchange/2004/02/formsregistry.xsd"
          Name="PremiumExtensions" InheritsFrom="Premium" IsRichClient="true">
  <Experience Name="Premium">
    <Client Application="MSIE" MinimumVersion="6" Platform="Windows NT" />
    <Client Application="MSIE" MinimumVersion="6" Platform="Windows 2000" />
    <Client Application="MSIE" MinimumVersion="6" Platform="Windows 98; Win 9x 4.90" />

    <ApplicationElement Name="Item">
      <ElementClass Value="IPM.Note.HelpTicket">
        <Mapping Action="Open"
                 Form="ViewHelpTicket.ASPX"/>
        <Mapping Action="Preview"
                 Form="ViewHelpTicket.ASPX"/>
        <Mapping Action="Reply"
                 Form="ViewHelpTicket.ASPX"/>
        <Mapping Action="ReplyAll"
                 Form="ViewHelpTicket.ASPX"/>
        <Mapping Action="Forward"
                 Form="ViewHelpTicket.ASPX"/>
        <Mapping Action="New"
                 Form="NewHelpTicket.ASPX"/>
      </ElementClass>
    </ApplicationElement>

    <ApplicationElement Name="PreFormAction">
      <ElementClass Value="IPM.Note.HelpTicket">
        <Mapping Action="Open"
Form="Microsoft.Exchange.Clients.Owa.Premium.Controls.CustomFormRedirectPreFormAction,Microsoft.Exchange.Clients.Owa"/>
        <Mapping Action="Preview"
Form="Microsoft.Exchange.Clients.Owa.Premium.Controls.CustomFormRedirectPreFormAction,Microsoft.Exchange.Clients.Owa"/>
        <Mapping Action="Reply"
Form="Microsoft.Exchange.Clients.Owa.Premium.Controls.CustomFormRedirectPreFormAction,Microsoft.Exchange.Clients.Owa"/>
        <Mapping Action="ReplyAll"
Form="Microsoft.Exchange.Clients.Owa.Premium.Controls.CustomFormRedirectPreFormAction,Microsoft.Exchange.Clients.Owa"/>
        <Mapping Action="Forward"
Form="Microsoft.Exchange.Clients.Owa.Premium.Controls.CustomFormRedirectPreFormAction,Microsoft.Exchange.Clients.Owa"/>
        <Mapping Action="New"
Form="Microsoft.Exchange.Clients.Owa.Premium.Controls.CustomFormRedirectPreFormAction,Microsoft.Exchange.Clients.Owa"/>
      </ElementClass>
    </ApplicationElement>
  </Experience>
</Registry>

A few notes on the way that I implemented the above mappings.  The documentation states that multiple “actions” can be space-separated in the same mapping element.  I have not seen this work, so I set the mapping for each action in a separate tag.  I’d also note that the documentation has language that says that if either the Action or State values are omitted from a mapping tag then they will apply “unless a more specific mapping is specified”.  You should be aware that the more specific mapping may be in the built-in registry files, so if you have a mapping that doesn’t seem to be working, you should try explicitly mapping actions and states that you’re trying to handle.  I’ve taken this approach in the above example.

The Registry.XML file can be placed in C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\forms\Customization\ or any folder directly under C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\forms\.  For this example, I’m placing the Registry.XML, ViewHelpTicket.ASPX, and NewHelpTicket.ASPX all under C:\Program Files\Microsoft\Exchange Server\ClientAccess\Owa\forms\HelpTicket\).  Also note that the form paths are relative to where the Registry.xml file is located.

OWA will not read in the new file until is it restarted, and depending on your environment, this may not happen without some action on your part.  To force the restart, you can run “iisreset /noforce” from a command line on the Exchange Client Access server where you are creating these files.

If your file is read in successfully, you can find an event in the Windows Application Event Viewer that looks like the following:

image

(Source = MSExchange OWA, EventId = 73)

If you have an error in your Registry.XML file you’ll instead see an event like this:

image

(Source = MSExchange OWA, EventId = 9)

Event 9 will include a description of the parsing error, along with line numbers and position.  Note also that a bad Registry.XML file can leave OWA in a non-working state, since the mappings are merged into the same structures that handle the normal workings of OWA.

In the next installment of this series, I’ll cover the UIExtenstions.xml file, and how we’ll use that to enable users to create a new item with this custom type.

MSDN Documentation:

Details of forms registry file (registry.xml): http://msdn.microsoft.com/en-us/library/bb891879.aspx

Postedby Rick_H | 1 Comments    
Filed under: , ,
Where’s the MAPI Documentation?
28 January 09 03:38 PM

Many of our customers are still using MAPI for programmatic access to Exchange mailboxes.  The documentation for MAPI was recently relocated on MSDN, so we’re trying to get the word out about where to start.

MAPI Documentation on MSDN (http://msdn.microsoft.com/en-us/library/cc765775.aspx)

For more details on the change in content and organization, head over to the Office Client Developer Content Blog.

Postedby Rick_H | 0 Comments    
Filed under: ,
Messaging Development at the PDC
31 October 08 12:54 AM

If you were at the Professional Developer Conference in L.A. this week, then you're probably exhausted, suffering from a bit of information overload, and either on your way home or getting ready to head that way.  There was a ton of information on so many topics, and thankfully even if you weren't in L.A., you can still watch the sessions online.

I wanted to call out a few sessions that might be of interest to Messaging Developers:

Exchange Web Services Managed API: Unified Communications Development for Exchange - If you use EWS, are thinking about using EWS, or got scared away from EWS by terms like SOAP and WS-*, then you have to watch this session.  Jason Henderson lays out the details of a new managed API that will greatly simplify developing code that integrates with Exchange.

Office Business Applications: Enhanced Deployment - The title of this one doesn't capture all of the interesting topics that Saurabh covered.  In the process of covering the deployment scenario, he also hit on a lot of the changes that are coming to Office Business Application development (AKA: VSTO) in the next version of Visual Studio, including the removal of runtime dependencies on Primary Interop Assemblies (PIA's).

Microsoft Office Communications Server and Exchange: Platform Futures - This session talked about the future of extensibility for Office Communications Server and Office Communicator as well as the UC functionality of Exchange Server.

There's a ton of other great content available at: https://sessions.microsoftpdc.com

 

Postedby Rick_H | 0 Comments    
Filed under: ,
Outlook Web Access Custom Forms for Public Folder Items, ConvertId Fails
29 September 08 09:21 PM

I have seen a few cases now where people are trying to implement custom forms for Public Folder Items under Outlook Web Access (Exchange 2007 Sp1, see here for more details on this functionality).

One problem arises when you run into an item that includes “%2B” in the value for the Id that is passed to the custom form on the Query String.  When you use the typical methods of retrieving the value of this field, such as retrieving the value from the QueryString collection that is a member of the System.Web.HttpRequest object, .NET helpfully UrlDecodes the value that is returned.  When you turn around and use this value to populate an AlternatePublicFolderItemIdType, which in turn is used to populate a ConvertIdType, which is then used to call ConvertId, you will receive an error indicating that the item could not be found.

The simple solution for this issue is to UrlEncode the Id value before placing it into the AlternatePublicFolderItemIdType, like this: 

AlternatePublicFolderItemIdType id = new AlternatePublicFolderItemIdType();
id.Format = IdFormatType.OwaId;
id.ItemId = HttpUtility.UrlEncode(Request.QueryString["Id"]);

One of my colleagues correctly pointed out that this round trip UrlDecode/UrlEncode could possibly return a different value than was originally on the raw query string, but I have not found any cases yet where this happens.

A second nuance of this scenario is that the schema for the AlternatePublicFolderItemId Type requires a value for the FolderId field, but when the source type is IdFormatType.OwaId, this value is ignored. Simply populate this field with an empty string and it will pass schema validation.

Postedby Rick_H | 1 Comments    
Filed under: , ,
SMTP Sinks on Exchange Cluster
25 February 08 04:08 PM

In an Exchange cluster environment with multiple active nodes, Exchange instantiates one IIS virtual server (And therefore one SMTP Virtual Server Instance) for each external interface of the cluster.  This configuration is duplicated on each node of the cluster.

For example, if you have an Active/Active/Passive cluster, then the external interfaces might map to the individual nodes like so:

Ext.        Node
-------------------------
EXV1 -> Node1
EXV2 -> Node2
              Node3 (Passive)

On each node, there will be two SMTP Virtual Server Instances (VSI), labeled EXV1 and EXV2.  Whenever a node becomes active, it starts the VSI that is associated with the external interface that is pointing to it.  So if Node1 fails, and Node3 takes over servicing EXV1, then Node3 will start up its EXV1 VSI.  Likewise if Node2 fails instead, Node3 will start its EXV2 VSI.

Since each SMTP instance has its own event bindings, you must register bindings that apply to any SMTP VSI that could become active at any time. For a clustered Exchange setup this means that you should register the SMTP Sink on every SMTP VSI that exists on every box.

Using the simple example in this KB Article (How to add a disclaimer to outgoing SMTP messages in Visual Basic script), instead of registering with just this:

cscript smtpreg.vbs /add 1 onarrival SMTPScriptingHost CDO.SS_SMTPOnArrivalSink "mail from=*@your-domain-here.com"
cscript smtpreg.vbs /setprop 1 onarrival SMTPScriptingHost Sink ScriptName "C:\EventSinkScript.vbs"

You would have to register additional bindings for all other instances (one for each active node).  So for our Active/Active/Passive cluster mentioned above, you would register the sink like this:

cscript smtpreg.vbs /add 1 onarrival SMTPScriptingHost CDO.SS_SMTPOnArrivalSink "mail from=*@your-domain-here.com"
cscript smtpreg.vbs /setprop 1 onarrival SMTPScriptingHost Sink ScriptName "C:\EventSinkScript.vbs"
cscript smtpreg.vbs /add 2 onarrival SMTPScriptingHost CDO.SS_SMTPOnArrivalSink "mail from=*@your-domain-here.com"
cscript smtpreg.vbs /setprop 2 onarrival SMTPScriptingHost Sink ScriptName "C:\EventSinkScript.vbs"

If you look at the code in the SMTPREG.VBS file's "DisplaySinks" method you can see some hints on how to do this enumeration programmatically.

Postedby Rick_H | 0 Comments    
Introduction
25 February 08 03:58 PM

I have another blog at http://onemanshouting.com that I have maintained for several years with varying degrees of activity.  Since joining Microsoft I haven't made a lot of time for blogging, but I have started running into work-related topics that might make good blog fodder. I decided to start this blog as a location to post information that might be useful to developers who are leveraging various Microsoft technologies in the messaging space.

 

 

Postedby Rick_H | 0 Comments    
Filed under:

This Blog

Syndication

Page view tracker