Welcome to MSDN Blogs Sign in | Join | Help

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 | 1 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

· 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...

FYI: We're Still Hiring in Charlotte, Las Colinas, and Fargo...

I remember working in Richmond, VA for years thinking how awesome it would be to work at Microsoft.  It always seemed so unrealistic - even if I got past the interview process, I'd still have to move across the country.  Not only would it be a hard opportunity to get but it would be a big personal sacrifice to move.  Well all that changed when I heard about a job in Charlotte, NC with Microsoft.  Microsoft was in Charlotte all this time?!  I had no idea.  It changed everything for me.  I learned about a whole new world of Microsoft that wasn't product development but product support.  Helping people find the right menu item so that they can correctly format text or send an email in Outlook didn't sound too exciting to me but helping some of Microsoft's biggest ISV and business customer's make design decisions and fix their Exchange and Outlook applications was worth investigating.  I found that Developer Support, for me, was a great blend of what I like to do with technology: be creative, learn new things, and communicate with people.  I could do all this without the pressure of unrealistic deadlines laid on me by a bad project specifications/contracts, without travelling to a new city for each project, and with the comfort of the best benefits in the business!

We just hired Rick and we are looking for more good folks who want to work in either Charlotte, Las Colinas, or Fargo on our team.  We are looking for .NET and/or C++ developers with good communication skills, a thirst for knowledge, and the ability to have effective technical discussions with peers and customers.  We don't expect candidates to have Exchange or Outlook development experience necessarily - we can teach you that, you just have to be ready to learn!  Feel free to email or IM me if you think you might be interested in a job in Developer Support for Office or Exchange and want to know more...

Posted by mstehle | 2 Comments
Filed under: ,

FYI: After Installing Exchange 2007 SP1 32-bit CDOSYS Might Not Be Registered...

Exchange 2007 only adds 64-bit components in a 64-bit install, when you install Exchange it will register CDOEX.DLL which will replace CDOSYS.DLL and be used when you instantiate CDO.Message.  So you will see CDO.Message pointing to "C:\Program Files\Common Files\Microsoft Shared\CDO\CDOEX.DLL" in the 64-bit registry and you will see CDO.Message pointing to "C:\WINNT\SysWow64\cdosys.dll" in the 32-bit registry.  If your application is a 64-bit process it will load up CDOEX.DLL when you instantiate CDO.Message.  If your application is a 32-bit process it will load up CDOSYS.DLL when you instantiate CDO.Message.

 

After installing 64-bit Exchange 2007 you may notice that 32-bit scripts and applications get a "Library not found" error when using CDOSYS.  This appears to be a side effect of registering the 64-bit CDOEX.dll.  To resolve the issue simply open a 32-bit instance of cmd.exe from the SysWow64 directory and run "regsvr32 cdosys.dll" to re-register the 32-bit CDOSYS.

OOM.NET: Part 2 - Outlook Item Leaks

Outlook item leaks are the most common OOM with .NET issues that we see and I’ve debugged enough of them to compile this list of the four basic mistakes that contribute to item leaks.  An “item leak” is most commonly seen as an item that won’t refresh or can’t be saved in the Outlook UI.  A common example of this problem is that a user will complain that they receive a dialog stating that they can’t save or update an appointment in Outlook when a particular AddIn is loaded.  This is because the AddIn has not released the item reference and Outlook is reusing it.  In order to fix the problem properly you need to look at your code for a few of the common mistakes listed below…

 

Outlook Processes Reuse Cached Item References

If an item is in memory in Outlook.EXE then Outlook will reuse it when another action requests it.  For example, if a COM AddIn leaks an item reference and a user is interacting with that item through the UI they might not be able to save their changes all of a sudden.  This is especially true if the item were to be modified in the MAPI store after it has been leaked.  An easy test would be to modify the suspected leaked item using MFCMAPI while Outlook is running then open the item in the Outlook UI and try to modify it.  You will get a dialog saying that the item cannot be saved if you have leaked the item.

 

Do Not Chain Child Object References

One of the advantages of pure .NET programming is that you don't have to worry about memory management because the garbage collector and the dispose pattern handle all of that.  What we have to remember that OOM programming in .NET is not pure .NET programming ***it is COM programming in .NET*** because of that the rules of COM apply here.  Every object referenced must be released.  When you chain objects you lose track of a reference that you need to release. 

 

See the example below...

 

        // This leaks an Attachments reference

        ProcessAttachments(email.Attachments);

 

        // This doesn’t leak

        Outlook.Attachments oAttachs = email.Attachments;

        ProcessAttachments(oAttachs);

        Marshal.ReleaseCOMObject(oAttachs);

        oAttachs = null;

 

