Pawel Forys
Posts: 17
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 |
|
Andrei Smolin
Add-in Express team
Posts: 18819
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.
Andrei Smolin
Add-in Express Team Leader |
|
Pawel Forys
Posts: 17
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 |
|
Andrei Smolin
Add-in Express team
Posts: 18819
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.
Andrei Smolin
Add-in Express Team Leader |
|
Pawel Forys
Posts: 17
Joined: 2016-04-27
|
Thanks again, I will implement it per your recommendation.
Kind regards,
Pawel Forys |
|
Andrei Smolin
Add-in Express team
Posts: 18819
Joined: 2006-05-11
|
You are welcome!
Andrei Smolin
Add-in Express Team Leader |
|