Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / C#

Synchronize Invoke Events

Rate me:
Please Sign up or sign in to vote.
2.00/5 (2 votes)
15 Apr 2009CPOL1 min read 17.8K   142   10   2
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.

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Syrian Arab Republic Syrian Arab Republic
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
General[My vote of 2] Fair article Pin
BillW3314-Oct-10 4:37
professionalBillW3314-Oct-10 4:37 
GeneralMy vote of 2 Pin
jpmik15-Apr-09 12:45
jpmik15-Apr-09 12:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.