Unknown COMexception from Outlook: 0xE0B40219

Add-in Express™ Support Service
That's what is more important than anything else

Unknown COMexception from Outlook: 0xE0B40219
Outlook 2003 throws "COMException (0xE0B40219): Unknown Error" 
Ben Empson




Posts: 25
Joined: 2008-08-28
Hi Andrei. My user is using Outlook 2003. Also, I have the version neutral Office PIA's which equates to Outlook 2000 (I think), which means there is no Namespace.ExchangeConnectionMode.

I want to switch the PIAs to use the Outlook 2003 version (which I believe has the required property ExchangeConnectionMode), but this was a decision made at the very start of the build of my plugin. Do you know the best way to switch?

Cheers, Ben
Posted 23 Jun, 2009 11:53:35 Top
Andrei Smolin


Add-in Express team


Posts: 16842
Joined: 2006-05-11
Ben,

There's another way: you can use late binding to access any version-specific property or method. See System.Type.InvokeMember() in MSDN.

See also the HostVersion property of the add-in module.

Regards from Belarus (GMT+2),

Andrei Smolin
Add-in Express Team Leader
Posted 23 Jun, 2009 12:14:54 Top
Ben Empson




Posts: 25
Joined: 2008-08-28
Thanks Andrei. I've found a way of switching in the Office 2003 PIA so I have access to the property now.

However, I'm still trying to find how to do what you said - which is use Outlook in the same mode as the user.


                NamenSpace ns = outlookApp.GetNamespace("MAPI");
                if (ns.ExchangeConnectionMode != OlExchangeConnectionMode.olNoExchange)
                    ...



What exactly do I need to set in order to ensure I'm using the same mode as reported by the NameSpace?

Cheers, Ben
Posted 23 Jun, 2009 12:26:49 Top
Andrei Smolin


Add-in Express team


Posts: 16842
Joined: 2006-05-11
Ben,

Check the state of the Use Cached Exchange Mode flag on the user's PC and use the same state in your test enviroment.

Regards from Belarus (GMT+2),

Andrei Smolin
Add-in Express Team Leader
Posted 23 Jun, 2009 14:52:59 Top
Ben Empson




Posts: 25
Joined: 2008-08-28
Ah OK, you mean just for testing. I'll give it a go...
Posted 23 Jun, 2009 16:24:29 Top
Ben Empson




Posts: 25
Joined: 2008-08-28
Hi Andrei. I have now fully tested this with an Exchange connected Outlook, both in Cached Mode and not in Cached Mode. In both circumstances, the plugin works fine in my test VM.

So, I'm still left with a customer with a problem that I cannot replicate. Do you have any other ideas?

Cheers, Ben
Posted 26 Jun, 2009 07:48:06 Top
Andrei Smolin


Add-in Express team


Posts: 16842
Joined: 2006-05-11
Hi Ben,

Could you post your current code? I'd like to see how you release COM objects.

Regards from Belarus (GMT+2),

Andrei Smolin
Add-in Express Team Leader
Posted 26 Jun, 2009 08:50:47 Top
Ben Empson




Posts: 25
Joined: 2008-08-28

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows.Forms;
using Outlook;

using Proj.Domain.Classes;
using Proj.Domain.Library;

namespace Proj.Domain.OutlookCode.Sync
{
    public class OutlookSession
    {
        #region Declarations

        //[DllImport("Mapi32.dll", PreserveSig = true)]
        //private static extern void
        //WrapCompressedRTFStream(
        //        [MarshalAs(UnmanagedType.Interface)] IStream lpCompressedRTFStream,
        //        uint ulflags,
        //        [MarshalAs(UnmanagedType.Interface)] out IStream lpUncompressedRTFStream
        //     );

        private static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private Outlook._Application outlookApp = null;
        private AddinExpress.MAPI.ADXMAPIStoreAccessor adxmapiStoreAccessor1;
        private NameSpace ns = null;
        private MAPIFolder cal = null;
        private Items items = null;
        private TimeZoneInfo tzi;

        #endregion

