Hooked events Outlook VSTO continuing job on main Thread - c#

I have developed an Outlook VSTO addin. Some tasks should be made on a background thread. Typically, checking something in my local db or invoking a web request. After reading several posts, I dropped the idea of calling the Outlook Object Model (OOM) in a background thread.
I have some wpf controls and I successfully managed to use the .NET 40 TPL to perform the async task and when completed to "finish" the job (i.e. accessing the UI or the OOM) in the Main VSTA Thread.
To do so I use a syntax of the form:
Task<SomeResult> task = Task.Factory.StartNew(()=>{
//Do long tasks that have nothing to do with UI or OOM
return SomeResult();
});
//now I need to access the OOM
task.ContinueWith((Task<SomeResult> tsk) =>{
//Do something clever using SomeResult that uses the OOM
},TaskScheduler.FromCurrentSynchronizationContext());
So far so good. But now I want to do something similar when hooking an event in the OOM where there are no Form/WPF control. Precisely, my problem comes from the fact that TaskScheduler.FromCurrentSynchronizationContext() throws an exception.
For instance,
Items inboxItems = ...;
inboxItems.ItemAdd += AddNewInboxItems;
private void AddNewInboxItems(object item)
{
Task<SomeResult> task = Task.Factory.StartNew(()=>{
//Do long tasks that have nothing to do with OOM
return SomeResult()});
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
/* Ouch TaskScheduler.FromCurrentSynchronizationContext() throws an InvalidOperationException, 'The current SynchronizationContext may not be used as a TaskScheduler.' */
task.ContinueWith((Task<SomeResult> tsk) =>{
//Do something clever using SomeResult that uses the OOM
}),scheduler};
}
/* Ouch TaskScheduler.FromCurrentSynchronizationContext() throws an InvalidOperationException, 'The current SynchronizationContext may not be used as a TaskScheduler.' */
Note that I tried to create a TaskScheduler in addin initialization and putting it in a singleton as suggested here. But it does not work, the continuation task is not performed in the desired VSTA Main thread but another one (inspected with VisualStudio).
Any idea ?

There is known bug that SynchronizationContext.Current might be null in several places where it should not (including office add-ins). That bug was fixed in .NET 4.5. But since you cannot upgrade to .NET 4.5, you have to find a workaround. As a suggestion, try to do:
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
when initializing your addon.

You can use the SynchronizationContext class which provides the basic functionality for propagating a synchronization context in various synchronization models. The Post method dispatches an asynchronous message to a synchronization context, i.e. the Post method starts an asynchronous request to post a message. See Using SynchronizationContext for sending events back to the UI for WinForms or WPF for more information and sample code.
FYI The Current property allows to get the synchronization context for the current thread. This property is useful for propagating a synchronization context from one thread to another.

Related

Why would await Task.Run() not return to the same thread?

I'm running into a situation that's making me think I don't understand async / await mechanics as well as I thought.
I've got a Windows desktop app which is mostly WPF but uses a WinForms host to show a 3rd party COM object. The process of loading the COM object is pretty slow, so I've been trying to move the creation and initialization of those objects to a task to free up the UI while that work happens, but I'm finding that in some situations when the await Task.Run() returns, it's not on the UI thread. As a result, when I change the Visible property on the WinForms host after the task returns it throws because of a cross-thread call.
The calling function looks like this:
public async Task<bool> LoadPreview(string filePath)
{
bool result;
try
{
await _semaphore.WaitAsync();
result = await Task.Run(() => CreateAndInitializePreviewer(filePath));
if (result)
{
Visible = false; // <-- occasionally crashes because I'm not on the UI thread
_currentHandler.DoPreview();
Visible = true;
}
}
finally
{
_semaphore.Release();
}
return result;
}
The code inside CreateAndInitializePreviewer does not have any async / await calls. I've verified that before the call to Task.Run() I'm always on the UI thread.
Any suggestions on what I should be looking for that would cause the await Task.Run() to come back to a different thread? Any ideas are appreciated.
The process of loading the COM object is pretty slow, so I've been trying to move the creation and initialization of those objects to a task to free up the UI while that work happens
This is probably not going to work, unless your COM object is free-threaded (which is unlikely). If you want to push that work off your UI thread, you'll probably need to create a separate STA thread to hold the COM object and marshal calls to/from that thread - meaning all calls to that COM object, since it would live in the other STA thread. It's fairly straightforward in WPF to create a second UI (STA) thread; it's quite a bit harder but still possible in WinForms.
Any suggestions on what I should be looking for that would cause the await Task.Run() to come back to a different thread?
Yes. await will capture SynchronizationContext.Current if it is not null, and it should not be null in this case. It should be either an instance of DispatcherSynchronizationContext (which continues executing by sending a message to the WPF dispatcher) or WinFormsSynchronizationContext (which continues executing by sending a message to the WinForms winproc).
My initial guess is that there's something odd going on with SynchronizationContext.Current due to the WinForms-in-WPF architecture.

