Recently, I was helping someone with a Outlook item leak type issue involving a Task FormRegion. The symptom was that after opening a task, closing it, and reopening the item they were getting the infamous error message, “COM object that has been separated from its underlying RCW cannot be used.” They were familiar with some of the issues discussed here and knew to call ReleaseCOMObject() on objects as they were done with them. However, that is the only part of proper Outlook coding with .NET – you need to use scope and have good timing when you lay out .NET classes that handle events and use Outlook objects. The following class is a simple example of that scope and timing…
Scope
As I pointed out in my original OOM.NET post, you need to consider the scope of the objects whose events you listen to. The key is not to call ReleaseCOMObject() on an item while you are still listening to events from it. In the class below, notice that _task is defined at the module level so that as long as the instance of TaskRegion is alive and listening to the Write() event _task will not go out of scope and get garbage collected by the CLR.
Timing
It seems like there is a general understanding now that Outlook objects need to be released. Additionally, you must *always decrement the event handlers that you add*. However, the humor in a good joke is not just the punch line but also good timing – you have to be strategic about when to call ReleaseCOMObject() and when to decrement the event handler. Since this is a FormRegion class I’m utilizing the FormRegionShowing event to initialize _task and add the event handler and I use FormRegionClosed to remove the event handler and release _task. In a simple item wrapper class you might use the constructor and a dispose method in the same way. The goal is not call ReleaseCOMObject() until the events are unhooked, that way the COM object become separated from your RCW that is still trying to handle events.
…NOTE – I’m also employing Patrick’s fix in FormRegionInitializing for a FormRegion specific leak scenario…
partial class TaskRegion { Outlook.TaskItem _task = null; #region Form Region Factory [Microsoft.Office.Tools.Outlook.FormRegionMessageClass (Microsoft.Office.Tools.Outlook.FormRegionMessageClassAttribute.Task)] [Microsoft.Office.Tools.Outlook.FormRegionName ("ReleaseTaskRegion.TaskRegion")] public partial class TaskRegionFactory { // Occurs before the form region is initialized. // To prevent the form region from appearing, set e.Cancel to true. // Use e.OutlookItem to get a reference to the current Outlook item. private void TaskRegionFactory_FormRegionInitializing(object sender, Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs e) { Marshal.ReleaseComObject(e.OutlookItem); } } #endregion // Occurs before the form region is displayed. // Use this.OutlookItem to get a reference to the current Outlook item. // Use this.OutlookFormRegion to get a reference to the form region. private void TaskRegion_FormRegionShowing(object sender, System.EventArgs e) { _task = this.OutlookItem as Outlook.TaskItem; _task.Write += new Outlook.ItemEvents_10_WriteEventHandler(_task_Write); } private void _task_Write(ref bool Cancel) { System.Diagnostics.Debug.WriteLine("Write fired!"); } // Occurs when the form region is closed. // Use this.OutlookItem to get a reference to the current Outlook item. // Use this.OutlookFormRegion to get a reference to the form region. private void TaskRegion_FormRegionClosed(object sender, System.EventArgs e) { _task.Write -= new Outlook.ItemEvents_10_WriteEventHandler(_task_Write); System.Runtime.InteropServices.Marshal.ReleaseComObject(_task); } }
…This post is a continuation of my efforts to document common issues I’ve seen when .NET programmers write solutions with Outlook’s object model – be they separate executables, VSTO Add-ins, or Outlook FormRegions. To see all the posts in this series check out my posts with the OOM.NET tag…
…To check out more blog posts from Microsoft’s Messaging Developer Support team which supports Outlook, Exchange, and other email-related development using Microsoft APIs check out the DevMsgTeam tag across all MSDN blogs…
Misha Shneerson, a senior developer on the VSTO team, has a great post giving us hope in the next version of the .NET framework and the 4.0 CLR. The feature name Misha uses gives it away, “NOPIA” means no interop assemblies!
…I recently submitted the following KB article for publication but wanted to get the content out. I will update this post when the KB article is published…
SYMPTOMS
Scenario 1
When you submit an Exchange Web Services request to Microsoft Exchange Server 2007 that contains invalid XML characters, you may get the following ErrorSchemaValidation exception response:
“The request failed schema validation: [character], hexadecimal value [value], is an invalid character.”
Scenario 2
When you submit an Exchange Web Services request to Microsoft Exchange Server 2007 that returns item properties in the response containing invalid XML characters using the Visual Studio auto-generated proxy classes, you may get the following InvalidOperationException exception:
”There is an error in XML document” “[character] hexadecimal value [value], is an invalid character.”
”There is an error in XML document”
“[character] hexadecimal value [value], is an invalid character.”
CAUSE
It is possible for Exchange item properties to have values that contain characters outside the valid range in the XML specification which is defined here:
http://www.w3.org/TR/2000/WD-xml-2e-20000814#dt-character
When an Exchange Web Services client makes a request to retrieve that item property, the Exchange server encodes the property values to ensure the response is transmittable. If this response is received by the client and subsequently validated against the XML schema, it will fail validation because of the invalid XML characters.
Conversely, if an Exchange Web Services client tries to send these characters in a request, even if they are encoded, the Exchange server will try and validate the request against the XML schema which will fail.
RESOLUTION
There is no way to successfully post an Exchange Web Service request that contains invalid XML characters. If you are updating a property’s value that contains invalid characters you must decide whether to replace or omit these characters.
In order to read Exchange Web Service responses from Microsoft Exchange Server 2007 that contain invalid XML characters, you should skip XML validation. If you are using the Visual Studio auto-generated proxy classes for Exchange Web Services then you can create a special partial class which overrides the GetReaderForMessage method of the ExchangeServiceBinding class which turns XML validation off:
public partial class MyExchangeServiceBinding: ExchangeServiceBinding { protected overrideXmlReader GetReaderForMessage( SoapClientMessage message, int bufferSize) { XmlReader retval = base.GetReaderForMessage( message, bufferSize); XmlTextReader xrt = retval asXmlTextReader; if(null!= xrt) { xrt.Normalization = false; } returnretval; } }
…Recently, I worked with a customer who was facing the problem described below. We requested a fix but ended up finding a workaround using a Transport Agent. At this time there is no plan to fix this issue in Exchange 2007…
In an OnSyncSave store event in Exchange 2007 every field's value of a plain text message which triggers the event returns the following exception in the first pre-commit event:
"Operation failed to complete and the status is unavailable. The field may be unavailable or the operation was not attempted."
When the OnSyncSave event fires again for this message after the message has been committed to the store the property values can be read.
NOTE: HTML and RTF messages do not have this problem - this only applies to Plain Text messages.
There are no straight forward workarounds, only other options which may or may not suit your application’s needs:
Again, depending on your solution one of these workarounds or combination of two might accomplish what you need. An example of combining two of these options would be to use a transport agent to tag messages as they are sent to a particular mailbox and then use a rule to key off this tag and move those messages to a specific folder on deliver.