Since the reference to the Attachments object is maintained it can be released properly.

 

Leaking a Child Item Leaks the Parent

If a child item that holds an internal reference to its parent – such as attachments or recipients - is leaked then the parent is in turn leaked.  In the example above, chaining and leaking the child Attachments collection of the email will cause the email itself to leak.  The parent object will never get cleaned up regardless of how many times GC.Collect or ReleaseCOMObject are called.

 

Be Careful with foreach Loops

The basic use of a foreach loop to iterate through items in a collection that have an internal reference to their parent – see above - will leak items.  Either avoid foreach loops altogether by using for loops...

 

        // This leaks, GC.Collect will not help

        foreach (Outlook.Attachment oAttach in oAttachs)

        {

            Marshal.ReleaseCOMObject(oAttach);

            oAttach = null;

        }

 

        // This doesn’t leak

        for (int i = 1; i < oAttachs.Count; i++)

        {

            Outlook.Attachment oAttach = oAttachs[i];

 

            // Do nothing with attachment

            Marshal.ReleaseCOMObject(oAttach);

            oAttach = null;

        }

 

…or follow the notes in this discussion to articulate the handling and cleanup of the underlying objects in the enumerator created by the foreach loop to ensure that things get cleaned up properly…

 

        IEnumerator attachsEnum = oAttaches.GetEnumerator();

        while (attachsEnum.MoveNext())

        {

                    Attachment at = (Attachment)attachsEnum.Current;

                    Marshal.ReleaseCOMObject(at);

        }

        ICustomAdapter adapter = (ICustomAdapter)attachsEnum;

        Marshal.ReleaseComObject(adapter.GetUnderlyingObject());

        Marshal.ReleaseComObject(oAttaches);

 

OOM.NET: Part 1 - Introduction and Why Events Stop Firing...

OOM.NET is not a special API set that was created in managed code.  It is the name I've given to a series of posts I'll do about the “gotchas” of Outlook Object Model development in .NET.  I've compiled some notes over time of the most common issues, how to resolve them, and why they exist.  I'd love to hear back from you on other issues you face with OOM programming in .NET as well so that I can add them to this series...

With the introduction of Visual Studio Tools for Office and the maturity of .NET languages in general more and more Outlook developers are using .NET languages like C# and VB.NET.  The nice thing about .NET is that it encapsulates a lot of common functionality into a framework, manages memory, provides a framework for inter-operating with COM, and allows you to quickly write applications with very little concern for how all this happens.

The bad thing about .NET is that it allows you to quickly write applications with very little concern for how all that happens...

Developers can get caught up in the ease and speed of development in .NET.  They can suffer from an "It Just Works Addiction" - meaning in the bliss of swiftly writing code that just happens to compile and run it is possible to lose sight of what is making everything work.  This mentality simply will not work with Outlook development; especially in .NET because there is just too much that can happen to adversely affect your application.  So on with the show...

Why Events Stop Firing

Typically, the events stop firing problems come from adding an event handler from a locally scoped object or calling ReleaseCOMObject on an object which you are listening to events on.  The code below will eventually stop handling the NewInspector event because inspectors is locally scoped here and when GC runs it will get cleaned up thus stopping the event to fire…

 

        private void ThisAddIn_Startup(object sender, EventArgs e)

        {

            Outlook.Inspectors inspectors;

            inspectors = Application.Inspectors;

            inspectors.NewInspector += Inspectors_NewInspector;

        }

 

To fix it you would need to make inspectors a module level variable and call ReleaseCOMObject within a dispose method or in this case inside ThisAddIn_Shutdown.  You wouldn’t want to call ReleaseCOMObject on inspectors within ThisAddIn_Startup because it would free the underlying COM object and also cause your event to stop firing.

 

        private Outlook.Inspectors inspectors;

        private void ThisAddIn_Startup(object sender, EventArgs e)

        {

            inspectors = Application.Inspectors;

            inspectors.NewInspector += Inspectors_NewInspector;

        }

 

However, in Inspectors_NewInspector you *would* want to call ReleaseCOMObject on inspector once you were done with it.  For example…

 

        private void Inspectors_NewInspector(Outlook.Inspector inspector)

        {

            try

            {

                MessageBox.Show(inspector.Caption);

            }

            finally

            {

                Marshal.ReleaseComObject(inspector);

            }

        }

 

You wouldn’t want to call ReleaseCOMObject until you were done with that particular reference though.  In this case all the work is done within the try/finally block so we can safely release when we are done.  Also, it is highly recommended that you use a try/finally block to release objects in this scenario because it ensure the reference will get released even if there is an exception.

More Posts Next page »
 
Page view tracker