InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.

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

InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.
InvalidComObjectException appears vary rarely and for random users 
Pawel Forys




Posts: 16
Joined: 2016-04-27
Hi,
I am dealing with rather rare exception appearing in my Add-in developed with Add-in Express. With around 1000 pretty active users this exceptions happens to a single random user (but 5-6 times in a row) only once every 3-4 days.


System.Runtime.InteropServices.InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.
   at System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget, Boolean& pfNeedsRelease)
   at Microsoft.Office.Interop.Outlook.ApplicationClass.ActiveWindow()
   at AddIn.Infrastructure.OutlookApi.OutlookApiWindowsService.IsComposeButtonPressedInActiveWindow()
   at AddIn.Infrastructure.EventsHandlers.OutlookEventsHandler.OnSendMail(ADXOlItemSendEventArgs e)


I personally never experienced this error during development. I am suspecting that the issue is related to the way I try to handle the Toggle button for an inspector ribbon. The recommended approach is presented here: https://www.add-in-express.com/creating-addins-blog/2013/01/30/preserve-outlook-ribbon-controls-state/. But my implementation uses the index based on Window reference. Here is the code:



using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Outlook;
using AddIn.Domain.Inerfaces.Infrastructure;
using System.Linq;

namespace AddIn.Infrastructure.OutlookApi
{
    public sealed class OutlookApiWindowsService : IOutlookApiWindowsService
    {
        private readonly Dictionary<object, bool> windowsButtonsCache = new Dictionary<object, bool>();
        private readonly IOutlookApiAddinService addinService;

        public OutlookApiWindowsService(IOutlookApiAddinService addinService)
        {
            this.addinService = addinService;
        }

        public void SaveComposeButtonStateForActiveWindow(bool value)
        {
            var application = addinService.GetCurrentApplication();
            var activeWindow = application.ActiveWindow();

            if (activeWindow != null)
            {
                windowsButtonsCache[activeWindow] = value;
            }
        }


        public bool IsCurrentWindowAnExplorer()
        {
            object activeWindow = null;
            try
            {
                var application = addinService.GetCurrentApplication();
                activeWindow = application.ActiveWindow();
                return activeWindow is Explorer;

            }
            finally
            {
                AddinMarshal.ReleaseComObject(activeWindow);
            }
        }

        public bool IsComposeButtonPressedInActiveWindow()
        {
            
                var application = addinService.GetCurrentApplication();
                var activeWindow = application.ActiveWindow();
                if (activeWindow != null)
                {
                    if (!windowsButtonsCache.ContainsKey(activeWindow))
                    {
                        windowsButtonsCache[activeWindow] = false;
                    }
                    return windowsButtonsCache[activeWindow];

                }
                return false;
            
        }




        /// <summary>
        /// Removes reference from the windows buttons cache (used for set the "induct on Send" 
        /// value on each active window ) and releases underlying COM object
        /// </summary>
        /// <param name="window"></param>
        public void DisposeCurrentWindow(object window)
        {
            if (windowsButtonsCache.ContainsKey(window))
            {
                Marshal.ReleaseComObject(window);
                windowsButtonsCache.Remove(window);
            }
        }

        public void Dispose()
        {
            windowsButtonsCache.ToList().ForEach(i =>
            {
                if (i.Key == null)
                    return;
                Marshal.ReleaseComObject(i.Key);

            });
        }
    }
}


Is above way of keeping the window reference and not releasing the window each time, could result in such exception? I am suspecting some sort of race condition. I would like to know your opinion. Due to cumbersome release process I would like to avoid just experimenting with new solution of keeping the flag on the Email object until it is clear that my approach is wrong.

Thanks a lot for a feedback!

Best regards,
Pawel Forys
Posted 19 May, 2017 06:35:35 Top
Andrei Smolin


Add-in Express team


Posts: 14140
Joined: 2006-05-11
Hello Pawel,

I would use the approach we describe in section Updating ribbon controls at run time at https://www.add-in-express.com/docs/net-ribbon-components.php#properties-events. When using this approach, you find the window in which the control is displayed by checking the context. If (if!) you still need to use a dictionary in this case, I suggest using ADXAddinModule.GetOutlookwindowHandle() to get a window handle and use it as key in that dictionary.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 19 May, 2017 07:10:03 Top
Pawel Forys




Posts: 16
Joined: 2016-04-27
Hi Andrei,
Thanks a lot for the feedback. So recommendation here would be Release Inspector object each time? Will that help avoiding aforementioned exception: InvalidComObjectException ?

Regards,
Pawel
Posted 19 May, 2017 07:17:48 Top
Andrei Smolin


Add-in Express team


Posts: 14140
Joined: 2006-05-11
Releasing a COM object instead of holding it is a general recommendation. To have different state of a Ribbon control in different windows, using the context (as shown in that section) lets you release the COM object immediately after use.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 19 May, 2017 07:57:40 Top
Pawel Forys




Posts: 16
Joined: 2016-04-27
Thanks again, I will implement it per your recommendation.

Kind regards,
Pawel Forys
Posted 19 May, 2017 08:28:26 Top
Andrei Smolin


Add-in Express team


Posts: 14140
Joined: 2006-05-11
You are welcome!

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 19 May, 2017 08:46:19 Top