Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Synchronize Invoke Events

0.00/5 (No votes)
15 Apr 2009 1  
Safely calling a control's method from a different thread.

Introduction

Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread.

Background

Windows Forms uses the single-threaded apartment (STA) model because Windows Forms is based on native Win32 windows that are inherently apartment-threaded. The STA model implies that a window can be created on any thread, but it cannot switch threads once created, and all function calls to it must occur on its creation thread. Outside Windows Forms, classes in the .NET Framework use the free threading model.

The STA model requires that any method on a control that needs to be called from outside the control's creation thread must be marshaled to (executed on) the control's creation thread. The base class Control provides several methods (Invoke, BeginInvoke, and EndInvoke) for this purpose. Invoke makes synchronous method calls, and BeginInvoke makes asynchronous method calls.

Using the code

Use the property 'InvokeRequired' to indicate whether the caller must call an invoke method when making method calls to the control because the caller is on a different thread than the one the control was created on.

public static class Utility
{
    public static void SynchronizeInvokeEvent(object sender, 
                       Delegate TargetEvent, EventArgs e)
    {
        if (TargetEvent != null)
        {
            Delegate[] InvocationList = TargetEvent.GetInvocationList();
            foreach (Delegate TargetDelegate in InvocationList)
            {
                try
                {
                    System.ComponentModel.ISynchronizeInvoke Target = 
                           TargetDelegate.Target 
                           as System.ComponentModel.ISynchronizeInvoke;
                    if (Target != null)
                    {
                        if (Target.InvokeRequired)
                            Target.Invoke(TargetDelegate, new object[] { sender, e });
                        else
                            TargetDelegate.DynamicInvoke(new object[] { sender, e });
                    }
                    else
                    {
                        TargetDelegate.DynamicInvoke(new object[] { sender, e });
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
    }
} 

// You must add references to System.Management.dll
public class ProcessWatcher : IDisposable
{
    public delegate void TaskManagerEventHandler(object sender, 
                         TaskManagerEventArgs e);
    public event TaskManagerEventHandler ProcessCreated;
    private bool m_StartedWatcher;
    private System.Management.ManagementEventWatcher m_CreateProcessWatcher;
    public ProcessWatcher()
    {
        m_StartedWatcher = false;
    }
    public void Dispose()
    {
        StopWatcher();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
    public void StartWatcher()
    {
        if (m_StartedWatcher)
            return;
        m_StartedWatcher = true;
        System.Management.EventWatcherOptions eventOptions = 
           new System.Management.EventWatcherOptions();
        eventOptions.BlockSize = 1;
        eventOptions.Timeout = new TimeSpan(0, 0, 2);
        m_CreateProcessWatcher = new System.Management.ManagementEventWatcher
            (
                "root\\CIMV2",
                "SELECT * FROM __InstanceCreationEvent WITHIN 1 " + 
                "WHERE TargetInstance ISA \"Win32_Process\"",
                eventOptions
            );
        m_CreateProcessWatcher.EventArrived += 
          new System.Management.EventArrivedEventHandler(
          OnCreateProcessWatcher_EventArrived);
        m_CreateProcessWatcher.Start();
    }
    public void StopWatcher()
    {
        if (!m_StartedWatcher)
            return;
        m_StartedWatcher = false;
        m_CreateProcessWatcher.Stop();
        m_CreateProcessWatcher.EventArrived -= 
          new System.Management.EventArrivedEventHandler(
          OnCreateProcessWatcher_EventArrived);
        m_CreateProcessWatcher.Dispose();
        m_CreateProcessWatcher = null;
    }
    void OnCreateProcessWatcher_EventArrived(object sender, 
         System.Management.EventArrivedEventArgs e)
    {
        int ProcessID;
        if (int.TryParse(((System.Management.ManagementBaseObject)
            e.NewEvent["TargetInstance"])["ProcessId"].ToString(), 
            out ProcessID))
        {
            OnCreateProcess(new TaskManagerEventArgs(
              System.Diagnostics.Process.GetProcessById(ProcessID)));
        }
    }
    private void OnCreateProcess(TaskManagerEventArgs e)
    {
        Utility.SynchronizeInvokeEvent(this, ProcessCreated, e);
    }
    public class TaskManagerEventArgs : EventArgs
    {
        private System.Diagnostics.Process m_Process;
        public TaskManagerEventArgs(System.Diagnostics.Process Process)
        {
            m_Process = Process;
        }
        public System.Diagnostics.Process Process
        {
            get { return m_Process; }
        }
    }
} 

public partial class Form1 : Form
{
    private ProcessWatcher m_ProcessWatcher;
    public Form1()
    {
        InitializeComponent();
        m_ProcessWatcher = new ProcessWatcher();
        m_ProcessWatcher.ProcessCreated += new 
          ProcessWatcher.TaskManagerEventHandler(
          m_ProcessWatcher_ProcessCreated);
    }
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        // Dispose ProcessWatcher here or in form disposing method;
        m_ProcessWatcher.Dispose();
        base.OnFormClosing(e);
    }
    private void m_ProcessWatcher_ProcessCreated(object sender, 
                 ProcessWatcher.TaskManagerEventArgs e)
    {
        this.listBox1.Items.Add(e.Process.ProcessName);
    }
    private void m_StartProcessWatcherButton_Click(object sender, EventArgs e)
    {
        m_ProcessWatcher.StartWatcher();
    }
    private void m_StopProcessWatcherButton_Click(object sender, EventArgs e)
    {
        m_ProcessWatcher.StopWatcher();
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here