Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / New Users / December 2004

Tip: Looking for answers? Try searching our database.

WinForms UI Thread Safety (Controls) & Events

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Andrew - 06 Dec 2004 14:09 GMT
I'm quite sure this is the case, but I'd like to consult to be sure:

If I have a UI which subscribes to a bunch of events which any of a number
of threads could trigger... and the UI's event handlers will update that UI's
controls, then the control updates _are_ done on the UI thread... correct?

Thanks,

Andrew
Timofey Kazakov - 06 Dec 2004 14:50 GMT
Hello, "Andrew"

> I'm quite sure this is the case, but I'd like to consult to be sure:
>
> If I have a UI which subscribes to a bunch of events which any of a number
> of threads could trigger... and the UI's event handlers will update that UI's
> controls, then the control updates _are_ done on the UI thread... correct?

All control updates must be done in UI thread. So if you have event handler that could invoked from another thread - you need to switch to UI thread through Control.Invoke method.

As another case you can create RealProxy wrapper and all method of MarshalByRefObject will be invoked on UI thread transparently:

class Program : MarshalByRefObject
{
   static Program instance;

   void Test()
   {
       Console.WriteLine(Thread.CurrentThread.Name);
   }

   static void Main(string[] args)
   {
       Thread.CurrentThread.Name = "Main Thread";

       //all method's called through instance whil be done on main thread.
       instance = (Program) ApartmentProxy.Marshal(new Program());
       
       Thread thread = new Thread(new ThreadStart(ThreadStart));
       thread.Start();

       //this sample based on APC (asynchronous procedure call), so we need periodically to call Thread.Sleep(0) on working thread
       Thread.Sleep(1000);
   }

   //Thread method
   static void ThreadStart()
   {
       Thread.CurrentThread.Name = "Child Thread";

       Console.WriteLine(Thread.CurrentThread.Name);
       Console.WriteLine("Calling instance.Test");

       // this method will be invoked on main thread
       instance.Test();
   }
}

//ApartmentProxy implementation
public class ApartmentProxy : RealProxy
{
   delegate void ApcHandler(IntPtr param);        

   [DllImport("kernel32.dll")]
   extern static IntPtr GetCurrentThreadId();

   [DllImport("kernel32.dll")]
   extern static bool QueueUserAPC(ApcHandler hadler, IntPtr thread, IntPtr param);

   [DllImport("kernel32.dll")]
   extern static IntPtr GetCurrentThread();

   [DllImport("kernel32.dll")]
   extern static IntPtr GetCurrentProcess();

   [DllImport("kernel32.dll")]
   extern static bool CloseHandle(IntPtr handle);

   [DllImport("kernel32.dll")]
   extern static bool DuplicateHandle(IntPtr sourceProcessHandle,
       IntPtr sourceHandle, IntPtr targetProcessHandle,    
       out IntPtr targetHandle, uint desiredAccess, bool inheritHandle,
       uint dwOptions);

   private class ApartmentMessage
   {    
       private ApartmentProxy _proxy;
       private IMessage _source;
       private IMessage _result;

       private ApartmentMessage(ApartmentProxy proxy, IMessage source) {
           _proxy = proxy;
           _source = source;
       }

       private static void UserApcHandler(IntPtr param)
       {
           GCHandle messageHandle = (GCHandle)param;
           ApartmentMessage This = (ApartmentMessage)messageHandle.Target;
           try {
               IMethodCallMessage mcm = This._source as IMethodCallMessage;

               if (mcm != null) {
                   This._result = RemotingServices.ExecuteMessage(This._proxy.GetUnwrappedServer(), mcm);
               }
               else {
                   This._result = This._proxy.InitializeServerObject((IConstructionCallMessage) This._source);    
               }
           }
           finally {
               This._proxy._event.Set();
           }
       }

       public static IMessage ExecuteAppratmentMessage(ApartmentProxy proxy, IMessage msg)
       {
           ApartmentMessage appMessage = new ApartmentMessage(proxy, msg);

           GCHandle messageHandle = GCHandle.Alloc(appMessage);
           ApcHandler apcHandler = new ApcHandler(UserApcHandler);
           
           bool bResult = QueueUserAPC(apcHandler, proxy._ownedThread, (IntPtr)messageHandle);
           proxy._event.WaitOne();                

           GC.KeepAlive(apcHandler);
           messageHandle.Free();

           return appMessage._result;
       }
   }

   internal IntPtr _ownedThread;
   internal AutoResetEvent _event;

   private ApartmentProxy(Type serverType)
       : base (serverType)
   {
       IntPtr currentThread = GetCurrentThread();
       IntPtr currentProcess = GetCurrentProcess();
       DuplicateHandle(currentProcess, currentThread, currentProcess, out _ownedThread, 0, false, 2);
       
       _event = new AutoResetEvent(false);
   }

   private ApartmentProxy(MarshalByRefObject mbr)
       : this (mbr.GetType())
   {
       AttachServer(mbr);
   }

   ~ApartmentProxy()
   {
       if (_ownedThread != IntPtr.Zero) {
           CloseHandle(_ownedThread);
       }
   }

   public override IMessage Invoke(IMessage msg)
   {
       IConstructionCallMessage ccm = msg as IConstructionCallMessage;
       if (ccm != null) {
           RealProxy.SetStubData(this, GetCurrentThreadId());
           return InitializeServerObject(ccm);
       }
       lock (typeof(ApartmentMessage))
       {
           return ApartmentMessage.ExecuteAppratmentMessage(this, msg);
       }
   }

   public static MarshalByRefObject Marshal(MarshalByRefObject obj)
   {
       return (MarshalByRefObject) new ApartmentProxy(obj).GetTransparentProxy();
   }
}
Jon Skeet [C# MVP] - 06 Dec 2004 16:20 GMT
> I'm quite sure this is the case, but I'd like to consult to be sure:
>
> If I have a UI which subscribes to a bunch of events which any of a number
> of threads could trigger... and the UI's event handlers will update that UI's
> controls, then the control updates _are_ done on the UI thread... correct?

No - events naturally occur in the thread which raises them, not the
thread which subscribes to them. You need to make sure you only access
the UI in the UI thread.

See http://www.pobox.com/~skeet/csharp/threads/winforms.shtml for more
information.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too


Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.