locked
How to marshall call from work thread to UI thread to access Outlook Object Model? RRS feed

  • Question

  • Hi,

    I have a managed addin for OL2010. In a work thread, I need to marshall some code execution (access OOM) to the main UI thread.

    I tried to use SynchronizationContext, but it appears no available.

    What are the ways to do that in the managed envirionment?

     

    Thanks

    jhou

     

    Thursday, January 26, 2012 5:59 PM

Answers

  • The synchronization context for the thread shouldn't be null after Outlook is running with a UI and the message pump has been primed. I have seen instances where that takes a while after startup however. But the thread is always there as long as Outlook is running, so the context can't be null after it's established.
     
    If you bother with creating a windows form you can use that for the context if you create the form in the main thread, and make sure it doesn't get garbage collected. When I do that I create a form and add a singleton instance of it in OnConnection() or a VSTO Startup(). The form object variable is declared in my Connect or ThisAddin class at class level. That form will be running in the main thread.
     
    I can then put delegates for procedures in the form, or actual procedures, and when calling them check for InvokeRequired, and if so I call Invoke() to invoke the method. That then executes in the main thread.
     
    In many ways that's a simpler approach than synching contexts. Any System.Windows.Forms.Timer objects placed on the form will run in the main thread too, which can be an advantage. When the timer fires you can then start a background thread and as long as you don't use the Outlook object model the thread can do pretty much anything.

    --
    Ken Slovak
    MVP - Outlook
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007
     
     
    "jhou" <=?utf-8?B?amhvdQ==?=> wrote in message news:9d2b13bf-f21c-4fe1-81cc-a59e3a8ae558...

    Hi Ken,

     

    Thank you very much for your help.

    Following is my solution/findings:

    1. Create a winform in my addin.

    2. Catch the SynchronizationContext right away after instantiating the form. The process of instantiating a winform creates a WindowFormsSynchronizationContext and assigns  to WindowSystem.Threading.SynchronizationContext.Current. This was the only reliable way for me to catach the SynchronizationContext.

    3. The WindowSystem.Threading.SynchronizationContext.Current is nulled by Outlook or whoever after the form was instantiated in my environment. I can occasionally catch the synchronizationContext in explorer's SelectionChange or Activate event handler, but not reliable.

    4. My impression is that if you do not create a winform, the SynchronizationContext may not available at all.


    Ken Slovak MVP - Outlook
    • Marked as answer by jhou Friday, January 27, 2012 7:08 PM
    Friday, January 27, 2012 6:18 PM

