Is it a bug?

  • Thread starter Thread starter Dmitri Lazarev
  • Start date Start date
D

Dmitri Lazarev

I have this code:

using System;
namespace Test
{
public class Control : System.Windows.Forms.Control
{
public Control()
{
}
}

public class Application
{
[STAThread]
static void Main()
{
control.CreateControl();
AnotherThreadCall().Join();
ThisThreadCall();
AnotherThreadCall().Join();
}

static public Control control = new Control();
static public System.Threading.Thread AnotherThreadCall()
{
System.Threading.Thread thread = new
System.Threading.Thread(new System.Threading.ThreadStart(DoSomething));
thread.Start();
return thread;
}
static public void ThisThreadCall()
{
DoSomething();
}
static public void DoSomething()
{
if ( control.InvokeRequired )
{
System.Diagnostics.Trace.WriteLine("Invoke required");
System.Diagnostics.Trace.Flush();
// System.Windows.Forms.MessageBox.Show("Invoke required");
}
else
{
System.Diagnostics.Trace.WriteLine("Invoke not required");
System.Diagnostics.Trace.Flush();
// System.Windows.Forms.MessageBox.Show("Invoke not
required");
}
}
}
}

after executing this code in the output will be
Invoke required
Invoke not required
Invoke required

but if i remove comments before MessageBox.Show output will be:
Invoke required
Invoke not required
Invoke not required

and after this InvokeRequired for control will be always false.

if i replace MessageBox dialog with my own window InvokeRequired works fine.

What is it?

WBR, Lazarev Dmitri
 
Hi Dimitry,
There isn't any bug. The messages are OK. The problem is that when you show
the first Invoke-Not-Required message box and the control gets destroyed.
You can check this. Set a break point in ThisThreadCall and step into
DoSomething method and keep an eye on contol.IsHandleCreated property. You
will see that when you close the message box the property becomes *false*.
Since there is no handle Calling InvokeRequired doesn't make sense anymore.
Whatever is the result any attempt to call a contol's method, requiring a
handle is going to fail.
So the right question is "Why the control gets destroyed after message box?"
And the answer is "Because it is not used anywhere."
There is no message loop. No one can interact with the underlaying windows
control. The control object still exist, but keeping the windows control
alive is a waste of resources.
And the CLR seems like do some cleaning and frees the handle. In order to
keep the control alive there should be message loop started and the control
has to be child of some form.

You can try the following out. The only diference is that I create a UI
thread showing a form and set the control to be a child of this form.

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

namespace Test
{
public class Control : System.Windows.Forms.Control
{
public Control()
{
}
}

public class MyForm: Form
{
public static MyForm form;
public static void RunForm()
{
System.Threading.Thread t = new Thread(new ThreadStart(DoUI));
t.Start();
}
public static void DoUI()
{
Application.Run(form);
}

public MyForm(Control ctrl)
{
form = this;
//if you comment out the following line the result is going to be
as before
this.Controls.Add(ctrl);
}
}



public class TestApplication
{
[STAThread]
static void Main()
{


System.Threading.Thread.CurrentThread.Name = "Main Thread";
control.CreateControl();
MyForm form = new MyForm(control);
MyForm.RunForm();
AnotherThreadCall().Join();
ThisThreadCall();
AnotherThreadCall().Join();
Console.ReadLine();
}

static public Control control = new Control();
static public Form form = new Form();


static public System.Threading.Thread AnotherThreadCall()
{
System.Threading.Thread thread = new
System.Threading.Thread(new
System.Threading.ThreadStart(DoSomething));
thread.Name = "Worker Thread";
thread.Start();
return thread;
}
static public void ThisThreadCall()
{
DoSomething();
}
static public void DoSomething()
{
if ( control.InvokeRequired )
{
// System.Diagnostics.Trace.WriteLine("Invoke required");
// System.Diagnostics.Trace.Flush();
System.Windows.Forms.MessageBox.Show(String.Format("From
Thread \"{0}\" Invoke required",
System.Threading.Thread.CurrentThread.Name));
}
else
{
// System.Diagnostics.Trace.WriteLine("Invoke not required");
// System.Diagnostics.Trace.Flush();
System.Windows.Forms.MessageBox.Show(String.Format("From Thread
\"{0}\" Invoke is not required",
System.Threading.Thread.CurrentThread.Name));
}
}
}
}


HTH
B\rgds
100
 
Hi Dimitry,
There isn't any bug. The messages are OK. The problem is that when you show
the first Invoke-Not-Required message box and the control gets destroyed.
You can check this. Set a break point in ThisThreadCall and step into
DoSomething method and keep an eye on contol.IsHandleCreated property. You
will see that when you close the message box the property becomes *false*.
Since there is no handle Calling InvokeRequired doesn't make sense anymore.
Whatever is the result any attempt to call a contol's method, requiring a
handle is going to fail.
So the right question is "Why the control gets destroyed after message box?"
And the answer is "Because it is not used anywhere."

