.InvokeRequired always resulting in true

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

Guest

Wow... unbelieveable that this problem would arise right before giving the
software to our public testers... or maybe it is believable. We tweaked some
seemingly unrelated code somewhere else having nothing to to with this
object, and now the following function is causing a
System.StackOverflowException:

private void ResetAllDataFieldsSafely()
{

if(myWebBrowser.InvokeRequired) //always evaluates to true
{
SafelyResetAllDataFieldsDD d = new
SafelyResetAllDataFieldsDD(ResetAllDataFieldsSafely);
this.Invoke(d);
}
else
{ // now this should be running on the main GUI thread
ResetAllDataFields();
}
} // end ResetAllDataFieldsSafely ()

myWebBrowser.InvokeRequired always is true now! I will watch it in debug
mode and indeed this is an infinite loop. Why on earth is this happening?
Is there something strange you can do to a web browser object that will
always cause it to evaluate to true? I am calling ResetAllDataFieldsSafely()
from another thread.

It worked before...

Thanks to anyone and everyone who reads this,

Rob K
 
RobKinney1 said:
private void ResetAllDataFieldsSafely()
{

if(myWebBrowser.InvokeRequired) //always evaluates to true
{
SafelyResetAllDataFieldsDD d = new
SafelyResetAllDataFieldsDD(ResetAllDataFieldsSafely);
this.Invoke(d);
}
else
{ // now this should be running on the main GUI thread
ResetAllDataFields();
}
} // end ResetAllDataFieldsSafely ()

myWebBrowser.InvokeRequired always is true now!

Interesting. I must say, in C# 2.0, I would write the following:

myWebBrowser.Invoke((MethodInvoker) delegate
{
ResetAllDataFields();
});

.... and rely on Invoke dispatching it properly.

-- Barry
 
Barry! Thanks for replying! I wish I could have known about this sooner.
Excellent. It seemed I looked forever before I finally stumbled accross the
solution I had origninally.

Here is my confession though... I have 3 seperate browsers in my program.
Contained within ResetAllDataFields(), I destroy and then recreate all of
them (along with resetting a lot of different data). I simply latched onto
myWebBrowser because it was contained withing ResetAllDataFields() and it was
originally the one throwing a COM error when I was calling this from a worker
thread.

It seemed that in my function, if I just checked this object beforehand, I
would know if ResetAllDataFields needed to be invoked on the main thread..
But technically since I am grabbing all 3 browsers and destroying/creating
them within this function, I could have used any of them right there.

Is this sloppy? If so, how can I tell within a function like
ResetAllDataFieldsSafely() whether or not it is being run from a worker
thread or the main thread (without using a random control)?

I put in your suggested code where my original function call was and it
yielded the following error when it came accross another browser object when
destroying and recreating it:

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

(better than the infinite recursion!!)... but this leads me to believe that
even the invoker is trying to launch ResetAllDataFields on the worker
thread....

Am I missing something here?

Thank you so much for your help.

Rob K
 
RobKinney1 said:
Here is my confession though... I have 3 seperate browsers in my program.

I presume they are in different windows? With the error below, are your
windows created on different threads? Do you mean to do this?
Contained within ResetAllDataFields(), I destroy and then recreate all of
them (along with resetting a lot of different data). I simply latched onto
myWebBrowser because it was contained withing ResetAllDataFields() and it was
originally the one throwing a COM error when I was calling this from a worker
thread.

I presume you know that you need to do all this work on your GUI thread,
since Windows' windows have thread affinity.
It seemed that in my function, if I just checked this object beforehand, I
would know if ResetAllDataFields needed to be invoked on the main thread..
But technically since I am grabbing all 3 browsers and destroying/creating
them within this function, I could have used any of them right there.

You should create them on the GUI thread, not in your worker thread,
IMO.
Is this sloppy? If so, how can I tell within a function like
ResetAllDataFieldsSafely() whether or not it is being run from a worker
thread or the main thread (without using a random control)?

In your situation, I'd make sure that everywhere that needs to
communicate with the UI has a reference to something which implements
ISynchronizeInvoke (every control does) and pass a delegate to its
Invoke() method.

Because of the way anonymous delegates capture variables from the outer
context, it's as simple as a 'using' block - Invoke + anonymous delegate
simply and relatively efficiently switches context to the GUI and you
can code on.
I put in your suggested code where my original function call was and it
yielded the following error when it came accross another browser object when
destroying and recreating it:

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

That's a pretty serious error. You need to create and parent controls
all on the GUI thread. There are ways to get separate top-level windows
to run on separate threads in WinForms, but it isn't recommended if it
can be avoided, for complexity reasons.

-- Barry
 
Thank you for responding again Barry.

I think I have some work to do. I have gone back to a backup from last
Friday and am reconstructing the recent changes I had made.

You information is very valuable now that I can start from here and build it
up the correct way.

Supposedly that function was supposed to be invoking the reset procedure on
the main GUI thread (called from the worker threads) but somehow... someway
it started to fail (as described above -- probably the case of fat finger on
my part)... I wonder if that is even possible...

Nonetheless, I am back and hard at work. Thank you for your comments. I
will post back when I get it working. :~}

Rob K
 
Thanks Barry. Everything is working great now. I am using your
MethodInvoker example and it has solved more than just this problem.

Hopefully one day I can be of service to you as well.

Excellent!

Rob K
 
Back
Top