ExcelDna: Async: The calling thread must be STA

I'm working with ExcelDna and async functions. If there's an exception in the async:d code I want to show a fancy WPF error window. My problem is that I'm getting the error "The calling thread must be STA, because many UI components require this." How can I solve this?
[ExcelFunction(Description = "", Category = "")]
public static async Task<object> /*string*/ Foo(CancellationToken ct)
{
try
{
return await Task.Run(async () =>
{
await Task.Delay(1000, ct);
throw new Exception("BOO");
return "HelloWorld";
}, ct2.Token);
}
catch (Exception e)
{
return ShowWpfErrorWindowThatRequiresSTA(e);
}
}
When your Excel function runs there is no SynchronizationContext.Current installed, so the async/await mechanism will runs the code after await (including your catch handler) on a ThreadPool thread. That's not a context where you can directly show your WPF form.
Installing a DispatcherSynchronizationContext corresponding to a Dispatcher running on the main thread (or another thread) would work, but you have to do that for every UDF call. Somehow the native code path through Excel loses the .NET call context on the main thread, so the SynchronizationContext gets lost.
Better is probably to assume that the catch handler is running on a ThreadPool thread, and make a SynchronizationContext.Post call from the catch handler to take you back to the main thread running your Dispatcher and WPF form.
You can look at how Excel-DNA implements the (WinForms) LogDisplay window. (https://github.com/Excel-DNA/ExcelDna/blob/master/Source/ExcelDna.Integration/LogDisplay.cs). You can call LogDisplay.WriteLine(...) from any thread, and it will do a _syncContext.Post to run the 'Show' on the main thread.
The C# async/await mechanism works less well with Excel since the native/managed transitions, and whatever Excel does internally, messes up the thread context that needs to flow between continuations. Even on the .NET side, it's not clear how thread context is managed between AppDomains (different Excel add-ins). So it's best not to rely on the .NET runtime being able to thread any kind of context through the managed/native transitions.
Many Office plugins have a problem where SynchronizationContext.Current is null, and asynchronous continuations execute on the thread pool. I'd check the value of SynchronizationContext.Current before the first await.
I have had some success in creating a WinFormsSynchronizationContext and installing that on the thread before the first await. However, installing a WPF context would be more complex.

DoEvents vs anything else --> for long COM operation

I have a WPF program where my model need to load a "Out-of-Proc" (.exe) COM component in order to achieve some validations when a user make an action on the UI. I'd like to inform the user that a long action will take place to let him know that the application is busy, not just frozen. But any action on the UI does occur after the COM action is completed.
I think that any COM communication should be done on the main UI thread. It eliminates any solution that would run on another thread than the main (UI) thread.
I tried many options without success:
MSDN Dispatcher.PushFrame (DoEvents)
StackOverflow HCL (PushFrame)
Jason programming Blog (PushFrame)
I can't see how I can achieve a synchronous action from a model where I would need to refresh the UI.
My action has a property "IsLoading" that I subscribe from my view and where I try to update the UI according to its state but it seems that is not possible in WPF ???
Any other suggestions ?
Can I use async/await and do my COM action from another thread running another dispatcher (kind of complex) and will loose required synchronicity (user need results of COM operation to continue its work) ?
Mainly for Blindy...
Some clearer explications (more details about required synchronicity):
When a user click on a TreeView item, I load a grid then need to verify that data entered in the grid is still valid. To do validation, I need to load an application through COM and automate it to load a document, then parse it and verify the data in the grid (in the Model of the Grid in the view). That takes 10 seconds.
If I do that on another thread, then user can do an action to select to add a new row in the grid which still depends on the same COM application loaded with the previous document. I still need to wait for the application to load. It is a synchronous action. My application depends on that COM application with its loaded document to be in valid state for user to take more actions. But I need to give user some feedback on what I’m doing (start COM app and load on document). Doing COM action on another thread just report the problem later but do not solve the fact that user need to wait that the action would complete. I think I need to (force) update my WPF app but can’t find any (twisted) way to do it.
[UPDATE] As the OP has updated the question and specified he's using out-of-proc COM objects, the custom STA thread plumbing described below doesn't make sense. Now, a simple await Task.Run(() => { /* call the out-of-proc COM */}) is enough to keep the UI responsive. Kudos to #acelent for clarifying this point.
Recently I answered a related question: StaTaskScheduler and STA thread message pumping.
The solution was to create and use STA COM objects on a dedicated background STA thread which provides both message pumping and thread affinity for those COM objects.
I'd like to show how ThreadWithAffinityContext can be used in your case, with async/await:
dynamic _comObject = null;
ThreadWithAffinityContext _staThread = null;
// Start the long-running task
Task NewCommandHandlerAsync()
{
// create the ThreadWithAffinityContext if haven't done this yet
if (_staThread == null)
_staThread = new ThreadWithAffinityContext(
staThread: true,
pumpMessages: true);
// create the COM Object if haven't done this yet
if (_comObject == null)
{
await _staThread.Run(() =>
{
// _comObject will live on a dedicated STA thread,
// run by ThreadWithAffinityContext
_comObject = new ComObject();
}, CancellationToken.None);
}
// use the COM object
await _staThread.Run(() =>
{
// run a lengthy process
_comObject.DoWork();
}, CancellationToken.None);
}
// keep track of pending NewCommandHandlerAsync
Task _newCommandHandler = null;
// handle a WPF command
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
try
{
// avoid re-entrancy (i.e., running two NewCommandHandlerAsync in parallel)
if (_newCommandHandler != null)
throw new InvalidOperationException("One NewCommandHandlerAsync at a time!");
try
{
await _newCommandHandler = NewCommandHandlerAsync();
}
finally
{
_newCommandHandler = null;
}
}
catch (Exception ex)
{
// handle all exceptions possibly thrown inside "async void" method
MessageBox.Show(ex.Message);
}
}
The fact that we have offloaded the lengthy process _comObject.DoWork() to a separate thread doesn't automatically solve the other common UI-related problem:
How to handle the UI when the lengthy background operation is pending?
There is a number of options. E.g., you can disable the UI elements which fire NewCommand_Executed event, to avoid re-entrancy, and enable another UI element to allow the user to cancel the pending work (a Stop button). You should also provide some progress feedback, if your COM object supports that.
Alternatively, you can display a modal dialog before staring the long-running task and hide it when the task has completed. As far as the UI usability goes, modality is less desirable, but it's very easy to implement (example).
You can create and use COM objects on any thread, the marshaller will take care of running it on a background thread if your application uses the STA threading model. There's no need to funnel every call through the main UI thread.
As to your last question in comments, since you'll be running this on a background thread, you of course need to synchronize like usual with lock and Invoke the result back on the main thread when done. This is nothing specially related to COM, it's just how Windows threading works.
In short, stop using the UI thread for heavy duty, non-UI related work.
I used this once for WPF so as to force the screen to re-Paint :
I used auto-translation from VB so I hope it's correct
private Action EmptyDelegate = () => { };
[System.Runtime.CompilerServices.Extension()]
public void Refresh(UIElement uiElement)
{
uiElement.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, EmptyDelegate);
}