How system determine using or not using this control? And why system
destroying this control only after
message box. This sample was constructed from project where control used for
marshalling and it is work fine as long as we need without destroying before
calling MessageBox.
If i build this sample on the WindowsApplication - it works fine and control
doesn't really need to be added to any
collection. If i remove message boxes and add GC.Collect() after
control.CreateControl or in any place all seems work fine too.
There is no message loop. No one can interact with the underlaying windows
control. The control object still exist, but keeping the windows control
alive is a waste of resources.

Sorry that is my control he was created and i use it . :) I need him. In the
real project message loop exists, but it is at com single thread apartment.
And i invoking methods of this control from different threads. This is works
fine before calling message box in the contol's thread. If message box was
called from
another thread all works fine too.
And the CLR seems like do some cleaning and frees the handle. In order to
keep the control alive there should be message loop started and the control
has to be child of some form.

You can try the following out. The only diference is that I create a UI
thread showing a form and set the control to be a child of this form.

i always have all windows that i need. this is used in interop. Can i block
freeing the handle?
 
Hi Dimitri,
(Look for comments inline)
How system determine using or not using this control? And why system
destroying this control only after
message box. This sample was constructed from project where control used for
marshalling and it is work fine as long as we need without destroying before
calling MessageBox.
If i build this sample on the WindowsApplication - it works fine and control
doesn't really need to be added to any
collection. If i remove message boxes and add GC.Collect() after
control.CreateControl or in any place all seems work fine too.


It seems like CLR examines the Parent property of the controls. The ultimate
parent has to be a form. All controls and forms in the chain have to be
created and have their handles created.
Why does CLR destroy the control after MessageBox? Frankly I don't know. It
hapens only when the message box is showed from within the thread owned the
control's handle. GC.Collect, though, has nothing to do with handles. It
takes care of managed heap only. As long you keep refences to your control
it will not touch anything.
Mybe the message boxes are only one of the triggers, which start the
cleaning.
Sorry that is my control he was created and i use it . :) I need him. In the
real project message loop exists, but it is at com single thread apartment.
And i invoking methods of this control from different threads. This is works
fine before calling message box in the contol's thread. If message box was
called from
another thread all works fine too.

I did more tests. You are right. You don't have to have a message loop in
order for your control to be considered as *in use*. The more important for
the control is to have a parent and going down through the parents chain the
ultimate parent has to be a Form.
i always have all windows that i need. this is used in interop. Can i block
freeing the handle?

In my previous post a answerd your question why the messages are like they
are ;)))
But I can't say whether it is a bug or not. It has some consistence.
If you use a form insted of control (you know that a Form class inherits
from Control so it is kind of control) Everything works just fine (with or
without a message loop). If you use Control (or UserControl) ), though, the
control gets destroyed unless it has a parent and its ultimate parent is a
form. It looks like Forms are designed to be the one that can host other
controls and have thier own life (molodci :-P). Controls are designed to be
only a child and they can't live without their parents. Anyway Forms are
kind of control so there has to be some way to *lock* control's handle.
What you can do (if you don't have better idea) is to use form instaed of
control. The form will stay alive.

B\rgds
100
 
It seems like CLR examines the Parent property of the controls. The ultimate
parent has to be a form. All controls and forms in the chain have to be
created and have their handles created.
Why does CLR destroy the control after MessageBox? Frankly I don't know. It
hapens only when the message box is showed from within the thread owned the
control's handle. GC.Collect, though, has nothing to do with handles. It
takes care of managed heap only. As long you keep refences to your control
it will not touch anything.
Mybe the message boxes are only one of the triggers, which start the
cleaning.

This code

form.Show();
form.Controls.Add(control);
AnotherThreadCall().Join();
ThisThreadCall();
AnotherThreadCall().Join();

does't work too. :)
 
This is just amazing!

Let's rewrite some things


[STAThread]
static void Main()
{
control.CreateControl();
AnotherThreadCall().Join();
ThisThreadCall();
AnotherThreadCall().Join();
}

static public void DoSomething()
{
if ( form.InvokeRequired )
{
System.Diagnostics.Trace.WriteLine("Invoke required");
System.Diagnostics.Trace.Flush();
// System.Windows.Forms.MessageBox.Show("Invoke required");
}
else
{
System.Diagnostics.Trace.WriteLine("Invoke not required");
System.Diagnostics.Trace.Flush();
// System.Windows.Forms.MessageBox.Show("Invoke not
required");
}

and now let's play with form. Let's simply set ShowInTaskBar = false
Oops! Code doesn't work again. And in this case there are no differences how
many controls on the form. :)
 
Yeah, it doesn't work. Both the form get destroyed after the message box. It
doesn't look normal to me.
 
Back
Top