Welcome to MSDN Blogs Sign in | Join | Help

OOM.NET: New Outlook Hotfix for Item Leak Scenarios

The latest hotfix package for Outlook was released this week which resolves many of the .NET item leak side effects in the calendar by changing the behavior of Outlook to not reuse calendar item references in memory and instead reload them from the store.  The KB article calls out a specific scenario of unintentionally saving and sending a meeting request, however, the fix actually goes beyond this scenario and I would recommend it for any .NET item leak problems involving calendar items.

957692  Description of the Outlook 2007 hotfix package (Outlook.msp): October 28, 2008

http://support.microsoft.com/kb/957692

"Consider the following scenario:

•In Outlook 2007, a user has a managed add-in installed. This managed add-in is listening to an Application level event.

•As a meeting organizer, the user sends a meeting request to an attendee.

•Then, the organizer opens the meeting appointment to make some changes.

•After that, the organizer clicks Send Update to try to send the meeting update.

•During the sending of the meeting update, the organizer cancels the send and clicks Don't save changes.

In this scenario, a meeting update is sent to the attendee unexpectedly. Additionally, the changes to the meeting appointment are also saved in the organizer's calendar. "

If you have a .NET Item leak issue involving meetings or appointments, this fix may resolve it.  If you are experiencing such an issue with a managed add-in then you should click the "View and request hotfix downloads" link at the top of the KB article.  Please note, this does not replace the need to properly release references in add-in code.

Posted by mstehle | 1 Comments
Filed under: ,

Now THAT is a shutdown!

Steve asked me to link to a post from Ryan Gregg detailing some changes in Outlook 2007 SP 2 Beta.  Here is what Steve wrote…

Ryan posted an article about some changes we're making in Outlook 2007 SP2. The gist is we're changing Outlook's shutdown behavior to always shutdown regardless of whether there are other running applications using the Outlook Object Model. He's looking for feedback on the logic change, so be sure to send him your comments.

Posted by mstehle | 1 Comments

FYI: Exchange API spotting - Exchange 2007 SP1 RU4 Released

The Exchange Development Blog has a new post about the recently released Rollup 4 for Exchange 2007 SP 1.  There are several key changes to Exchange Web Services in this release that are discussed in the post…

“…If you have written code against Exchange Web Services, we definitely recommend that you take a look at RU4. The following are the biggest changes that will be coming in the rollup:

- Item IDs are now returned after MoveItem/CopyItem calls.

- Unknown/unsupported item types are returned as Messages.

- Updating tasks can now have correct behavior for Start and Complete Date values…"

TalkBack: Synchronous Event Processing In Your Custom Applications

The Exchange Developer blog has a post updating the future vision of Exchange development.  If you remember, there was a post on the Exchange Team blog back in September 2005 that gave us the first glimpse of this vision leading up to Exchange 2007.  The vision has come into focus some more but the overall direction hasn't changed in the two and a half years since the first Exchange 12 roadmap post.

Here are the important notes from the roadmap…

  1. Exchange Web Services are the API of choice in Exchange 2007 and beyond for mailbox and public folder access.
  2. The web store APIs (WebDAV, CDOEX, and Store Events) which are de-emphasized in Exchange 2007 will be going away in the next release.
  3. The MAPI and CDO 1.21 download will be aligned with the lifecycle of Exchange 2003, meaning they will be moving to extended support in the near future.
  4. Since at this point store event sinks will be gone in the next version of Exchange, synchronous event processing with OnSyncSave and OnSyncDelete will not be possible.

We’d like to hear from you about the last point…

I am working with the product team to discuss any scenarios where EWS notifications and Transport Agents might not be sufficient to replace synchronous store event processing in a custom solution.  The Exchange development team and my Exchange support team are aware of the potential challenges arising from changing the development interfaces available.  Between Exchange Web Services notifications and Transport Agents we feel that we can fill a lot of the gaps left by removing synchronous store event processing but we want to hear from you.  Are there scenarios that you currently require synchronous event processing for right now?  Please email me or leave a comment describing your scenario. Getting your feedback will help us prepare documentation, workarounds, or solutions that can mitigate the challenges presented by this change.

Hey, that's me!

The Exchange SDK has just been refreshed.  Part of that refresh includes pictures of Steve, Glen, and me by our blog feeds displayed on the main page - pretty neat!

Posted by mstehle | 0 Comments

OUTBOX: Understanding and Fixing Slow Exchange Web Services Code (Part 2)

In part 1 of this series I showed how to and the importance of doing the minimal number of requests, emphasizing the use of the IdOnly shape until we really needed more properties.  I also illustrated the affect of batching requests and finding a balance between the number of items requested in a batch and the total time to retrieve a set of items...