RaiseCanExecuteChanged called while await causes deadlock

I am using WPF and DelegateCommand from PRISM and have the following problem:
I start an async operation like:
public async void ProgramDevice()
{
var result = await FirmwareLoader.DownloadFirmwareAsync();
}
Inside this method an event is fired which I registered to and should update my DelegateCommand so it can't be executed:
//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged +=
(s, e) => Dispatcher.Invoke(() => UiCommand.RaiseCanExecuteChanged());
Now I have the problem, that the RaiseCanExecuteChanged causes a deadlock (I checked and the Dispatcher.Invoke does not cause it, because when I e.g. show a MessageBox instead it works fine).
Am I doing something wrong or how can I work around this problem?
I see you've already solved your problem, but I thought I'd give a more general solution that will help you prevent such deadlocks in the future.
In your case, you could easily avoid this deadlock by using ConfigureAwait like this:
var result = await FirmwareLoader.DownloadFirmwareAsync().ConfigureAwait(false);
What this does is allows the continuation to be performed on a different thread than the original. Doing so is not always possible, since a lot of times you need the continuation to be performed on the UI thread, but for this question I don't believe that's the case. So basically, the best practice is to always use ConfigureAwait(false) unless you need to resume execution from the original thread.
This article explains in detail why these kind of deadlocks happen and how to avoid them. Another recommended read is Best Practices in Asynchronous Programming.
Found the problem:
It was not the RaiseCanExecuteChanged, but the actual CanExecute which is triggered by it. In there I had an AsyncLock which waited for the programming task to be finished, before returning the value I use to descide if UiCommand can be executed --> deadlock as the programming task triggered it...
I solved it by simple using the "sync" property (which does not use the lock and just returns the current value/stat) of the value I need.
Am I doing something wrong or how can I work around this problem?
Method Dispatcher.Invoke blocks working thread until UI thread makes all updates
UI thread uses some resources locked by working thread (through RaiseCanExecuteChanged -> CanExecute method chain in the above code) and blocks
Deadlock since worker thread waits for UI thread to finish update and UI thread waits worker thread to release locked resources
A possible way to ensure no deadlocks is to asynchronously invoke updates on UI thread using Dispatcher.BeginInvoke.
//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged +=
(s, e) => Dispatcher.BeginInvoke(() => UiCommand.RaiseCanExecuteChanged());
This way UI thread will wait for a moment when working thread releases locked resources and then will update. But there will be no deadlock.

