Controls created on one thread cannot be parented to a control on a different thread

G

gregbacchus

I am trying to make a window that pops up, to show that my application
is busy. I want it to be able to be called from anywhere, any thread,
so I put it as a static method in a class.

Code listed below.

The problem is, no matter what I can think of doing, I get the
following exception:

"Controls created on one thread cannot be parented to a control on a
different thread"

Trace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller,
Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[]
args)
at UI.Waiter.Busy(Object waitOn)

or i used to get this with a slightly different configuration (same
exception):
Trace:
at System.Windows.Forms.ControlCollection.Add(Control value)
at System.Windows.Forms.ControlCollection.Add(Control value)
at UI.Controls.WaitForm.InitializeComponent()
at UI.Controls.WaitForm..ctor()
at UI.Waiter..cctor()


Anyone got any ideas... or am i going about it the wrong way all
together!?

Cheers
Greg



----- CODE -----

using System;
using System.Collections;
using System.Threading;
using System.Windows.Forms;

namespace UI
{
/// <summary>
/// Summary description for Waiter.
/// </summary>
public sealed class Waiter
{
private static ArrayList _list = new ArrayList();
private static Mutex _lock = new Mutex();
private static Controls.WaitForm _form = null;

private static Form _parent = null;
public static Form Parent { get { return _parent; } set { _parent =
value; } }

private Waiter()
{
}

private delegate void BusyDelegate( object obj );
public static void Busy( object waitOn )
{
if( _parent != null )
_parent.Invoke( new BusyDelegate( InternalBusy ), new object[] {
waitOn } );
}

private static void InternalBusy( object waitOn )
{
// this has to be done here for threading reasons
if( _form == null )
_form = new Controls.WaitForm();

// show the waiting form
_lock.WaitOne();
try
{
_list.Add( waitOn );
if( _list.Count == 1 )
{
_form.Restart();
_form.Show();
System.Windows.Forms.Application.DoEvents();
}
}
finally
{
_lock.ReleaseMutex();
}
}

public static void Ready( object waitOn )
{
_lock.WaitOne();
try
{
_list.Remove( waitOn );
if( _list.Count == 0 )
{
_form.Hide();
}
}
finally
{
_lock.ReleaseMutex();
}
}
}
}
 
G

Guest

A form/control may only be accessed by the thread owning the control. All
calls to a form on another thread must be serialized by calling
Control.Invoke
(http://msdn.microsoft.com/library/d...omponentmodelisynchronizeinvokeclasstopic.asp).
As a result, the parent of a form must live on the same thread as the form
itself. So you will have to change your design a bit. Do you need to set
the parent of your popup form?

Regards, Jakob.


I am trying to make a window that pops up, to show that my application
is busy. I want it to be able to be called from anywhere, any thread,
so I put it as a static method in a class.

Code listed below.

The problem is, no matter what I can think of doing, I get the
following exception:

"Controls created on one thread cannot be parented to a control on a
different thread"

Trace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller,
Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[]
args)
at UI.Waiter.Busy(Object waitOn)

or i used to get this with a slightly different configuration (same
exception):
Trace:
at System.Windows.Forms.ControlCollection.Add(Control value)
at System.Windows.Forms.ControlCollection.Add(Control value)
at UI.Controls.WaitForm.InitializeComponent()
at UI.Controls.WaitForm..ctor()
at UI.Waiter..cctor()


Anyone got any ideas... or am i going about it the wrong way all
together!?

Cheers
Greg



----- CODE -----

using System;
using System.Collections;
using System.Threading;
using System.Windows.Forms;

namespace UI
{
/// <summary>
/// Summary description for Waiter.
/// </summary>
public sealed class Waiter
{
private static ArrayList _list = new ArrayList();
private static Mutex _lock = new Mutex();
private static Controls.WaitForm _form = null;

private static Form _parent = null;
public static Form Parent { get { return _parent; } set { _parent =
value; } }

private Waiter()
{
}

private delegate void BusyDelegate( object obj );
public static void Busy( object waitOn )
{
if( _parent != null )
_parent.Invoke( new BusyDelegate( InternalBusy ), new object[] {
waitOn } );
}

private static void InternalBusy( object waitOn )
{
// this has to be done here for threading reasons
if( _form == null )
_form = new Controls.WaitForm();

// show the waiting form
_lock.WaitOne();
try
{
_list.Add( waitOn );
if( _list.Count == 1 )
{
_form.Restart();
_form.Show();
System.Windows.Forms.Application.DoEvents();
}
}
finally
{
_lock.ReleaseMutex();
}
}

public static void Ready( object waitOn )
{
_lock.WaitOne();
try
{
_list.Remove( waitOn );
if( _list.Count == 0 )
{
_form.Hide();
}
}
finally
{
_lock.ReleaseMutex();
}
}
}
}
 
H

Herfried K. Wagner [MVP]

I am trying to make a window that pops up, to show that
my application is busy. I want it to be able to be called from
anywhere, any thread, so I put it as a static method in a class.

Always show your forms in the app's main UI thread and use 'Control.Invoke'
to communicate in the tread -> UI direction:

Multithreading:

<URL:http://msdn.microsoft.com/library/en-us/dnforms/html/winforms06112002.asp>
<URL:http://msdn.microsoft.com/library/en-us/dnforms/html/winforms08162002.asp>
<URL:http://msdn.microsoft.com/library/en-us/dnforms/html/winforms01232003.asp>

<URL:http://www.devx.com/dotnet/Article/11358/>

<URL:http://msdn.microsoft.com/library/e...SystemWindowsFormsControlClassInvokeTopic.asp>

Multithreading in Visual Basic .NET (Visual Basic Language Concepts)
<URL:http://msdn.microsoft.com/library/en-us/vbcn7/html/vaconthreadinginvisualbasic.asp>

Sample:

<URL:http://dotnet.mvps.org/dotnet/samples/filesystem/downloads/FileSystemEnumerator.zip>
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Hi Giedriusm,

This is Windows OS requirement rather that Windows Forms. This is done
because winprocs are not re-entrant and threadsafe in almost all of the
cases. To make them so requires I great deal of programing discipline, which
will make windows programming harder, but the benefit would be not so big.
To make programing easier windows streams all calls to the winproc (sending
and posting messages) thru the creator's thread's message pump. For posted
messages it comes naturally, sended messages go thru the messsage only when
they cross threads' boundaries. That is all gread and all progarmmers get
advantages of this feature, but beacuse all the communication between
parents and their children (like request, responses, notifications, etc) are
based on window messages there are big chances of deadlocks if the parent
and the child are created by separated threads. That's why Windows checks
and doesn't allow control created in one thread to parent controls created
in different one.



--
HTH
Stoitcho Goutsev (100) [C# MVP]


I am trying to make a window that pops up, to show that my application
is busy. I want it to be able to be called from anywhere, any thread,
so I put it as a static method in a class.

Code listed below.

The problem is, no matter what I can think of doing, I get the
following exception:

"Controls created on one thread cannot be parented to a control on a
different thread"

Trace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller,
Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[]
args)
at UI.Waiter.Busy(Object waitOn)

or i used to get this with a slightly different configuration (same
exception):
Trace:
at System.Windows.Forms.ControlCollection.Add(Control value)
at System.Windows.Forms.ControlCollection.Add(Control value)
at UI.Controls.WaitForm.InitializeComponent()
at UI.Controls.WaitForm..ctor()
at UI.Waiter..cctor()


Anyone got any ideas... or am i going about it the wrong way all
together!?

Cheers
Greg



----- CODE -----

using System;
using System.Collections;
using System.Threading;
using System.Windows.Forms;

namespace UI
{
/// <summary>
/// Summary description for Waiter.
/// </summary>
public sealed class Waiter
{
private static ArrayList _list = new ArrayList();
private static Mutex _lock = new Mutex();
private static Controls.WaitForm _form = null;

private static Form _parent = null;
public static Form Parent { get { return _parent; } set { _parent =
value; } }

private Waiter()
{
}

private delegate void BusyDelegate( object obj );
public static void Busy( object waitOn )
{
if( _parent != null )
_parent.Invoke( new BusyDelegate( InternalBusy ), new object[] {
waitOn } );
}

private static void InternalBusy( object waitOn )
{
// this has to be done here for threading reasons
if( _form == null )
_form = new Controls.WaitForm();

// show the waiting form
_lock.WaitOne();
try
{
_list.Add( waitOn );
if( _list.Count == 1 )
{
_form.Restart();
_form.Show();
System.Windows.Forms.Application.DoEvents();
}
}
finally
{
_lock.ReleaseMutex();
}
}

public static void Ready( object waitOn )
{
_lock.WaitOne();
try
{
_list.Remove( waitOn );
if( _list.Count == 0 )
{
_form.Hide();
}
}
finally
{
_lock.ReleaseMutex();
}
}
}
}
 
G

gregbacchus

That's what I thought I am doing in public static void Busy( object
waitOn ) with the parent.Invoke... where parent is the main UI form.
 
G

gregbacchus

That's what I am doing in public static void Busy( object waitOn ). The
parent is the Main Window of the application for that reason. So that
with the invoke, the whole thing should be happening in the thread of
the Main form. Or am I wrong??
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Windows, doesn't allow parenting controls created in different thread in the
first place. Win32 API fails when you try to do that so, you won't get to
the point where you call the parent's Invoke mehod
 
G

gregbacchus

The class "Waiter" is not a Control. The only point of the Parent
property is so that I can use invoke on the Main UI Thread.

So I set the Parent Property and the call Busy(). This should, by my
understanding call InternalBusy in the thread of Parent. It is here
that is the Popup form is created and all its controls are added....
all in the thread of the Parent Form (main UI form)... Then as its all
happening in this thread, why then is it coming up with the error??
Cheers

Greg
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Yes that part of your code is correct.
However, from what I can see in the stack trace the Waiter class has a
static constructor which actually creates the WaitForm control. The type is
loaded and static constructor is executed by the thread where the first time
the instance of the object is created or method is accessed (depending on
what type of initialization is set). So if you create the form in the static
constructor it is possible that it is created not in the UI thread.
IMHO the code snippet you posted doesn't match the stack trace. At least it
doesn' show the point where you add the WaitForm control to the parent's
Control collection. This is actually the place where the exception is
thrown.

If you can post some working example that demonstrates the problem. That
would help to isolate the problem faster.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top