Even after applying these techniques in the calendar folder performance can still suffer.  Doing an AllProperties request for even a single item in the calendar can be exponentially slower than an AllProperties request for a single item in the inbox (or other "non-calendar" folder).  Since Exchange's genesis the calendar folder has always been a special case when writing client applications - there is a ton of business logic and background processing that goes on to make everything work just right.  Because of this, it might be tempting to just write off this performance hit to the "crazy 'ole calendar folder" but there is an answer and it lies in exactly what properties are requested on each item during an AllProperties request on a CalendarItem...

Understand the AllProperties BaseShape and Schema Properties

It turns out that this is a key concept in Exchange Web Services (read more here).  There is no SELECT * or GetProps(NULL)  in EWS, clients should know what properties they want to request and request explicitly.  What about the AllProperties shape?  Doesn't that return "all properties"?  In fact it does not (not that the previously mentioned methods return absolutely every property on an item either) and that may be confusing to some but it is the way it is.  What AllProperties returns is the full EWS schema for the given item type - in other words the property list returned will change based on the message class of the item requested.  This decreases the value of this base shape, reducing it to more or less a lines-of-code-saver.

Many of the EWS schema properties do not correlate directly to a single column on the item in the Exchange database, instead they are calculated on the client access server based on the values of multiple columns.  Some of these calculations, especially ones in the calendar folder, can be quite expensive.  In the CalendarItem schema there are four properties whose names indicate they may take a significant amount of effort to calculate: AdjacentMeetings, AdjacentMeetingCount, ConflictingMeetings, and ConflictingMeetingCount.  Calculating the values of these properties can be assumed to require knowing about all the other items in the calendar - it might be time consuming to do that for every item in the calendar.  What would performance be like if the request was for AllProperties minus these four properties?..

The Solution

The first question is, "How do I request AllProperties minus AdjacentMeetings, AdjacentMeetingCount, ConflictingMeetings, and ConflictingMeetingCount?"  The question, as stated, has no solution.  However the reverse is possible, request IdOnly plus all the other CalendarItem schema properties by specifying the schema properties explicitly using their FieldURI adding them to the AdditionalProperties collection on the ItemShape.  The code looks something like this...

GetItemType git = new GetItemType();
git.ItemIds = itemIds.ToArray();
git.ItemShape = new ItemResponseShapeType();
git.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;

List<BasePathToElementType> props = new List<BasePathToElementType>();

// Only add these properties if we are querying the calendar folder...
if (folderId.Id == DistinguishedFolderIdNameType.calendar)
{
    //props.Add(GetProp(UnindexedFieldURIType.calendarAdjacentMeetingCount));
    //props.Add(GetProp(UnindexedFieldURIType.calendarAdjacentMeetings));
    props.Add(GetProp(UnindexedFieldURIType.calendarAllowNewTimeProposal));
    props.Add(GetProp(UnindexedFieldURIType.calendarAppointmentReplyTime));
...
}

git.ItemShape.AdditionalProperties = props.ToArray();

It might seem tedious to request each property explicitly, however this approach can ensure the efficiency of EWS client applications by eliminating unused data from requests and responses.  I'd consider this a best practice when requesting item properties.  Let's look at my test results:

First, I created a simple application which calls GetItem with AllProperties against every appointment in a test account's calendar.  I tested with individual requests for each item and batching items in the requests.  Here are the results...

Items per request Total # of Requests Time (seconds) Test 1 Time (seconds)
Test 2
Average Time (seconds)
1 1432 851 895 873
25 58 762 777 769.5
50 29 762 759 760.5

...And then I changed the application to call GetItem with IdOnly and added every schema property except AdjacentMeetings, AdjacentMeetingCount, ConflictingMeetings, and ConflictingMeetingCount...

Items per request Total # of Requests Time (seconds) Test 1 Time (seconds)
Test 2
Average Time (seconds)
1 1432 104 105 104.5
25 58 15 15 15
50 29 13 13 13

...As you can see the results aren't even close, those few extra properties really cost you.  In the end, it is best to request the properties needed explicitly.  The expense of calculated properties and the reality of what AllProperties actually returns greatly devalues this BaseShape and for most purposes it should be avoided.

Updated 8/15/08 - I forgot to include the code above...

Posted by mstehle | 2 Comments

Understanding How You Use This Blog

Ed has asked if a few of us could help out with his survey by posting a link to it here...

Greetings Blog Readers,

My name is Ed Jolly, and I am a director in the Commercial Technical Support (CTS) organization at Microsoft. I am here to request a few minutes of your time.

