Geeks With Blogs
Brian Biales because blogging is just the easiest way to remember things

I discovered an interesting issue while decorating an EventTopic subscription in my ModuleController for one of my modules.  The decoration was like this:

[EventSubscription(EventTopicNames.MessageArrived, ThreadOption.UserInterface)]
public void OnMsgReceived(object sender, EventArgs<Msg> ev)

The event is fired on a non-UI thread, and I disovered this event handler was being called on that thread, NOT on the UserInterface thread as specified in the decoration.  This decoration works fine in a View's Presenter class.  But in my ModuleController class, it doesn't.

Well, I've looked into this further, and posted my findings at the CodePlex discussion group
http://www.codeplex.com/smartclient/Thread/View.aspx?ThreadId=8956
but I'll include my findings here as well:

When I subscribe to the event explicitly like this, my event handler does indeed get called on the UI thread:

        public override void Run()
        {
            AddServices();
            ExtendMenu();
            ExtendToolStrip();
            AddViews();
            // Explicitly subscribe to this event topic
            WorkItem.EventTopics{EventTopicNames.MessageArrived].AddSubscription(
                this, "OnMsgReceived", WorkItem, ThreadOption.UserInterface);
        }

 

But when I decorate my event handler like the following, (in ModuleController.cs) this event handler gets called actually on the same thread on which it was fired (which is NOT the UI thread).

        [EventSubscription(EventTopicNames.MessageArrived, ThreadOption.UserInterface)]
        public void OnMsgReceived(object sender, EventArgs<Msg> ev)
        {
            ...
        }

 

The relevant code I found while debugging (which I could only do when I explicitly subscribe to the event, although someone who knows how could do this by stepping through the CAB code that handles the decorations...)

If SynchronizationContext.Current is null at the time the subscription is being added, then the subscription’s syncContext property will NOT get set, and I believe this is likely the ultimate issue.  You can see below the code snippet at subscription time.  Below that is a code snippet at Fire execution time, and it clearly will simply call the event handler directly on the current thread if the syncContext property was not set at subscription time.

 

At Subscription time:
In EventBroker\Subscription.cs Ln 93

         if (threadOption == ThreadOption.UserInterface)
         {
                // If there's a syncronization context (i.e. the WindowsFormsSynchronizationContext 
                // created to marshal back to the thread where a control was initially created 
                // in a particular thread), capture it to marshal back to it through the 
                // context, that basically goes through a Post/Send.
                if (SynchronizationContext.Current != null)
                {
                      syncContext = SynchronizationContext.Current;
                }
         }

At Fire time:
In EventBroker\Subscription.cs Ln 215

    private void CallOnUserInterface(object sender, EventArgs e, List<Exception> exceptions)
    {
          Delegate handler = CreateSubscriptionDelegate();
          if (handler != null)
          {
                if (syncContext != null)
                {
                      syncContext.Send(delegate(object data)
                      {
                            try
                            {
                                  ((Delegate)data).DynamicInvoke(sender, e);
                            }
                            catch (TargetInvocationException ex)
                            {
                                  exceptions.Add(ex.InnerException);
                            }
                      }, handler);
                }
                else
                {
                      try
                      {
                            handler.DynamicInvoke(sender, e);
                      }
                      catch (TargetInvocationException ex)
                      {
                            exceptions.Add(ex.InnerException);
                      }
                }
          }
    }

Posted on Thursday, April 12, 2007 12:15 PM .NET , SCSF - Smart Client Software Factory , C# | Back to top


Comments on this post: SCSF/CAB EventSubscription decoration's ThreadOption.UserInterface does not always work

# re: SCSF/CAB EventSubscription decoration's ThreadOption.UserInterface does not always work
Requesting Gravatar...
Good catch!
Left by Spartaco on May 13, 2009 1:41 PM

# re: SCSF/CAB EventSubscription decoration's ThreadOption.UserInterface does not always work
Requesting Gravatar...
Thank you - saved me from lots of trouble!
Left by Jens on Jan 10, 2017 8:31 AM

Your comment:
 (will show your gravatar)


Copyright © Brian Biales | Powered by: GeeksWithBlogs.net