Understanding the manage COM-Addins dialog

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

Understanding the manage COM-Addins dialog
 
OliverM


Guest


I am trying to figure out which events are going to fire, if a user selects
Excel->File->Options->Add-Ins->COM Add-Ins and unchecks or removes my AddIn.

So far I tried to subscribe to the following events with no luck
- AppDomain.CurrentDomain.UnhandledException
- AppDomain.CurrentDomain.DomainUnload
- AppDomain.CurrentDomain.ProcessExit
- AppDomain.CurrentDomain.FirstChanceException

None of the above fired, instead Excel crashes properly (most likely because I can not do all clean up needed).

I am out of ideas which events to hook in order to run my clean up code.

Kind regards
Oliver
Posted 07 Oct, 2015 05:34:51 Top
nwein




Posts: 577
Joined: 2011-03-28
I think AddinBeginShutdown (in your ADXAddinModule) is what you are looking for.
You can also handle XLL removal by registering the OnRemove event (in your ADXXLLModule)
Posted 07 Oct, 2015 10:13:48 Top
OliverM


Guest


Appreciate your help, dude. Found the reason why AddIn crashes Excel on DomainUnload.
Will post problem description tomorrow.
Posted 07 Oct, 2015 12:56:13 Top
Andrei Smolin


Add-in Express team


Posts: 18829
Joined: 2006-05-11
Thank you Nir!

Hello Oliver,

OliverM writes:
Will post problem description tomorrow.


That would be great!


Andrei Smolin
Add-in Express Team Leader
Posted 08 Oct, 2015 01:43:08 Top
OliverM


Guest


The reason why my AddIn crashes Excel on DomainUnload but not on normal AppExit is as simple as it is surprising. I implemented custom events when excel calculation mode changes https://www.add-in-express.com/creating-addins-blog/2011/12/05/excel-calculation-mode-event/

First I did a small test project to get it going and sorted out a small issue with https://www.add-in-express.com/forum/read.php?FID=5&TID=13296&MID=67687#message67687

Once it worked I shifted the code to the main project and deleted all System.Diagnostics.Debug.Print statements from the code. I should not have done that!
Chasing the issue why Excel crashes I put them back in and it showed me the root of the problem.

In the test project debug.print statements for calculation mode change to manual looked like

adxRibbonCommandManual_OnAction. cancelAction=True
AddinModule_OnSendMessage. WM_OptionManual
adxRibbonCommandManual_OnAction. cancelAction=False


In the main project debug.print statements for calculation mode change to manual now looks like
adxRibbonCommandManual_OnAction. cancelAction=True
AddinModule_OnSendMessage. WM_OptionManual
adxRibbonCommandManual_OnAction. cancelAction=False
AddinModule_OnSendMessage. WM_OptionManual
adxRibbonCommandManual_OnAction. cancelAction=False

ADXRibbonCommand method seems to fire once too often. I have absolutely no idea what is causing this behavior!

This nicely explains why Excel crashes on DomainUnload. In order to cover the use case calculation mode is changed using Ribbon-File-Options-Formula an ADXRibbonCommand is used. Again ADXRibbonCommand fires 3 instead of 2 times.

If Ribbon-File-Options is selected ADXRibbonCommand can not distinguish whether the user only changes calculation mode or unloads the domain. As a consequence the OnSendMessage event handler tries to invoke the application options dialog while domain is being unloaded. This results in a proper crash.


Andrei, do you have any idea why all the ADXRibbonCommands fire 3 instaed of 2 times?


Kind regards
Oliver
Posted 08 Oct, 2015 08:59:53 Top
OliverM


Guest


As a result of some more research I need to correct my former post.

I thought I can trick the SendMessage event handler and added a lousy hack

_adxCommandCount = _adxCommandCount + 1;
if (_adxCommandCount == 2)
{
  _adxCommandCount = 0;
  return;
}