We would like to learn more about blog readership through a brief survey. This is an opportunity for us to better understand what is valuable to you and what you would like to see in the future.

Below is a link that will take you to another website to complete the survey. Based on what we learn, we may request more feedback in future surveys like this.  When you open the survey, you will see a list of blogs that CTS engineers contribute to across many different products. We have not posted a listing of these blogs in the past, and I hope it helps you find other blogs that are helpful to you.

The blog survey is completely anonymous.

· Location: http://www.tsisurveys.com/mssurveys/blog/index.asp [mstehle, 8/22/2008] The survey is now closed.

· Availability:  Until August 22. You may receive a request to complete this survey through multiple RSS feeds. You need only to complete it one time.

· Length: The survey can be a maximum of 11 questions.

· Time: Less than 5 minutes (but providing more information in the open text fields may take a minute or two extra, improving our ability to understand your needs in these blogs).

Thank you in advance for your time, participation and assistance.

Ed Jolly (edjolly@microsoft.com)

Posted by mstehle | 0 Comments

Take Notice of Exchange Web Service Notifications

The Exchange API-spotting blog, which is run by the Exchange MSDN content and Exchange API product  folks, as a new post about the transition from store event sinks to Exchange Web Service notifications.  The aim is to provide some more detail about notifications and try to directly answer some questions that have been posed by developers about how notifications can realistically replace store event sinks when they go away.

...If you have feedback for the product team about the transition or any scenarios that you are concerned about, please comment on their blog post.  These posts are going up well before the next release of Exchange in order to keep you informed but also to hear what you have to say.  The nice thing about Exchange 2007 is that you have EWS notifications already available to test with so while your store sinks might work now, you should begin thinking about and testing the migration of your sink solution to one based on EWS notifications.  If you hit roadblocks now my team can work with you on Exchange 2007 and get answers before the upcoming release where Exchange store sinks are not a fallback option...

In fact, the Exchange MSDN content team has been very busy spreading the word about what the transition from the gang of de-emphasized APIs (CDO 1.21, CDOEX, WebDAV, etc.) to the new ones (EWS and Agents) will look like...

Migrating from Exchange 2007 Legacy APIs

Migrating to Exchange Web Services, Part 1: Messaging

Exchange 2007 Legacy API Property Mapping

Disclaimer: This post is about Disclaimers...

I was reviewing a customer's case today and it reminded me of a topic I meant to blog about a while ago...

Ever since Exchange 2000/2003 customers have tried to apply the disclaimer SMTP sink sample to internal mail and found that the disclaimer text is not stamped on these messages.  Of course in Exchange 2000/2003 this won't work.  This is further described in the Exchange 2003 documentation on TechNet about The Advanced Queuing Engine...

"...the CDO interface does not support changing the content of store-submitted messages. This is a limitation that CDO_OnArrival event sinks share with all other event sinks. This limitation exists because Exchange converts a store-submitted message to a temporary SMTP version for the event sink to handle, and then discards the temporary version after the sink finishes processing...

...Because Exchange discards the temporary copy of a store-submitted message, you cannot use an event sink to add a disclaimer or other modifications to all outbound messages, unless you force all messages to be received through SMTP. To do this, you must install the event sink on a bridgehead server that is separate from the mailbox servers in your organization."

This is one of the better explanations about why disclaimers don't work for internal mail in Exchange 2000/2003 and provides more detail than the KB article.  What you don't get from the KB article or the TechNet documentation is that in Exchange 2007 this all changes...

The entire transport process is redone to the point that not only can internal messages have disclaimers added to them but there is no scripting or coding involved in doing so.  Exchange 2007 has built in transport rules that an administrator can turn on and configure as needed without writing code.  The impact on Exchange development here is that the Exchange 2000/2003 experience leads many of us to assume that tasks like adding disclaimers, prepending text to the subject lines,  adding headers, adding recipients, or redirecting messages requires code but in Exchange 2007 it does not

...Of course not every solution fits within the fixed set of conditions and actions defined for these transport rules - in those cases a custom transport agent can be used to implement whatever business logic is needed.  However, it is important to remember this functionality exists in Exchange 2007 before setting out on developing a custom solution as to not reinvent the wheel....

OUTBOX: Understanding and Fixing Slow Exchange Web Services Code (Part 1)

Recently I was working with a customer who was concerned about Exchange Web Services performance.  He was testing some EWS code whose purpose was to retrieve all properties of every item in a mailbox.  The code was structured like this...

