Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to find a control's owner thread?

I am having some threading issues with a large app I am working on (getting cross-thread exceptions). Is there a way to find the thread name/id that a particular control was created on?

The error occurs when I try to add a new control to my control's control collection. I can't really create a small, reproducible sample so I will describe it as best as I can.

I have a main control that sits on a form, call it _mainControl. In its constructor I instantiate an instance of another control, something like

ChildControl _childControl = new ChildControl();

Now _childControl exists but I do not add it to _mainControls collection yet.

Eventually, _mainControl receives an event notification that I should add the control. In the event handler I check if this.InvokeRequired and if it is, I Invoke the handler, something like the following:

AddControlEventHander(...)
{
    if(InvokeRequired)
    {
        BeginInvoke(new MethodInvoker(AddControlEventHander);
        return;
    }
    Controls.Add(_childControl);
}

The exception is always thrown at Controls.Add ("Cross-thread operation not valid: Control '_item' accessed from a thread other than the thread it was created on").

Now, what I don't understand is how this is possible. I created _childControl on the same thread that _mainControl was created on. When I look at the threads window while I am debugging, the current thread name/id is the same when I call Control.Add as it was when the _childControl was added. However, the thing that confuses me the most are the following calls from _mainControl:

InvokeReuqired == false;
_childControl.InvokeRequired == false;
_childControl._item.InvokeRequired == true; //I made _item public just to try this and it returns true!

How is that possible? Is it possible for _childControl to be created on one thread while its children are created somehow on another? All of _childControl's children are created during initialization, as is normally done.

If anyone has any tips/suggestions as to what might be going on please let me know.

Thanks.

Edit:

In case anyone is interested, I found out what was happening. I was curious as to how a control can be created on one thread and it's children created on a another thread even though the InitializeComponent was all done on the same thread. So, I found out which thread the child was being created on using code similar to what Charles suggested below. Once I knew that, I at least knew which thread to focus on. Then I broke on the OnHandleCreated event of the child control and found the issue.

One thing I didn't know was that the handle of a control is created when the control is first made visible, not when it is created. So a thread that didn't own the control was trying to set it's visibility to true. So I added a check to see if InvokeRequired and thought that would do the trick. However, something I really didn't expect is that calling InvokeRequired will create the handle of the control if it is not created yet! This in effect causes the control to be created on the wrong thread and always return false for InvokeRequired. I got around this by touching the Handle property of the control so that it is created before InvokeRequired is called.

Thanks for the help guys :)

like image 657
Flack Avatar asked Oct 27 '09 23:10

Flack


1 Answers

To get the owner thread for a control, try this:

private Thread GetControlOwnerThread(Control ctrl)
{
    if (ctrl.InvokeRequired)
        ctrl.BeginInvoke(
            new Action<Control>(GetControlOwnerThread),
            new object[] {ctrl});
    else
        return System.Threading.Thread.CurrentThread;
}

Can child controls be on a different thread from the parent (container control)? Yes, it all depends on what thread was running when the control was constructed (new'ed up)

You always have to check InvokeRequired... Because you never know what thread might be calling into the method you are coding... Whether you need to check InvokeRequired separately for each child control, depends on how sure you are that all the controls were created on the same thread or not. If all controls are created when the form is created, in the same initialization routine, then you are probably safe to assume they were all created on the same thread

like image 70
Charles Bretana Avatar answered Oct 18 '22 01:10

Charles Bretana