Ok, that worked and the third event was simply ignored. Nevertheless Excel crashed next time I unchecked my AddIn in the COM AddIns dialog like usual. Spending some time scratching my head I found out, that getting a DomainUnloadException is only the result of a prior ThreadAbortedException (happens while process trys to invoke the command in the second loop. Hence I wrapped the the call in a try-catch block and handled the exception like so.


_cancelAction = false;
try
{
       commandBarsObj.GetType()
          .InvokeMember("ExecuteMso", System.Reflection.BindingFlags.InvokeMethod, null, commandBarsObj,
              new object[] { "ApplicationOptionsDialog" });
       CheckCalculationMode();
}
catch (ThreadAbortException exc)
{
       Thread.ResetAbort();
}
finally
{
       _cancelAction = true;
}


That did not help at all, maybe its simply to late to cancel the abort request at this point? I am meahnwile quite pessimistic that this issue can be solved without replacing the adxRibbonCommand which is used to get notified when the Excel options dialog opens.
Posted 08 Oct, 2015 12:20:16 Top
OliverM


Guest


Update:

As it turned out using an adxRibbonCommand object to capture the opening of the Excel options dialog is a real show stopper in case user unloads the AddIn rather than changing the calculation mode.
But I did not want to loose this nice feature, hence I thought about alternative ways. Here is what I can come up with.

1. Use Win32API SetWindowsHookEx -> in the callback function check HCBT_DESTROYWND param for class name "NUIDialog" -> if true, check window caption -> if caption equals "Excel Options" check whether calculation mode has changed

2. Use a background worker

3. Use the Alamo line pattern (timer)


Solution 1 needs a bunch of API calls and I am still not sure whether it will always work as class NUIDialog seems to be a base type which is also used when working with charts (format axis etc). Moreover I am afraid some language settings might use a differnt caption for the Excel options dialog.

Using a timer often looks like if the developer couldn't do better or simply was lazy.

Luckily I have a background worker running which is most of the time bored and in idle state. I decided to add a new task to it and make him check the calculation mode. Of course on could remove all other adxRibbonCommand objects (used to handle ribbon click events) but I prefer getting informed right away rather than being notified with a possible delay.


If anybody is interested in the Win32 SetWindowsHookEx example implementation just let me know and I will post it.

If anybody can contribute with a complete new approach how to handle the issue - you're welcome!
Posted 09 Oct, 2015 13:31:24 Top
Andrei Smolin


Add-in Express team


Posts: 18829
Joined: 2006-05-11
Hello Oliver,

May I ask you what you need to achieve?

I've tested the add-in and found that it produces an exception in Excel 2016 and Excel 2013 (build 15.0.4753) when getting ExcelApp.Calculation in AddinInitialize if Excel shows the Start screen at startup. I've modified the code by wrapping that call in a try/catch block and added an event handler of the WorkbookActivate event in which I also get the calculation mode.

Still, these are minor changes as they don't add any new functionality to the add-in.

I wonder why would you need to intercept the domain-related events.


Andrei Smolin
Add-in Express Team Leader
Posted 13 Oct, 2015 04:48:52 Top
OliverM


Guest


Hi Andrei,

As it turned out I do not need to intercept these events but in my initial posting I thought I have to.
As my AddIn crashed on COM dialog -> AddIn unload but not on normal application close, I assumed the issue occurs because my clean up code is not correctly organized to cover the use case domain unload.

Then it turned the reason is the moment InvokeMember tries to execute the call the thread is already aborted.
Hence the only solution is to find a new way to get notified the Excel options dialog has been opened.

With my current solution I can load/unload the addin without any trouble.

You mention
..and found that it produces an exception in Excel 2016 and Excel 2013 (build 15.0.4753) when getting ExcelApp.Calculation in AddinInitialize if Excel shows the Start screen at startup.


I am using Excel 2013 (build 15.0.4753) as well and can not reproduce the issue. What I encountered was an error when getting the calculation mode in the AddInStartUpComplete method if Excel was started opening a saved workbook. But I will follow your advice and wrap it like you did. I also thought about using the WorkbookActivate/SheetActivate event to get the calculation mode but I prefer getting notified about a chance with as little delay as possible.
Posted 13 Oct, 2015 09:01:30 Top
Andrei Smolin


Add-in Express team


Posts: 18829
Joined: 2006-05-11
Thank you for the explanation.

OliverM writes:
I am using Excel 2013 (build 15.0.4753) as well and can not reproduce the issue.


Did you check starting Excel so that it show the Start screen?

OliverM writes:
I also thought about using the WorkbookActivate/SheetActivate event to get the calculation mode


I have to do so in order to get the CalculationMode when you select to create a new workbook on the Start screen. Failing to do so in this moment leaves currentCalculationMode == null because WorkbookOpen doesn't occur in this scenario.

In your scenario (opening an existing workbook), WorkbookOpen occurs before the WorkbookActivate event, so you are right: WorkbookActivate is a bit late for you.


Andrei Smolin
Add-in Express Team Leader
Posted 13 Oct, 2015 10:09:06 Top