Control.Handle is this a BUG?

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

Hi,

[ Using C# in .Net 1.1 ].

I've been struggling with a "hang" in my application all day and I think I
have just worked out why the problem occurs. Please read through and see if
u think this is a bug.

I have a few UI controls that are being updated on a TimerThread ( I check
the "InvokeRequired" and perform the Invoke if required, when I want to
update the control when Im in the TimerThread ). However I was noticing
problems when this occurred as my app would hang. In the debugger I noticed
that just before the hang, the InvokeRequired was returning "False" when it
was being called from the TimerThread - when it should have being returning
"True".

After much swearing etc.. and many hours, I eventually managed to determine
that the "_CreateThreadId" property for my controls was changing **after**
that control was accessed by the TimerThread. I think that this may all be
being caused by the Control.Handle property. It seems as though the handle
isnt actually assigned until you request it, and when you do it overwrites
the "_CreateThreadId" with the ThreadID of the thread that was running when
the Handle was requested. As such this causes the InvokeRequired method to
return the wrong value and then the app gets screwed when the control is
attempted to be updated from the 2 different threads.

Is this a bug ?

As a very simple workaround I request "myControl.Handle" straight after
creating the control and this gets rid of the problem - even though I am not
actually doing anything with it!! A quick workaround, but a horrid problem!!

RichS
 
Hi!
being caused by the Control.Handle property. It seems as though the
handle
isnt actually assigned until you request it

This is very likely.
It seems as though the handle isnt actually assigned until you request it,
and when you do it overwrites
the "_CreateThreadId" with the ThreadID of the thread that was running
when
the Handle was requested.

Hmm, sounds like a Framework bug! NB Microsoft!

Can you avoid querying the Handle property from the timer thread? For
example, do not try to update invisible controls or controls
whose Parent is null? If such a check still causes the handle to be created
on the wrong thread, your workaround seems to be an appropriate solution. I
have used something like this to force the handle to be created and it
worked just fine.

--
Sincerely,
Dmytro Lapshyn [Visual Developer - Visual C# MVP]


RichS said:
Hi,

[ Using C# in .Net 1.1 ].

I've been struggling with a "hang" in my application all day and I think I
have just worked out why the problem occurs. Please read through and see
if
u think this is a bug.

I have a few UI controls that are being updated on a TimerThread ( I check
the "InvokeRequired" and perform the Invoke if required, when I want to
update the control when Im in the TimerThread ). However I was noticing
problems when this occurred as my app would hang. In the debugger I
noticed
that just before the hang, the InvokeRequired was returning "False" when
it
was being called from the TimerThread - when it should have being
returning
"True".

After much swearing etc.. and many hours, I eventually managed to
determine
that the "_CreateThreadId" property for my controls was changing **after**
that control was accessed by the TimerThread. I think that this may all
be
being caused by the Control.Handle property. It seems as though the
handle
isnt actually assigned until you request it, and when you do it overwrites
the "_CreateThreadId" with the ThreadID of the thread that was running
when
the Handle was requested. As such this causes the InvokeRequired method
to
return the wrong value and then the app gets screwed when the control is
attempted to be updated from the 2 different threads.

Is this a bug ?

As a very simple workaround I request "myControl.Handle" straight after
creating the control and this gets rid of the problem - even though I am
not
actually doing anything with it!! A quick workaround, but a horrid
problem!!

RichS
 
Not just me going mad then :).

I would be interested if anybody else knew of this problem.

RichS

Dmytro Lapshyn said:
Hi!
being caused by the Control.Handle property. It seems as though the
handle
isnt actually assigned until you request it

This is very likely.
It seems as though the handle isnt actually assigned until you request it,
and when you do it overwrites
the "_CreateThreadId" with the ThreadID of the thread that was running
when
the Handle was requested.

Hmm, sounds like a Framework bug! NB Microsoft!

Can you avoid querying the Handle property from the timer thread? For
example, do not try to update invisible controls or controls
whose Parent is null? If such a check still causes the handle to be created
on the wrong thread, your workaround seems to be an appropriate solution. I
have used something like this to force the handle to be created and it
worked just fine.

--
Sincerely,
Dmytro Lapshyn [Visual Developer - Visual C# MVP]


RichS said:
Hi,

[ Using C# in .Net 1.1 ].

I've been struggling with a "hang" in my application all day and I think I
have just worked out why the problem occurs. Please read through and see
if
u think this is a bug.

I have a few UI controls that are being updated on a TimerThread ( I check
the "InvokeRequired" and perform the Invoke if required, when I want to
update the control when Im in the TimerThread ). However I was noticing
problems when this occurred as my app would hang. In the debugger I
noticed
that just before the hang, the InvokeRequired was returning "False" when
it
was being called from the TimerThread - when it should have being
returning
"True".

After much swearing etc.. and many hours, I eventually managed to
determine
that the "_CreateThreadId" property for my controls was changing **after**
that control was accessed by the TimerThread. I think that this may all
be
being caused by the Control.Handle property. It seems as though the
handle
isnt actually assigned until you request it, and when you do it overwrites
the "_CreateThreadId" with the ThreadID of the thread that was running
when
the Handle was requested. As such this causes the InvokeRequired method
to
return the wrong value and then the app gets screwed when the control is
attempted to be updated from the 2 different threads.

Is this a bug ?

As a very simple workaround I request "myControl.Handle" straight after
creating the control and this gets rid of the problem - even though I am
not
actually doing anything with it!! A quick workaround, but a horrid
problem!!

RichS
 
RichS said:
I would be interested if anybody else knew of this problem.
I seem to have it too. In my case, I've seen the "_CreateThreadId"
having the UI thread's ID (normally), the thread ID of another (non-UI)
thread and the value 0. Initially, when I tried to force the handle
creation (using the statement "IntPtr brh = buttonRun.Handle", the
program hung before returning.

Is there a way to use the VS debugger to troubleshoot this kind of a
problem? For instance, it would be nice to break whenever
"_CreateThreadId" changes. Even better would be a way to have the
debugger breakpoint when an invoke should have been used but wasn't.
The current behaviour (playing Russian roulette as to whether or not
the missing invoke produces an observable effect) isn't very
satisfactory.

Thanks

Mike
 
Hi,

In the end I think the problems I was having ( which sound just like your
problems ) were all down to some things accessing / setting UI values on a
thread other than the UI thread.

In the end I debugged my app in VS2005 ( which seems much better at spotting
UI thread problems ). After a bit of work clearing up the problems
identified with VS2005 I havent had these Control.Handle issues.

Hope this helps,
Rich.

PS, To help with this I created a class to wrap the InvokeRequired / Invoke
functionality:


public class ControlHelper
{
private delegate void DelegateUpdateFormOwner( Form _form, Form
_formParent );
private delegate void DelegateUpdateControlText( Control _control,
string _strText );
private delegate void DelegateUpdateControlVisible( Control
_control, bool _bVisible );
private delegate void DelegateUpdateControlEnabled( Control
_control, bool _bEnabled );
private delegate void DelegateUpdateControlCursor( Control _control,
Cursor _cursor );
private delegate void DelegateUpdateControlAdd( Control
_controlParent, Control _controlChild );
private delegate void DelegateUpdateControlRemove( Control
_controlParent, Control _controlChild );

private delegate void DelegateUpdateListViewAdd( ListView _listView,
ListViewItem _listViewItem );


/// <summary>
/// Constructor
/// </summary>
public ControlHelper()
{
}

/// <summary>
/// Attempts to set the Owner of a form
/// </summary>
/// <param name="_form"></param>
public static void SetMainWindowOwner( Form _form )
{
Process process = Process.GetCurrentProcess();
IntPtr intptrMainWindow = process.MainWindowHandle;

if ( IntPtr.Zero != intptrMainWindow )
{
Form formMain = Control.FromHandle( intptrMainWindow ) as
Form;
if ( null != formMain )
{
UpdateFormOwner( _form, formMain );
}
}
}

/// <summary>
/// Initial handler for updating the owner of a control
/// </summary>
/// <param name="_form"></param>
/// <param name="_formParent"></param>
public static void UpdateFormOwner( Form _form, Form _formParent )
{
if ( true != _form.InvokeRequired )
{
PerformUpdateFormOwner( _form, _formParent );
}
else
{
_form.Invoke( new DelegateUpdateFormOwner(
ControlHelper.PerformUpdateFormOwner ), new object[] { _form, _formParent } );
}
}

/// <summary>
/// Actioner for updating the Owner
/// </summary>
/// <param name="_form"></param>
/// <param name="_formParent"></param>
private static void PerformUpdateFormOwner( Form _form, Form
_formParent )
{
_form.Owner = _formParent;
}

/// <summary>
/// Initial handler for updating the text of a control
/// </summary>
/// <param name="_control"></param>
/// <param name="_strText"></param>
public static void UpdateControlText( Control _control, string
_strText )
{
if ( true != _control.InvokeRequired )
{
PerformUpdateControlText( _control, _strText );
}
else
{
_control.Invoke( new DelegateUpdateControlText(
ControlHelper.PerformUpdateControlText ), new object[] { _control, _strText }
);
}
}

/// <summary>
/// Actioner for updating the Text
/// </summary>
/// <param name="_control"></param>
/// <param name="_strText"></param>
private static void PerformUpdateControlText( Control _control,
string _strText )
{
_control.Text = _strText;
}

/// <summary>
/// Initial handler for updating the visible state of a control
/// </summary>
/// <param name="_control"></param>
/// <param name="_bVisible"></param>
public static void UpdateControlVisible( Control _control, bool
_bVisible )
{
if ( true != _control.InvokeRequired )
{
PerformUpdateControlVisible( _control, _bVisible );
}
else
{
_control.Invoke( new DelegateUpdateControlVisible(
ControlHelper.PerformUpdateControlVisible ), new object[] { _control,
_bVisible } );
}
}

....
 
Back
Top