Hi Terry,
Doing "GetDebugCalcEnginesAsync().GetAwaiter().GetResult();" will result in a synchronous, blocking call. This is not what you want running on the main thread (either in a ribbon callback, Excel event callback or inside QueueAsMacro.
The general pattern is that you have to quickly return from the ribbon callback or event. You cannot wait or block inside the ribbon callback, waiting for the slow async call(s) to complete first, and then return the result. You have to return something immediately (maybe some kind of empty menu or a label that indicates 'BUSY'). The only thing you can do is to start the async task (running on a different thread, typically a threadpool thread). Then, when the async task completes, you can call back into Excel to do some work based on the result. This call back to Excel will either call the ribbon, with 'Invalidate' or 'InvalidateControl' or talk to Excel through the COM object model. In both cases you should call Excel on the main thread only - so you can use QueueAsMacro from the worker thread, in order to get the code running on the main thread.
There are a few ways to do both the threadpool work, and the continuation after the work completes. But you have to have an awareness of what thread is running (call System.Threading.Thread.CurrentThread.ManagedThreadId - this will be 1 for the main thread, and something else for a threadpool thread).
In my example code earlier in the discussion, I handle a button click on the ribbon to start the async work, and when the async work is complete, I call QueueAsMacro to call Invalidate on the ribbon from the main thread. This Invalidate call causes the ribbon 'get' callback to run again, and this time I have a new result to respond with, thus updating the ribbon. One could do something similar with the Task.ContinueWith method, to schedule some work after the Task is completed. I used the async / await mechanism.
Now you might understand the problem with this code:
var task = Task.Run( () => GetDebugCalcEnginesAsync() );
var debugFiles = task.GetAwaiter().GetResult();
The second line will not return until the task is complete (that's what GetAwaiter().GetResult() does). When this runs, Excel is locked up (since the main thread is waiting for the task to complete). If anything tries to talk to Excel, or show a message box, or even call QueueAsMacro, it can't happen and you might get a deadlocked situation.
To answer some earlier questions you had:
1. Is Task.Run(()=>MethodAsync()) preferred over MethodAsync().GetAwaiter().GetResult()?
These are completely different - the one starts a task running on a threadpool thread, and the other blocks on the current thread until the task is complete, turning the async task into a synchronous call. You cannot use the second pattern in any of the callback situations.
2. "and add Debug.Assert checks if you have any doubt" ... is there some sort of easy pattern to implement to do this? Not really sure what/where I'm asserting.
You can call
Debug.Assert(System.Threading.Thread.CurrentThread.ManagedThreadId == 1);
near any code that will call back to Excel, either using the COM object model (Application, Range, etc) or using the ribbon interfaces (IRibbon.Invalidate / InvalidateControl).
Conversely, you can check that code that you expect to run on a threadpool thread is not running on the main thread using a similar check, with ManagedThreadId != 1.
-Govert