        #region Constructor
        public OutlookSession(Outlook._Application app, AddinExpress.MAPI.ADXMAPIStoreAccessor adxmsa)
        {
            try
            {
                log.DebugFormat("Entering {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
                this.outlookApp = app;
                this.adxmapiStoreAccessor1 = adxmsa;
                GetSession();

                //get the GMT timezone
                tzi = TimeZoneInfo.GetTimeZone("GMT Standard Time");
            }
            finally
            {
                log.DebugFormat("Exiting {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
            }
        }
        #endregion

        #region Destructors

        ~OutlookSession()
        {
            QuitSession();
        }

        public void Dispose()
        {
            QuitSession();
        }

        #endregion

        #region Utility functions

        private void GetSession()
        {
            try
            {
                log.DebugFormat("Entering {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
                ns = outlookApp.GetNamespace("MAPI");
                
            }
            finally
            {
                log.DebugFormat("Exiting {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
            }
        }

        private void QuitSession()
        {
            try
            {
                log.DebugFormat("Entering {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);

                if (items != null)
                    Marshal.ReleaseComObject(items);

                if (cal != null)
                    Marshal.ReleaseComObject(cal);

                if (ns != null)
                    Marshal.ReleaseComObject(ns);

                items = null;
                cal = null;
                ns = null;
            }
            finally
            {
                log.DebugFormat("Exiting {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
            }
        }

        #endregion

        #region CreateAppointment
        /// <summary>
        /// Creates an appointment in Outlook - the syncStyle determines how the appointment will be checked against 
        /// previously existing appointments.
        /// </summary>
        /// <param name="e">The Event to enter into Outlook</param>
        /// <param name="syncStyle">How the app should sync with existing appointments</param>
        /// <returns>True / False depending on whether the item was synched or ignored</returns>
        public bool CreateAppointment(Event e, Setting.SyncStyle syncStyle)
        {
            AppointmentItem item = null;

            try
            {
                log.DebugFormat("Entering {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);

                //see if the item already exists
                item = FindItemByUID(e.UID);

                if (item == null)
                {
                    //doesn't exist - create a new appointment
                    log.InfoFormat("Item with UID '{0}' does not exist - creating ({1}, {2:yyMMdd})", e.UID, e.Name, e.StartDate);

                    item = (AppointmentItem)outlookApp.CreateItem(OlItemType.olAppointmentItem);
                    SetItemProperties(item, e);
                    return true;
                }
                else
                {
                    switch (syncStyle)
                    {
                        case Setting.SyncStyle.InitialDownloadOnly:
                            log.InfoFormat("Item with UID '{0}' exists - sync style is initial download only, ignoring ({1}, {2:yyMMdd})", e.UID, e.Name, e.StartDate);
                            return false;

                        case Setting.SyncStyle.KeepSynched:
                            log.InfoFormat("Item with UID '{0}' exists - sync style is keep synched ({1}, {2:yyMMdd})", e.UID, e.Name, e.StartDate);
                            SetItemProperties(item, e);
                            return true;

                        default:
                            throw new System.Exception("Unhandled syncStyle: " + syncStyle.ToString());
                    }
                }
            }
            finally
            {
                if (item != null)
                    Marshal.ReleaseComObject(item);
                item = null;

                log.DebugFormat("Exiting {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
            }
        }
        #endregion

        #region FindItemByUID
        public AppointmentItem FindItemByUID(string UID)
        {
            try
            {
                log.DebugFormat("Entering {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);

                //get the calendar
                cal = ns.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);

                //get the items
                items = cal.Items;
                AppointmentItem item = null;

                do
                {
                    bool skipItem = true;
                    if (item != null)
                    {
                        Marshal.ReleaseComObject(item);
                        item = null;
                    }

                    try
                    {
                        item = (AppointmentItem)items.GetNext();
                        skipItem = false;
                    }
                    catch (InvalidCastException ex)
                    {
                        //handle the item not being an AppointmentItem
                        if (ex.Message.Contains("Unable to cast COM object of type 'System.__ComObject' to interface type 'Outlook.AppointmentItem'"))
                            skipItem = true;
                        else
                            throw ex;
                    }

                    if (skipItem)
                        log.DebugFormat("Item was not an AppointmentItem - skipping");
                    else
                    {
                        if (item == null)
                            break;

                        UserProperty up = item.UserProperties.Find("UID", true);
                        if (up != null && up.Value.ToString() == UID)
                        {
                            log.DebugFormat("Found matching item for UID {0}. EntryID = {1}", UID, item.EntryID);
                            return item;
                        }
                        else
                            log.DebugFormat("Item '{0}' on {1:dd/MM/yyyy HH:mm} (EntryID {2}) does not match {3}", item.Subject, item.Start, item.EntryID, UID);
                    }
                } while (true);

                log.DebugFormat("Unable to find item with UID {0}", UID);
                return null;
            }
            finally
            {
                if (items != null)
                    Marshal.ReleaseComObject(items);
                items = null;

                log.DebugFormat("Exiting {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
            }
        }
        #endregion

        #region SetItemProperties
        private void SetItemProperties(AppointmentItem item, Event e)
        {
            UserProperty up = null;
            UserProperty up1 = null;

            try
            {
                if (tzi == null)
                {
                    item.Start = e.StartDate.Value;
                    item.End = e.EndDate.Value;
                }
                else
                {
                    item.Start = TimeZoneInfo.GetIsDaylightSavingsFromUtc(e.StartDate.Value, tzi) ? e.StartDate.Value.AddHours(1) : e.StartDate.Value;
                    item.End = TimeZoneInfo.GetIsDaylightSavingsFromUtc(e.EndDate.Value, tzi) ? e.EndDate.Value.AddHours(1) : e.EndDate.Value;
                }

                if (e.IsAllDayEvent)
                    item.AllDayEvent = true;
                else
                    item.AllDayEvent = false;

                item.Subject = e.Name;
                item.Location = e.Location;
                item.Body = e.Description;
                up = item.UserProperties.Add("UID", OlUserPropertyType.olText, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
                up.Value = e.UID;
                up1 = item.UserProperties.Add("Source", OlUserPropertyType.olText, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
                up1.Value = "Proj";

                item.MeetingStatus = OlMeetingStatus.olNonMeeting;
                item.RequiredAttendees = string.Empty;
                item.Save();
                item.Send();
            }
            finally
            {
                if (up != null)
                    Marshal.ReleaseComObject(up);
                up = null;

                if (up1 != null)
                    Marshal.ReleaseComObject(up1);
                up1 = null;

                if (item != null && item.UserProperties != null)
                    Marshal.ReleaseComObject(item.UserProperties);
            }
        }
        #endregion

        #region RemoveAllProjAppointments
        public void RemoveAllProjAppointments()
        {
            try
            {
                log.DebugFormat("Entering {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);

                //get the calendar
                cal = ns.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);

                //get the items
                items = cal.Items;
                AppointmentItem item;

                do
                {
                    item = (AppointmentItem)items.GetNext();
                    if (item == null)
                        break;

                    UserProperty up = item.UserProperties.Find("Source", true);
                    if (up != null && up.Value.ToString() == "Proj")
                    {
                        UserProperty up1 = item.UserProperties.Find("UID", true);
                        log.DebugFormat("Removing UID: {0}, EntryID: {1}", up1.Value, item.EntryID);
                        item.Delete();

                        Marshal.ReleaseComObject(up1);
                        up1 = null;
                    }

                    //release resources
                    Marshal.ReleaseComObject(up);
                    up = null;
                    Marshal.ReleaseComObject(item);
                    item = null;

                } while (true);
            }
            finally
            {
                if (items != null)
                    Marshal.ReleaseComObject(items);
                items = null;

                log.DebugFormat("Exiting {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
            }
        }
        #endregion

        #region unused testing code - left for reference

        #region CreateAppointment
        public void CreateAppointment()
        {
            AppointmentItem item = null;

            try
            {
                log.DebugFormat("Entering {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);

                item = (AppointmentItem)outlookApp.CreateItem(OlItemType.olAppointmentItem);
                item.Start = Convert.ToDateTime(String.Format("{0:yyyy-MM-dd HH}:00:00", DateTime.Now.AddDays(2)));
                item.End = Convert.ToDateTime(String.Format("{0:yyyy-MM-dd HH}:00:00", DateTime.Now.AddDays(2).AddHours(1)));
                item.Subject = "Proj Event";
                item.Location = "London";
                item.Body = "Some advertising here: http://www.arrayx.co.uk";
                UserProperty up = item.UserProperties.Add("UID", OlUserPropertyType.olText, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
                up.Value = "7qg1s61nvhvm6ahgh3rtifnq58@google.com";

                //SetHTMLNotes(item);
                item.MeetingStatus = OlMeetingStatus.olNonMeeting;
                item.RequiredAttendees = string.Empty;
                item.Save();
                item.Send();
                MessageBox.Show(String.Format("Item created on {0:dd/MM/yyyy HH:mm:ss} - {1:dd/MM/yyyy HH:mm:ss}", item.Start, item.End));
                log.Info(String.Format("Item created on {0:dd/MM/yyyy HH:mm:ss} - {1:dd/MM/yyyy HH:mm:ss}", item.Start, item.End));
            }
            catch (System.Exception ex)
            {
                AppUtils.HandleException(ex);
            }
            finally
            {
                if (item != null)
                    Marshal.ReleaseComObject(item);
                item = null;

                log.DebugFormat("Exiting {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
            }
        }
        #endregion

        #region ListAppointments
        private void ListAppointments(object sender, EventArgs e)
        {
            try
            {
                log.DebugFormat("Entering {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
                ////get the calendar
                //cal = ns.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);

                ////get the items
                //items = cal.Items;

                //StringBuilder sb = new StringBuilder();
                //foreach (AppointmentItem item in items)
                //{
                //    sb.AppendFormat("{0:dd/MM/yyyy HH:mm} - {1:dd/MM/yyyy HH:mm}: {2}{3}", item.Start, item.End, item.Subject, Environment.NewLine);
                //}

                //textBox1.Text = sb.ToString();
            }
            finally
            {
                log.DebugFormat("Exiting {0}", System.Reflection.MethodBase.GetCurrentMethod().Name);
            }
        }
        #endregion

        #endregion
    }
}
 
Posted 26 Jun, 2009 11:48:36 Top
Andrei Smolin


Add-in Express team


Posts: 16842
Joined: 2006-05-11
Ben,

In FindItemByUID, you don't release both up and item.UserProperties.

In SetItemProperties, you don't release item.UserProperties.

In RemoveAllProjAppointments, you don't release item.UserProperties.

In public void CreateAppointment(), you don't release both up and item.UserProperties.

Don't use foreach to cycle through Outlook items: it creates and doesn't release COM references behind the scene. Don't use message boxes: they interfere with Outlook windowing; instead, use System.Diagnostics.Debug.Writline and DebugView (http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx).

If this doesn't help, please send me your project, I'll test it on a PC of ours.

Regards from Belarus (GMT+2),

Andrei Smolin
Add-in Express Team Leader
Posted 29 Jun, 2009 07:14:58 Top
Ben Empson




Posts: 25
Joined: 2008-08-28
Hi Andrey, thanks for looking through the code and providing comments, much appreciated :)

Of the methods you mentioned, only 2 are used during the processes I've been having problems with, these are FindItemByUID and SetItemProperties. I've modified FindItemByUID to release item.UserProperties, and SetItemProperties is already releasing item.UserProperties in the finally block.

RemoveAllProjAppointments is not used in the current scenario (but I've modded it anyway), CreateAppointment is old code I developed during the initial testing phases of the project and will never be used. Likewise the ListAppointments method is not used (the foreach is commented out), and FindItemByUID uses items.GetNext() to retrieve the appointments in a loop.

How can I send you my project? Would you like an installer or the full code?

Cheers, Ben
Posted 30 Jun, 2009 04:50:29 Top