Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

Wouldn't it be cool if you could write something like this in your winform apps? :

[RunInUIThread]
protected virtual void DoSomeUIStuff()
{
this.Text = "hey"
}

Download the binary files from here. (here are the source files)

-----------------

Update:  Hammet notes that such a facility exists in Castle, also here's a thread about it. I feel that it is too verbose and not lightwhegith enough for me, and not as easily extensible as my version, but it's good to know that such a thing already exists!

---------------------------

Inspired by Ayende's talk at devTeach where he showed using Castle Windsor, I decided to take a closer look at how to implement a UI-Thread-Safe solution for winforms, based on his talk.

With a little help from Oren, it took about 5 minutes to implement the basics using the wonderful framework known as DynamicProxy2, part of the Castle project.

During the past couple of days I've refactored this into a more general framework for adding custom attributes which intercept method calls on any object, and allow you to easily create your own attributes with such interception abilities.

The only catches are that for this to work

  • the methods you intercept should be marked as virtual.
  • the objects which contain these methods should not be created directly, but through a special factory class which generates a proxy that does the hard interception work.

Here's how simple it is to implement a UI-Thread safe method in a Winform application:

  1. Download the binary files from here. (here are the source files)
  2. unzip the 3 dll files (osherove.simpleinterception, Castle.Core and Castle.DynamicProxy2) into a folder
  3. Create a new winform project and add a reference only to osherove.SimpleInterception.dll.
  4. Add a method named "virtual void whatever()" to Form1 class that looks like this with the new RunInUIThread attribute from the dll you just referenced:

[RunInUIThread]
protected virtual void Whatever()
{
this.Text = "hey"
}

  1. Add a button to the form and in its event handler start a new thread which calls the whatever() method like this:

protected virtual void button1_Click(object sender, EventArgs e)
{
Thread test = new Thread(new ThreadStart(Whatever));
test.Start();
}

 

  1. Change the code in the Main() method under program.cs to create the form using the special factory:

Form f = AOPFactory.Create<Form1>();
Application.Run(f);

 

That's it! The Whatever() method will always run in the UI thread, no matter what thread calls it.

 

Here's how easy it is to add your own intercepting Attribute:

Simply derive from AOPAttributeBase:

 

[AttributeUsage(AttributeTargets.Method)]
public class CustomLoggingAttribute:AOPAttributeBase
{
   protected override void InvokeBefore(AOPInvocation invocation)
   {
     Console.WriteLine("before method {0}...", invocation.Method.Name);
   }

   protected override void InvokeAfter(AOPInvocation invocation)
   {
    Console.WriteLine("after method {0}...", invocation.Method.Name);
   }
}

Download the binary files from here. (here are the source files)

Published Saturday, May 19, 2007 4:16 AM by RoyOsherove
Filed under: , , ,

Comments

Saturday, May 19, 2007 11:16 AM by Wendy

# re: Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

During the past couple of days? You mean at some point between Ayende's talk and staying up until 4am and doing something like traveling on Friday... well I guess thats a couple of days :) I really enjoyed this part of Ayende's talk and love where you took it. Very sweet!
Saturday, May 19, 2007 3:01 PM by jayson knight

# re: Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

Interesting you should post this as I've been scratching my head thinking there must be an easier way than:

if (this.InvokeRequired)

this.Invoke(new MethodInvoker(delegate()

{

  doSomeUIWork();

}));

else

{

  doSomeUIWork();

}

for every single UI updating method in a form.

Have you done any performance benchmarks?

Saturday, May 19, 2007 4:10 PM by RoyOsherove

# re: Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

Jason: No Benchmarks, but then, when you're updating your UI from a background thread, that's hardly a perf-issue.

I am doing some caching on the proxy side of things to make sure that I don't re-get all attributes of a method on every call to it, but only on the first call.

basically that means that the "GetCustomAttributes() method is called once per virtual method on a form, which should definitely save some cycles.

It also ignores the "WndProc" loop totally.

I have not used this yet on a real application, but after a bit of smoke testing it seems to be ok (that is - no smoke is coming out of my machine when I test it!)

Roy.

Saturday, May 19, 2007 9:24 PM by Chris

# re: Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

That's a good idea - but maybe more flexible to refactor to a method that accepts a delegate (by which you can supply an anonymous one)? Chris
Sunday, May 20, 2007 2:02 PM by hammett

# re: Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

I _think_ we have a facility that do that (not sure if Ayende mentioned it). Craig Neuwirt has developed it. http://svn.castleproject.org:8080/svn/castle/trunk/Facilities/Synchronize/
Sunday, May 20, 2007 3:17 PM by RoyOsherove

# re: Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

hammet: no, he didn't mention it.

Looking at the code, it certainly looks like it will do the job!

Cool.

I still prefer using my way because it allows very easy out of the ox extensiblity with intercepting attributes with just one single class.

Thanks for the update!

Friday, July 20, 2007 1:29 PM by Paul

# re: Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

Can't test this thing in NUnit because it hangs on the Invoke call.. But works when launched as a normal winform. Tried not using forms at all at first and just instantiating a button and checking invokerequired from another thread but it always returned false. Any ideas why?

       [Test]

       public void TestNormalFormCrossThreadedProblem() //this simulates what happens on a winform app when functionality is called in a different thread

       {

           TestSetup();

           //create and launch form which is set to do a cross threaded operation for testing our AOP method of easily getting around the cross threaded UI issue

           AOP_TestForm form = new AOP_TestForm(AOP_TestForm.LoadTypeEnum.Bad);

           form.InvokeRequiredEvent += new AOP_TestForm.InvokeRequiredEventType(form_InvokeRequiredEvent);

           form.Show();

           InvokeRequiredWait.WaitOne();

           form.Dispose();

           Assert.IsTrue(InvokeRequired);

       }

       [Test]

       [Ignore]

       //this hangs for some reason in a unit test but if you uncomment the program class in the test form, change the project type to winform and launch you can see it work properly

       //not sure why, but for some reason the Invoke whether called through AOP or not hangs when done through the unit test

       public void TestFormCrossThreadedProblemFixAOP() //this simulates what happens on a winform app when functionality is called in a different thread

       {

           TestSetup();

           //create and launch form which is set to do a cross threaded operation for testing our AOP method of easily getting around the cross threaded UI issue

           AOP_TestForm form = Maverick.Shared.AOP.AOPFactory.Create<AOP_TestForm>();

           form.InvokeRequiredEvent += new AOP_TestForm.InvokeRequiredEventType(form_InvokeRequiredEvent);

           form.Show();

           InvokeRequiredWait.WaitOne();

           form.Dispose();

           Assert.IsFalse(InvokeRequired);

       }

Friday, August 03, 2007 10:43 AM by Israel A

# RunInUIThread

Friday, August 03, 2007 2:53 PM by Paulo Morgado

# re: Easier Winform UI Thread Safe Methods with DynamicProxy2 and Osherove.SimpleInterception

A few years ago I built a framework in .NET 1.1 to be used by former web programmers (with no previous knoledge of threading or Windows Forms) that had a class Event with a Fire method that took the delegate to fire and, if not null, iterated though the invocation list (Delegate.GetInvocationList()) and, if the Delegate.Target was of a type implementing System.ComponentModel.ISynchronizeInvoke Interface, it would call Traget.Invoke; otherwise Delegate.DynamicInvoke was used.

Couldn't the same be used here?

Wednesday, March 26, 2008 12:47 PM by Jacob Lewallen

# Announcing MultiFunctionMachineMachine

I&#39;m very pleased to formally announce a project that Aaron and I started a few months ago and have