This first thing to take note of is that there is too much information being requested in the base shapes of these requests.  Each of these requests is done requesting an AllProperties base shape.  A very simple change to improve speed would be us to use IdOnly base shapes on the requests until making the GetItem calls on the items themselves at which point all properties are desired...

  • GetItem | IdOnly- Get the root folder of the mailbox (one call)
    • FindFolder | IdOnly - Get the folder and the FolderIds of its sub folders (recursive, one call for each folder)
      • FindItem | IdOnly - Find each item in the folder (one call for each folder)

This code is still very chatty, it currently makes one FindFolder and one FindItem for each folder plus one GetItem for each item in the folders.  That is a lot of requests and responses going back and forth.  Fortunately, these calls can be restructured and combined to reduce chattiness.  Notice that FindFolder has the option for a deep traversal in its shape, this makes it possible to retrieve all the FolderIds for all folders underneath the mailbox root in a single request and response.  There is no deep traversal option in the base shape of a FindItem request and there is no ParentFolderIds property on the GetItem request but GetItem requests can request multiple ItemIds.  So while there still needs to be one FindItem request per folder, the GetItem calls can be batched instead of doing one per item...

  • FindFolder | IdOnly, Deep - Get the FolderId for every folder under the mailbox root (one call)
    • FindItem | IdOnly - Get the ItemIds for every item in the folder (one call per folder)
      • GetItem | AllProperties - Get the full set of properties on each item (approximately one call per folder)

This greatly reduces the number of requests made and in turn increases the performance of the code.  Notice that for the GetItem call it says, "approximately one call per folder".  Just because an ItemId array of 1500 items can be sent with a GetItem request doesn't mean it is the best idea.  Certainly batching the GetItem calls help performance but this should be broken up into a "reasonable" amount of items per request - large requests can take minutes to process and return.  The ideal threshold can be determined by taking into account the deployment environment, application requirements, and testing results for each application.

...Just to illustrate the point, here is some data from tests I did.  In this test I'm simply calling GetItem|AllProperties on every item in a test account's inbox.  The folder has about 1500 items in it of vary sizes, some of the items are pretty large.  There is just one Exchange server with all roles installed on it.  The time recorded here is the time it takes to get through all the GetItem requests and responses for the items in the inbox.  "Items per Request" is the number of ItemIds included in each GetItem request.  Remember, these results are specific to my environment and what I'm doing in my code, your mileage may vary...

Items per Request Total # of Requests Time (seconds) Test 1 Time (seconds) Test 2 Average Time Time per request
1 1531 250 262 256 0.17 seconds
2 766 193 188 190.5 0.25 seconds
5 307 149 151 150 0.49 seconds
10 154 137 135 136 0.88 seconds
25 62 129 129 129 2.08 seconds
50 31 135 130 132.5 4.27 seconds
100 16 134 131 132.5 8.28 seconds
250 7 132 131 131.5 18.79 seconds
500 4 131 134 132.5 33.13 seconds
1000 2 133 134 133.5 66.75 seconds
2000 1 135 131 133 133 seconds

DevMsgTeam: New Team Aggregate Blog Feed

As I have often pointed out and linked to there are several members of my team at Microsoft that have active blogs with interesting content.  We've often thought about creating a team blog, much like the Exchange team or Outlook team or even the Exchange Developer team.  However, those seemed like time consuming solutions that would create yet another place to get our posts from.

Instead we decided to create a common tag among all our common Exchange and Outlook developer support blogs posts (DevMsgTeam) so that there is one URL for all the posts on the team.  Enjoy!

The feed will aggregate here.

You can subscribe to the RSS feed here.

 

Posted by mstehle | 0 Comments
Filed under:

FYI: MSDN Protocol Docs...

Steve posted a link to the MSDN protocol documentation.  He talks about them here and here as well.

OOM.NET: Part 5 - Event Planning

Plan For What You Can Control

One of the common scenarios in OOM programming in managed code that required calling GC.Collect() was handling events.  As has been discussed earlier in this series, item references need to be released before they go out of scope.  This includes items passed into event handlers.  Take the ItemSend event for example...

// This leaks 'Item'
void app_ItemSend(object Item, ref bool Cancel)
{
    
}

// This does not leak...
void app_ItemSend(object Item, ref bool Cancel)
{
    try
    {

    }
    finally
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(Item);
    }
}

...In most cases you would expect that the caller invoking the delegate and passing 'Item' would be responsible for releasing it since that is where it was created.  However, that is not the case here - 'Item' must be released within the delegate, the caller does not call ReleaseCOMObject().

Know The Symptoms

