Luka Horvat
Luka Horvat

Reputation: 4402

Finding the correct thread to invoke a method without failing

I have this somewhat convoluted situation. I'm given an object o, a string n and array of objects args. I am to call the method n on the object o with the given arguments.

The problem is that this input always comes to the same thread, and the objects provided are (sometimes) controls created on different threads. So when I try to invoke certain methods on them I get the cross-thread invocation exception.

It wouldn't be a problem if I could just check if the object is a control and then call it's Invoke method and do my stuff inside of that call. Unfortunately, I can't do that because I could for example be passed a ControlCollection object that belongs to some control. If I call it's Add method, I get the exception, but the ControlCollection has no Invoke method.

What can I do about this? Performance isn't something I'm worried about yet so any solution is a good one. Maybe if I could catch the exception and get the thread it want's me to call from, then repeat the invocation? Or maybe there's some way to get the thread an object "belongs" to?

Upvotes: 2

Views: 91

Answers (1)

Enigmativity
Enigmativity

Reputation: 117057

Could you do it this way?

Action action = () => o.GetType().GetMethod(n).Invoke(o, args);

if (o is Control)
{
    var c = o as Control;
    c.Invoke(action);
}
else if (o is ControlCollection)
{
    var c = (o as ControlCollection).Owner;
    c.Invoke(action);
}
else
{
    action();
}

One approach to generalize this would be to do it this way:

        Action action = () => o.GetType().GetMethod(n).Invoke(o, args);

        var controlMaps = new Func<object, Control>[]
        {
            x => x as Control,
            x => o is ControlCollection ? (o as ControlCollection).Owner : null,
        };

        var c = controlMaps
                .Select(m => m(o))
                .Where(x => x != null)
                .FirstOrDefault();

        if (c != null)
        {
            c.Invoke(action);
        }
        else
        {
            action();
        }

Then, if you have a bunch of different object type you can create a mapping in the controlMaps array.

Upvotes: 3

Related Questions