none
Thread pool thread hangs on updating closed ribbon RRS feed

  • Question

  • Hi,

    In the addin in Outlook 2010 I use the Threadpool to do some tasks and all of them are by the event mechanism connected to the ribbon to show the status as the button ScreenTip using following function
    Ribbon is created by xml.

    public void UpdateBtnSyncCaption(string msg)
            {

                lock (_synclock)
                {
                    if (ThisAddIn.ApplicationClosed || ribbon == null) return;
                    try
                    {
                            _btnSyncScreenTip = msg;
                            ribbon.InvalidateControl("btnSync");
                      }
                    catch (Exception)
                    {
                        //ignore
                    }
                }

            }

    the method calling the status change is

     public event Action<string> SyncStatusChanged;
    

    private void SetStatus(string msg)

            {
                if (SyncStatusChanged != null && !ThisAddIn .ApplicationClosed )
                    SyncStatusChanged(msg);
            }

    in the ThisAddIn I have ExplorerClose event handler which (as the first statement) sets the static property ApplicationClosed to true.

     _explorer = App.ActiveExplorer();
     ((OL.ExplorerEvents_10_Event)_explorer).Close += OnApplicationClose;
    
            

    void OnApplicationClose()         {             ApplicationClosed = true;                 syncSession.CancelSync();                   //wait for current background workerBackgroundWorker worker = syncSession.Worker;                 if (worker != null)                 {                     while (worker.IsBusy)                     {                     };                 }             }

    }

                    


    Then it cancels all the processes (running threads). But some of them probably already reported their status and despite of there is the 
    ApplicationClosed flag check (and ApplicationClosed has been set to true), it proceeds and hangs on the ribbon.InvalidateControl("btnSync");   (this control is probably already destroyed). Try catch doesn't work.
    So the thread remains frozen here and my application can not end.

    The problem maybe could be that the ApplicationCLose is thrown when the applicatiopn is closed and not before, isn't it? 

    Any idea what could be wrong and how to solve it?

    Thanks a lot

    Filip

              
    Tuesday, September 25, 2012 9:02 AM

