Incorrect behavior of BackgroundWorker in Office solutions
-
Monday, June 14, 2010 12:18 PM
Hello,
I am developing an Excel solution where I need to do some background processing each time a sheet is modified.
My requirement is that the result of the background operation should be processed in the Excel UI (main) Thread.
I am trying use the BackgroundWorker class to execute this operation since the specification of this class is exactly what I am looking for.
However, my test shows that the RunWorkerCompleted event is not raised in the UI thread (which is the case when BackgroundWorker is used in a Windows Forms context). I have found some articles in this forum mentioning this behavior, including workarounds, but I have not seen any official statement that this is either a bug or an expected behavior of BackgroundWorker when it is used in a VSTO solution.
I could workaround this defect by using Control.Invoke to call my code. However, this is exactly what the BackgroundWorker is designed to do so I am looking for an explanation ...
This is the minimal code sample that you can add to an Excel Workbook cusomization in the code-behind file of a sheet:
private BackgroundWorker _worker;
private void OnSheetChange(Microsoft.Office.Interop.Excel.Range Target) {
if (_worker == null) {
TraceOperation("OnSheetChange (start worker)");
_worker = new BackgroundWorker();
_worker.DoWork += OnDoWork;
_worker.RunWorkerCompleted += OnRunWorkerCompleted;
_worker.RunWorkerAsync();
}
}
private static void OnDoWork(object sender, DoWorkEventArgs e) {
TraceOperation("Do Work");
Thread.Sleep(2000);
}
private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
TraceOperation("RunWorkerCompleted");
_worker = null;
}
private static void TraceOperation(string operation) {
Trace.WriteLine(operation + " - Thread #" + Thread.CurrentThread.ManagedThreadId);
}
My question is: does BackroundWorker rely on a different SynchronizationContext when it is used within VSTO applications ? Is it an expected behavior or a bug ? Is there a workaround other than using Control.Invoke (in my case, I may not have a realized control available when I need to process the result in the UI Thread).
Thanks a lot for your help
--
Cyril
All Replies
-
Monday, June 21, 2010 10:38 AM
Hello,
Would anyone care to reply to this thread ? Reproducing the behavior that I describe should be very straightforward.
I am going to cross post this question to the "Inovate On Office" forum ...
Regards
-- Cyril
-
Wednesday, June 30, 2010 10:16 PMModerator
Hi Grimshu,
I worked on the issue and it is one of the known issues with VSTO. To workaround the issue, Before using a BackgroundWorker (e.g. before you call RunWorkerAsync on a BackgroundWorker) you need to call SetSynchronizationContext like this:
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());Here is the code that I tried and it resolves the issue.
TraceOperation("OnSheetChange (start worker)");
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
_worker = new BackgroundWorker();
_worker.DoWork += OnDoWork;
_worker.RunWorkerCompleted += OnRunWorkerCompleted;
_worker.RunWorkerAsync();
Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
Regards,
Sidharth Shah
Microsoft Online Community Support
- Proposed As Answer by Sidharth Sh - MSFTMicrosoft Employee, Moderator Thursday, July 01, 2010 12:12 PM
- Marked As Answer by Sidharth Sh - MSFTMicrosoft Employee, Moderator Monday, July 05, 2010 5:00 PM
-
Friday, March 30, 2012 8:30 AM
HI Grimshu and Sidharth,
I had the same problem in VSTA code inside a thirdy party application.
The workaround suggested by Sidharth does solve the problem.
Just a clarifiation: it is important to invoke
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
before calling the
_worker.RunWorkerAsync();
it doesn't matter to invoke it at construction time of BackgroundWorker.
Regards.
-
Saturday, November 24, 2012 11:21 PM
Hi,
this information from Sidhath is essential for understanding how to get C# 5.0 async, using Task, Task<T>, Task.Delay() and await working within a VSTO add in.
I just couldn't work out why my async control flow was being done on threads with different ManagedThreadId values. Setting the SynchronizationContext gets everything working asynchronously on the same thread - a great starting point. Maybe this should be the default?
First time your user invokes a modal dialog in the Microsoft Word UI, your VSTO add in async code will likely fail with a RPC_E_SERVERCALL_RETRYLATER exception unless you are on the same thread - as per Sidhath's code - but even better if you can avoid a background thread at all by using short lived asynchronous operations, as made pretty easy in C# 5.0.
Hopefully my combination of keywords in this reply will help someone else - I've been surfing some combination of them for ages until I found this thread.
Regards,
George.

