1

I'm developing a VSTO Outlook add-in in VS2010. In the ThisAddIn_Startup method (which is called when the addin starts) my code needs to check if Outlook is running on the company network or not. If Outlook isn't running on the network it takes about 3 seconds to come back with the answer. So I wrapped the code up in a Task to make it run Async to ensure it doesn't hang Outlook while it's checking.

e.g.

bool onNetwork = false;
Task task = Task.Factory.StartNew(() =>
{
    onNetwork = IsConnectedToNetwork();
});

After it's finished checking it needs to load and display the relevant Form. So I changed the code to:

Task task = Task.Factory.StartNew(() =>
{
    if (IsConnectedToNetwork())
    {
        OnNetworkForm onNetworkForm = new OnNetworkForm();
        onNetworkForm.Show();
    }
    else
    {
        OffNetworkForm offNetworkForm = new OffNetworkForm();
        offNetworkForm.Show();
    }
});

But the Forms need to be loaded on the UI Thread. Otherwise I get an InvalidOptionationException when it tries to load and show the forms with the message:

The calling thread must be STA, because many UI components require this.

My question is how can I make the forms load on the UI Thread?

Please note

I can't use await as that's C# 5.0 and VS 2010 doesn't support C# 5.0. And the ThisAddin class isn't a control and therefore doesn't have the BeginInvoke or Invoke methods available.

leppie
  • 115,091
  • 17
  • 196
  • 297
m_collard
  • 2,008
  • 4
  • 29
  • 51
  • Isn't it possible to use ContinueWith (http://msdn.microsoft.com/en-us/library/vstudio/dd270696) on the first task? – Me.Name May 12 '14 at 12:09
  • Do you know which values I'd need to pass into the ContinueWith method to make it run on the UI thread? Thanks – m_collard May 12 '14 at 12:20
  • 1
    Thank you for the link ClickRick. It says to stores the var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); But I get "The current SynchronizationContext may not be used as a TaskScheduler." error. – m_collard May 12 '14 at 12:48
  • @m_collard Using TaskScheduler.FromCurrentSynchronizationContext as shown in Yuval's answer will probably do the trick. Must admit that I haven't used that approach. Since before TPL I've used AsyncOperationsManager (http://blog.subrosoftware.nl/?p=42 ), and since TPL was introduced, I only altered the extension method to use both – Me.Name May 12 '14 at 12:50
  • It doesn't work unfortunately :( And I've just tried using the AsyncOperationManager the same as in the link you posted. Still get "The calling thread must be STA, because many UI components require this." – m_collard May 12 '14 at 13:03
  • In which thread does your code execute? – Ventsyslav Raikov May 12 '14 at 13:11
  • Aaaah, it seems that the vsto environment doesn't run sta to begin with. In that case invoking on the calling thread is useless. In that case your best bet, is start a new thread to create and show your form, set that thread to STA before running, and show your form as dialog inside the created thread. – Me.Name May 12 '14 at 13:12
  • My code is a VSTO Outlook addin. Without using a Task to make it run async the forms open fine. Therefore the code must be running STA. – m_collard May 12 '14 at 13:15
  • Strange, then the AsyncOperationManager method should work too, as long as it is created in the main thread, not inside the task. Still, I could post an example with a normal thread, to see if it runs? – Me.Name May 12 '14 at 13:18

1 Answers1

5

An example by using an 'old fashioned' thread:

    var task = new Task<bool>(IsConnectedToNetwork);
    task.ContinueWith(res =>
        {
            bool onNetwork = res.Result;
            var thread = new Thread(() =>
            {
                Form frm = onNetwork ? (Form)new OnNetworkForm() : new OffNetworkForm();
                frm.ShowDialog();
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
        });
    task.Start();
Me.Name
  • 12,259
  • 3
  • 31
  • 48
  • Good news and bad news. Using your example makes the forms load. Thank you. But it then tries to add a COM custom pane in Outlook and displays the following error: Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.VisualStudio.Tools.Office.Runtime.Interop.ICustomTaskPaneSite'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{3CA8CD11-274A-41B6-A999-28562DAB3AA2}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)). – m_collard May 12 '14 at 13:56
  • I'm going to display the forms using your example. And try and display the Custom Panes a different way. Thanks for your help – m_collard May 12 '14 at 13:56
  • Glad that works at least and happy to help. Still have the feeling that the AsyncOperationManager could help further, if it is declared on the proper vsto thread. Think I need to start using vsto someday.. – Me.Name May 12 '14 at 14:01