The most common symptoms of this kind of leak (especially with the ItemSend event) are scenarios involving sending meeting requests and meeting updates.  If there is a leak in the ItemSend event and a meeting request or update is sent out the appointment in the calendar is ultimately leaked.  The user experience goes something like this...

  1. Organizer sends a meeting update.
  2. Organizer makes a change to the appointment item in his/her calendar.
  3. Outlook prompts the organizer to send a meeting update and save the changes.
  4. Organizer attempts to send the meeting update or save and close and Outlook throws a warning/error dialog stating that the appointment has been modified elsewhere and cannot be saved.

Another common scenario is there is a leak in the NewInspector event.  Typically the symptom here is that a user will open a message and close it, then try to open the message again at which time Outlook displays a dialog stating that the "Operation failed."

Get Fixes For What You Can't Control

There were some scenarios in Outlook 2003 and Outlook 2007 where - even if the OptionPagesAdd event was the only event handled - users still saw the meeting request experience described above.  To make a long story short the interop assembly is generated by tblimp.exe (more information here) in such a way that if one event on an interface such as ApplicationEvents_11_Events interface then "underneath the covers" they are all handled - in the case of OptionPagesAdd that means ItemSend and ItemLoad are handled too.  Because these events are not handled in the AddIn code there is no way to release the reference the items passed to them.  To address this issue, the Outlook team released a fix for Outlook 2003 and Outlook 2007.  Any Outlook install that is using extensive or multiple .NET AddIns should have the following fixes installed to avoid this issue...

Description of the Outlook 2007 post-Service Pack 1 hotfix package: January 28, 2008

Description of the Outlook 2003 post-Service Pack 3 hotfix package: December 13, 2007

OOM.NET: Part 4 - Don't Thread On Me

Patrick posted a discussion of multithreading with Outlook Object Model and why it doesn't help to make OOM calls on a seperate thread...

"Outlook Object Model is run in a STA COM server. This means that all OOM calls are executed on the main thread...You don't gain any performance [when multithreading] because all the calls are going to run on the same thread anyway and you incur the overhead hit of doing the marshaling to begin with, so there's not really an advantage to multithreading Outlook Object Model."

The typical scenario is a VSTO or .NET Shared AddIn for Outlook that wants to do some kind of "background" processing of items in a seperate thread as to not hold up the UI.  This other thread will have OOM code in it that loops through items, folders, or both.  This is the kind of thread that actually hurts performance - using OOM on a seperate thread rather than the main thread.  If an AddIn makes use of seperate threads for working with things outside Outlook such as a database, filesystem, etc. then these recommendations don't apply necessarily.

Creating new threads and even marshalling between them is pretty easy in .NET and like many things in .NET (and VB6 before it) is that this marshalling and threading is supposed to "just work".  Developers can get a lot of this working with out really understanding what is going on and what is required to make this work.  An understanding of COM and what it means to STA like Outlook Object Model is required to come to the conclusions in Patrick's post.

OOM.NET: Part 3 - Back to the Basics, MSDN Must Reads

The Outlook Developer Reference on MSDN has great information on .NET and COM interop which I would consider a prerequisite to any managed code development with Outlook Object Model.  It simply isn't enough to know how to accomplish tasks with OOM or to call GC.Collect here and there - it is important to understand the whole story...

Introduction to Interoperability Between COM and .NET - This is a short and sweet run through of COM Interop and sets up a foundation for understanding where some of the issues come from in my previous posts (OOM.NET Part 1 & Part 2).  Be sure to pay attention to the following quote...

"...a COM client manages an internal reference count provided by IUnknown to free a coclass, a .NET client relies on the runtime garbage collector provided by the .NET platform to free an object..."

...That is why RelaseCOMObject is so important - in the Outlook world of item in reuse we cannot rely on the runtime garbage collector provided by the .NET platform to free an object.

There is also some 101 type information about what a PIA is found in Why Use the Outlook PIA - the take away from this is, "Any number of interop assemblies can exist to describe a given COM type. As publisher of the type library, Outlook provides a Primary Interop Assembly (PIA) that contains the official description of the COM-based Outlook object model."  Installing and Referencing the Outlook 2007 PIA takes the next step to show how to add the reference in Visual Studio.  The list of classes and interfaces in OOM as seen by VB6 or VBA differ greatly from what is seen in a .NET project - for an explanation of that phenomenon read, Relating the Outlook PIA with the Object Model.

The MSDN documentation hits on some of the points I made in Part 1 and Part 2 of OOM.NET:

Looking forward to Part 4 of OOM.NET...

A huge area of trouble is in the handling of events and how to structure code and understand the code in the PIA to prevent item leaks.  I'll be posting more information on this topic soon - in the meantime read the following two articles for some background information...

More Posts Next page »
 
Page view tracker