tags:

views:

38

answers:

3
MethodInfo mi = typeof(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance | BindingFlags.NonPublic);
mi.Invoke(notify, null);

This throws the following exception:
{"Exception has been thrown by the target of an invocation."}

With the following inner exception:
"Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."

If I comment out a line of code that sets the images for the context menu entries then it stops throwing the exception.

Any ideas?

A: 

You are updating UI controls on a thread other than the thread that created them. That is not allowed.

In regular code you can use the Control.InvokeRequired property.

Assuming that you have a form with two buttons, and two labels here is how to do updates from another thread:

private void button1_Click(object sender, EventArgs e)
{
  //force execution on another thread
  new Thread(updateLabelThreaded).Start();
}

private void button2_Click(object sender, EventArgs e)
{
  //force execution on another thread
   new Thread(updateLabelReflect).Start();
}

private void updateLabelThreaded()
{
    if (!label1.InvokeRequired)
    {
      //if we are on the correct thread, do a trivial update
       label1.Text = "something";
    }
    else
    {
       //else invoke the same method, on the UI thread
        Invoke(new Action(updateLabelThreaded), null);
    }
}

 private void updateLabelReflect()
 {
     Control ctrl = label2;

     PropertyInfo pi = typeof (Label).GetProperty("InvokeRequired");
     bool shouldInvoke = (bool) pi.GetValue(ctrl, null);
     if (!shouldInvoke)
     {
        //if we are on the correct thread, reflect whatever is neccesary - business as usual
        PropertyInfo txtProp = typeof (Label).GetProperty("Text");
        txtProp.SetValue(ctrl, "Something 2", null);
     }
     else
     {
       //else invoke the same method, on the UI thread
       Invoke(new Action(updateLabelReflect), null);
     }
   }
SWeko
A: 

You may be confusing the Invoke method of MethodInfo, which just invokes the delegate on the current thread, with Control.Invoke which invokes a delegate on the UI thread.

You get this exception if you try to access a UI element from a thread other than the correct UI thread.

Basically you need to execute this code on the UI thread instead.

Is there any reason why you're trying to invoke ShowContextMenu via reflection instead of directly? You may just need something like (assuming C# 3):

MethodInvoker action = () => notify.ShowContextMenu();
someControl.Invoke(action);
Jon Skeet
ShowContextMenu is non-public so it has to be via reflection?It works fine if I don't set the ToolStripItem images.Also, if it is executed prior to the ToolStripItem images being set then it will show the context menu correctly, without throwing an exception, for the duration of the program, even after setting the images.
sqwerty
@sqwerty: If `ShowContextMenu` is non-public, you shouldn't be calling it at all. Calling non-public members via reflection is just asking for trouble - they can change between versions. If you really, really want to do this then just use `Control.Invoke` to do it on the right thread. The exact circumstances of when it fails are irrelevant: you're touching the UI from a non-UI thread. Don't do that.
Jon Skeet
A: 

You can't call UI methods from a non-UI thread. I recommend using TaskScheduler.FromCurrentSynchronizationContext to marshal the call to the UI thread.

Stephen Cleary