All replies

  • Why do you say that the SynchronizationContext isn't available?
     
    The context may not be available if you try to get it before any Outlook UI is initialized, or if the Windows message pump hasn't been primed. I usually try to get the context in handlers for Inspectors.NewInspector(), Explorer.SelectionChange() and Explorer.Activate(). On startup the SelectionChange() event will usually fire when the UI has been initialized.
     
    In those event handlers I call System.Windows.Forms.Application.DoEvents() to prime the message pump, then I check for the context and store it. Then I synch using the stored context when necessary.
     
    Another way to handle thread synchronization is to instantiate a Windows.Form in the addin startup code and to keep the form hidden and alive with a global or class level variable. You can design the form to have one or more Windows.Forms.Timer objects, which will fire in the main process thread. Code in the form can then check for InvokeRequired as a failsafe to make sure any calls made will always be executed on the main thread.

    --
    Ken Slovak
    MVP - Outlook
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007
     
     
    "jhou" <=?utf-8?B?amhvdQ==?=> wrote in message news:5c5eeff3-408b-4430-bb27-9aea5a0503f9...

    Hi,

    I have a managed addin for OL2010. In a work thread, I need to marshall some code execution (access OOM) to the main UI thread.

    I tried to use SynchronizationContext, but it appears no available.

    What are the ways to do that in the managed envirionment?

     

    Thanks

    jhou

     


    Ken Slovak MVP - Outlook
    Thursday, January 26, 2012 6:09 PM
  • Thanks for the quick response.

    I have tried the following code in the explorer's SelectionChange event handler. But the current synchronization context is still null;

    What was the problem here?

     

    Thanks again

     

    ==========

    public

     

    void

    OnSelectionChange()

    {

    System.Windows.Forms.Application.DoEvents();

    System.Threading.

     

    SynchronizationContext ct =System.Threading.SynchronizationContext

    .Current;

    Thursday, January 26, 2012 6:57 PM
  • The code looks like it should be OK. I use something very similar, but I call my procedure from all the event handlers I mentioned, did you only try SelectionChange()?
     
    A belt and suspenders approach might be to enable a forms timer after Application.MAPILogonComplete() fires and check for the context in that timer handler, as well as in the other event handlers.

    internal void SetThreadContext()

    {

        try

        {

            if (_syncContext == null)

            {

                System.Windows.Forms.Application.DoEvents();

                if (System.Threading.SynchronizationContext.Current != null)

                {

                    _syncContext = System.Threading.SynchronizationContext.Current;

                }

            }

        }

        catch (Exception ex)

        {

            Logger.LogException(ex, ex.Message);

        }

    }


    --
    Ken Slovak
    MVP - Outlook
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007
     
     
    "jhou" <=?utf-8?B?amhvdQ==?=> wrote in message news:6d9e4ccb-e782-4738-97ad-a6d97788a1ae...

    Thanks for the quick response.

    I have tried the following code in the explorer's SelectionChange event handler. But the current synchronization context is still null;

    What was the problem here?

     

    Thanks again

     

    ==========

    public

     

    void

    OnSelectionChange()

    {

    System.Windows.Forms.Application.DoEvents();

    System.Threading.

     

    SynchronizationContext ct =System.Threading.SynchronizationContext

    .Current;


    Ken Slovak MVP - Outlook
    Thursday, January 26, 2012 7:07 PM
  • Hi Ken,

     

    Thank you very much for your help.

    Following is my solution/findings:

    1. Create a winform in my addin.

    2. Catch the SynchronizationContext right away after instantiating the form. The process of instantiating a winform creates a WindowFormsSynchronizationContext and assigns  to WindowSystem.Threading.SynchronizationContext.Current. This was the only reliable way for me to catach the SynchronizationContext.

    3. The WindowSystem.Threading.SynchronizationContext.Current is nulled by Outlook or whoever after the form was instantiated in my environment. I can occasionally catch the synchronizationContext in explorer's SelectionChange or Activate event handler, but not reliable.

    4. My impression is that if you do not create a winform, the SynchronizationContext may not available at all.

    Friday, January 27, 2012 4:19 PM
  • The synchronization context for the thread shouldn't be null after Outlook is running with a UI and the message pump has been primed. I have seen instances where that takes a while after startup however. But the thread is always there as long as Outlook is running, so the context can't be null after it's established.
     
    If you bother with creating a windows form you can use that for the context if you create the form in the main thread, and make sure it doesn't get garbage collected. When I do that I create a form and add a singleton instance of it in OnConnection() or a VSTO Startup(). The form object variable is declared in my Connect or ThisAddin class at class level. That form will be running in the main thread.
     
    I can then put delegates for procedures in the form, or actual procedures, and when calling them check for InvokeRequired, and if so I call Invoke() to invoke the method. That then executes in the main thread.
     
    In many ways that's a simpler approach than synching contexts. Any System.Windows.Forms.Timer objects placed on the form will run in the main thread too, which can be an advantage. When the timer fires you can then start a background thread and as long as you don't use the Outlook object model the thread can do pretty much anything.

    --
    Ken Slovak
    MVP - Outlook
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007
     
     
    "jhou" <=?utf-8?B?amhvdQ==?=> wrote in message news:9d2b13bf-f21c-4fe1-81cc-a59e3a8ae558...

    Hi Ken,

     

    Thank you very much for your help.

    Following is my solution/findings:

    1. Create a winform in my addin.

    2. Catch the SynchronizationContext right away after instantiating the form. The process of instantiating a winform creates a WindowFormsSynchronizationContext and assigns  to WindowSystem.Threading.SynchronizationContext.Current. This was the only reliable way for me to catach the SynchronizationContext.

    3. The WindowSystem.Threading.SynchronizationContext.Current is nulled by Outlook or whoever after the form was instantiated in my environment. I can occasionally catch the synchronizationContext in explorer's SelectionChange or Activate event handler, but not reliable.

    4. My impression is that if you do not create a winform, the SynchronizationContext may not available at all.


    Ken Slovak MVP - Outlook
    • Marked as answer by jhou Friday, January 27, 2012 7:08 PM
    Friday, January 27, 2012 6:18 PM