How do I make my application wait for my UI to refresh?

Without using extra threads I would simply like to display a "Loading" label or something similar to the user when a large amount of data is being read or written. If I however attempt to modify any UI elements before calling the IO method, the application freezes for a while and then displays the "Loading" message after all the work is already done. This obviously doesn't help. How can I ensure that any UI changes are applied and visible before calling the IO method?
DataSet ds = STT_Import.ImportExcelToDataSet(filePath);
bool result = false;
if (ds != null)
{
int cellCount = ds.GetTotalCellCount();
if (Popup.ShowMessage(string.Format("Your file contains {0} cells. Inserting data will take approximately {1} seconds. Do you want to continue?",
cellCount, CalculateTime(cellCount)), "Confirm", MessageType.Confirm) == MessageBoxResult.Yes)
{
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
// Do actual work after user has been notified:
result = DB.StoreItems(_currentType, ds);
}
}
I tried looking for answers but couldn't find anything that answered my specific question, so I'm sorry if the question has been asked before.
When working with WPF, you can use the Dispatcher to queue commands on the UI thread at different DispatcherPriorities
This will allow you to queue your long-running process on the UI thread after everything in the DispatcherPriority.Render or DispatcherPriority.Loaded queues have occurred.
For example, your code may look like this:
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
// Do actual work after user has been notified:
Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(delegate() {
var result = DB.StoreItems(_currentType, ds); // Do Work
if (result)
StatusLabel.Content = "Finished";
else
StatusLabel.Content = "An error has occured";
}));
It should be noted though that its usually considered bad design to lock up an application while something is running.
A better solution would be to run the long-running process on a background thread, and simply disable your application form while it runs. There are many ways of doing this, but my personal preference is using the Task Parallel Library for it's simplicity.
As an example, your code to use a background thread would look something like this:
using System.Threading.Tasks;
...
// Tell user the application is working:
StatusLabel.Content = "Writing to database...";
MyWindow.IsEnabled = False;
// Do actual work after user has been notified:
Task.Factory.StartNew(() => DB.StoreItems(_currentType, ds))
// This runs after background thread is finished executing
.ContinueWith((e) =>
{
var isSuccessful = e.Result;
if (isSuccessful)
StatusLabel.Content = "Finished";
else
StatusLabel.Content = "An error has occured";
MyWindow.Enabled = true;
});
You are trying to solve the problem in the wrong manner. What you should be doing here is run the time-consuming task in a worker thread; this way, your UI will remain responsive and the current question will become moot.
There are several ways you can offload the task to a worker thread; among the most convenient are using the thread pool and asynchronous programming.
It is provably impossible to keep your UI responsive without utilizing additional threads unless your database provides an asynchronous version of the method you're using. If it does provide an asynchronous version of the method then you simply need to use that. (Keep in mind that async does not mean that it's using any other threads. It's entirely possible to create an asynchronous method that never uses additional threads, and that's exactly what's done with most network IO methods.) The specifics of how to go about doing that will depends on the type of DB framework you're using, and how you're using it.
If your DB framework does not provide async methods then the only way to keep the UI responsive is to perform the long running operation(s) in a non-UI thread.
The Approach you are using is not efficient way so I would suggest to go with Async Programing or threading
Async programming:
Visual Studio 2012 introduces a simplified approach, async programming, that leverages asynchronous support in the .NET Framework 4.5 and the Windows Runtime. The compiler does the difficult work that the developer used to do, and your application retains a logical structure that resembles synchronous code. As a result, you get all the advantages of asynchronous programming with a fraction of the effort. Support .Net framework 4.5
It will save your time to implementing System .Threading and very efficient for the task same as your where we have to wait for some operation
http://msdn.microsoft.com/en-ca/library/vstudio/hh191443.aspx
http://go.microsoft.com/fwlink/?LinkID=261549
or
Threading:
The advantage of threading is the ability to create applications that use more than one thread of execution. For example, a process can have a user interface thread that manages interactions with the user and worker threads that perform other tasks while the user interface thread waits for user input.Support .Net fremework 4.0 or Older
http://msdn.microsoft.com/en-us/library/aa645740%28v=vs.71%29.aspx
If you don't want the UI to be responsive I use a busy indicator.
There are prettier cursors - this is an in house application.
using (new WaitCursor())
{
// very long task
Search.ExecuteSearch(enumSrchType.NextPage);
}
public class WaitCursor : IDisposable
{
private Cursor _previousCursor;
public WaitCursor()
{
_previousCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
}
#region IDisposable Members
public void Dispose()
{
Mouse.OverrideCursor = _previousCursor;
}
#endregion
}

Categories

Resources