Incorrect behavior of BackgroundWorker in Office solutions

Answered 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 PM
    Moderator
     
     Answered

    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

     

     

     

  • 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.