Answers

  • Hmm, maybe I found the solution and it is following:  

    in This AddIn I have

    Dispatcher = Dispatcher.CurrentDispatcher;
    
    public static Dispatcher  Dispatcher { getset; }
    

    and I set the status using:

    ThisAddIn.Dispatcher.Invoke(new Action(() => SyncStatusChanged(msg)));

    this raise the event from the main thread. It seems it works, but more testing is needed.

    F.

     

    Tuesday, September 25, 2012 3:38 PM
  • I usually grab the thread context of the addin (which is Outlook's thread context) once the Outlook UI has been initialized and the Windows message pump has been primed. I store that and use a SendOrPost() callback to call a delegate procedure that uses the main thread context as its target, so the code runs on the main thread.
     
    Another approach I use sometime is to instantiate a hidden Windows Form during addin startup and put calls there in the form. I can then check if an Invoke is required and call procedures in the form, which is running on the main UI thread.

    --
    Ken Slovak
    [MVP-Outlook]
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007
    "Filip Mateasko" <=?utf-8?B?RmlsaXAgTWF0ZWFza28=?=> wrote in message news:300f4eb9-cdaa-4d3e-abc0-9ded880fb10f...
    Hmm, maybe I found the solution and it is following:  

    in This AddIn I have

    Dispatcher = Dispatcher.CurrentDispatcher;
    
    public static Dispatcher  Dispatcher { getset; }
    

    and I set the status using:

    ThisAddIn.Dispatcher.Invoke(new Action(() => SyncStatusChanged(msg)));

    this raise the event from the main thread. It seems it works, but more testing is needed.

    F.

     


    Ken Slovak MVP - Outlook
    Tuesday, September 25, 2012 6:29 PM
    Moderator

All replies

  • The problem is you're using background threading where it's not supported at all.
     
    Don't use background threading for that, none of the Office applications support using their object models from background threads, they are all single-threaded interfaces.

    --
    Ken Slovak
    [MVP-Outlook]
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007
    "Filip Mateasko" <=?utf-8?B?RmlsaXAgTWF0ZWFza28=?=> wrote in message news:cf3c6bf3-6c5a-4166-95ad-2732275a8b97...

    Hi,

    In the addin in Outlook 2010 I use the Threadpool to do some tasks and all of them are by the event mechanism connected to the ribbon to show the status as the button ScreenTip using following function
    Ribbon is created by xml.

    public void UpdateBtnSyncCaption(string msg)
            {

                lock (_synclock)
                {
                    if (ThisAddIn.ApplicationClosed || ribbon == null) return;
                    try
                    {
                            _btnSyncScreenTip = msg;
                            ribbon.InvalidateControl("btnSync");
                      }
                    catch (Exception)
                    {
                        //ignore
                    }
                }

            }

    the method calling the status change is

     public event Action<string> SyncStatusChanged;
    

    private void SetStatus(string msg)

            {
                if (SyncStatusChanged != null && !ThisAddIn .ApplicationClosed )
                    SyncStatusChanged(msg);
            }

    in the ThisAddIn I have ExplorerClose event handler which (as the first statement) sets the static property ApplicationClosed to true.

     _explorer = App.ActiveExplorer();
     ((OL.ExplorerEvents_10_Event)_explorer).Close += OnApplicationClose;
    
            

    void OnApplicationClose()         {             ApplicationClosed = true;                 syncSession.CancelSync();                   //wait for current background workerBackgroundWorker worker = syncSession.Worker;                 if (worker != null)                 {                     while (worker.IsBusy)                     {                     };                 }             }

    }

                    


    Then it cancels all the processes (running threads). But some of them probably already reported their status and despite of there is the 
    ApplicationClosed flag check (and ApplicationClosed has been set to true), it proceeds and hangs on the ribbon.InvalidateControl("btnSync");   (this control is probably already destroyed). Try catch doesn't work.
    So the thread remains frozen here and my application can not end.

    The problem maybe could be that the ApplicationCLose is thrown when the applicatiopn is closed and not before, isn't it? 

    Any idea what could be wrong and how to solve it?

    Thanks a lot

    Filip

              

    Ken Slovak MVP - Outlook
    Tuesday, September 25, 2012 3:01 PM
    Moderator
  • HI Ken,

    thanks for your reply, but I use threads to do some processing outside of the OOM (downloading msg files from server). After some big troubles with OOM I rewrote my app to use almost only Redemption now, but sometimes it's impossible, like here. The only problem is the Ribbon invalidation. So you think it's not possible to use it at all ? I've thought, that if the threads raise events and ribbon is attached to them by handler, then it's not a direct call and it's ok, isn't it?

    Any idea how to notify user by whatever different way, but certainly without any messageboxes or other dialogs ? 

    Thanks

    Filip


    Tuesday, September 25, 2012 3:09 PM
  • Hmm, maybe I found the solution and it is following:  

    in This AddIn I have

    Dispatcher = Dispatcher.CurrentDispatcher;
    
    public static Dispatcher  Dispatcher { getset; }
    

    and I set the status using:

    ThisAddIn.Dispatcher.Invoke(new Action(() => SyncStatusChanged(msg)));

    this raise the event from the main thread. It seems it works, but more testing is needed.

    F.

     

    Tuesday, September 25, 2012 3:38 PM
  • I usually grab the thread context of the addin (which is Outlook's thread context) once the Outlook UI has been initialized and the Windows message pump has been primed. I store that and use a SendOrPost() callback to call a delegate procedure that uses the main thread context as its target, so the code runs on the main thread.
     
    Another approach I use sometime is to instantiate a hidden Windows Form during addin startup and put calls there in the form. I can then check if an Invoke is required and call procedures in the form, which is running on the main UI thread.

    --
    Ken Slovak
    [MVP-Outlook]
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007
    "Filip Mateasko" <=?utf-8?B?RmlsaXAgTWF0ZWFza28=?=> wrote in message news:300f4eb9-cdaa-4d3e-abc0-9ded880fb10f...
    Hmm, maybe I found the solution and it is following:  

    in This AddIn I have

    Dispatcher = Dispatcher.CurrentDispatcher;
    
    public static Dispatcher  Dispatcher { getset; }
    

    and I set the status using:

    ThisAddIn.Dispatcher.Invoke(new Action(() => SyncStatusChanged(msg)));

    this raise the event from the main thread. It seems it works, but more testing is needed.

    F.

     


    Ken Slovak MVP - Outlook
    Tuesday, September 25, 2012 6:29 PM
    Moderator
  • Ken, how do you grab the Add-in thread context? Could you provide some sample code please ? My solution still seems to work, but I'd like to know the other way and I can not figure it out..

    Thanks

    F.



    Tuesday, October 2, 2012 11:46 AM
  • Well,

    unfortunatelly my solution isn't 100% - now I have the same situation, so please Ken, some sample code for your solution. I'd like to try it.

    Thanks

    F.

    Tuesday, October 2, 2012 1:20 PM
  • Well, as I said, you need to grab the thread context when the Windows message pump has been primed and the Outlook user interace is active after startup.
     
    Also, because you're synching to the main Outlook thread your code calling the thread context will run in that context and block the UI thread while it's running. So I don't use this method for very long-running processes.
     
    So what I do is use a number of different event handlers and have code in each to grab the context if it's not already set. I use Explorer.SelectionChange(), Explorer.Activate(), Inspectors.NewInspector() and a couple of other handlers with code like this:
     
    // at class level, I usually have this in my Connect class

    private static System.Threading.SynchronizationContext _syncContext;

    // example from an event handler, in this case NewInspector()

    if (_syncContext == null)

    {

        SetThreadContext();

    }

    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);

        }

    }

     

    To call into the thread context I'd have code like this in a procedure:

    object[] args = new object[1];

    args[0] = true; // however many parameters you need with whatever values you need

    System.Threading.SendOrPostCallback callback = new System.Threading.SendOrPostCallback(ThreadCallProcedure);

    if (callback != null)

    {

        System.Threading.SynchronizationContext context = Connect.SyncContext;

        if (context != null)

        {

            context.Send(callback, args);

        }

    }

    // delegate procedure

    private void ThreadCallProcedure(Object state)

    {

        object[] args = (object[])state;

        // parse and use the arguments passed

        // now call a procedure to run in the main thread, whatever you call will now run in that thread

        // for example:

        bool myArg = (bool)args[0];

        DoSomethingInMainThread(myArg);

    }

    --
    Ken Slovak
    [MVP-Outlook]
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007
    "Filip Mateasko" <=?utf-8?B?RmlsaXAgTWF0ZWFza28=?=> wrote in message news:b22079d7-e611-4d85-b3b8-257fe5c87e14...

    Ken, how do you grab the Add-in thread context? Could you provide some sample code please ? My solution still seems to work, but I'd like to know the other way and I can not figure it out..

    Thanks

    F.




    Ken Slovak MVP - Outlook
    Tuesday, October 2, 2012 2:51 PM
    Moderator