AsyncCallback method accessing WebBrowser control causes exception

A

Artie

Hi,

We have an app which uses an AsyncCallback method to handle the return
from a COM call.

In this callback, if we try to do anything with a WebBrowser control,
we get the following exception:

"InvalidCastException was unhandled
Specified cast is not valid"

If we remove all code which tries to access the WebBrowser, the
exception doesn't occur.

Has anyone got any idea why this would happen? We can access any
other controls in our app and no exception is thrown.

Any help much appreciated.

Artie
 
M

Marc Gravell

First-off; have you used Invoke/BeginInvoke to get back to the UI
thread before touching the controls?

Otherwise, I'd guess that you simply have a bug - i.e. you are
genuinely doing an invalid cast, perhaps picking up the wrong element
from a DHTML query?

Marc
 
A

Artie

First-off; have you used Invoke/BeginInvoke to get back to the UI
thread before touching the controls?

Otherwise, I'd guess that you simply have a bug - i.e. you are
genuinely doing an invalid cast, perhaps picking up the wrong element
from a DHTML query?

Marc

hi Marc,

Yes, we have BeginInvoke to start the process and then EndInvoke in
the callback.

After EndInvoke, even doing something as trivial to our WebBrowser
control as:
webBrowser.Document.Title = "test";

throws the exception.

In this case, what would be causing the invalid cast?

Artie
 
M

Marc Gravell

Ah - confusion due to the very confusing Delegate.[Begin]Invoke vs
Control.[Begin]Invoke; I meant the latter, which pushes work back to
the UI thread. Your EndInvoke usage sounds like Delegate.EndInvoke...

What I meant was something like:

void MyCallback(IAsyncResult result)
{
// (note: this bit runs on a worker thread)
// TODO: usual stuff to get the answer from result

// now jump to UI thread before touching controls
this.Invoke((MethodInvoker)delegate
{
// (note: this bit runs on the UI thread)
// TODO: things that impact your WebBrowser, e.g.
webBrowser1.Left += 5;
});
}
 
W

Willy Denoyette [MVP]

Artie said:
hi Marc,

Yes, we have BeginInvoke to start the process and then EndInvoke in
the callback.

After EndInvoke, even doing something as trivial to our WebBrowser
control as:
webBrowser.Document.Title = "test";

throws the exception.

In this case, what would be causing the invalid cast?

Artie


This is (IMO) not what Marc means with Invoke/BeginInvoke, you are not
allowed to "touch" the webrowser control from your callback which runs on a
thread pulled from the Thread Pool, you have to marshal the call to the
thread owning the "webrowser" control using Control.Invoke/BeginInvoke .

Willy.
 
A

Artie

This is (IMO) not what Marc means with Invoke/BeginInvoke, you are not
allowed to "touch" the webrowser control from your callback which runs on a
thread pulled from the Thread Pool, you have to marshal the call to the
thread owning the "webrowser" control using Control.Invoke/BeginInvoke .

Willy.

public void manualMatchCallbackMethod(IAsyncResult ar)
{
// Retrieve the delegate.
manualMatchAsyncMethodCaller caller =
(manualMatchAsyncMethodCaller)ar.AsyncState;
 
A

Artie

This is (IMO) not what Marc means with Invoke/BeginInvoke, you are not
allowed to "touch" the webrowser control from your callback which runs on a
thread pulled from the Thread Pool, you have to marshal the call to the
thread owning the "webrowser" control using Control.Invoke/BeginInvoke .

Willy.

Hi,

Our code currently has the following in the callback:

public void callbackMethod(IAsyncResult ar)
{
// Retrieve the delegate.
asyncMethodCaller caller = (asyncMethodCaller)ar.AsyncState;

val = caller.EndInvoke(ar);

....

// Added this next line after earlier post
wbControl.Invoke(new _methodInvoker(wbControl.Focus));

wbControl.Document.Title = "blah"; // this is the line that throws
the exception


Is this the right pattern to follow?

Artie
 
W

Willy Denoyette [MVP]

Artie said:
public void manualMatchCallbackMethod(IAsyncResult ar)
{
// Retrieve the delegate.
manualMatchAsyncMethodCaller caller =
(manualMatchAsyncMethodCaller)ar.AsyncState;



I'm not clear on what you are trying to tell us by this piece of code.
Your problem is that you are accessing the RCW wrapping a "WebBrowser" COM
control from a thread that is not the thread on which the control was
created. The InvalidCastException is the result of a failing
cross-Thread/Apartment QueryInterface call, all you can do about this is
access the control from the creators thread (the UI thread) by calling
Control.BeginInvoke or Control.Invoke as suggested by Marc in his last
reply, or by something like this.

this.BeginInvoke((MethodInvoker)delegate
{
webBrowser.Document.Title = "test";
});

Willy.
 
M

Marc Gravell

wbControl.Invoke(new _methodInvoker(wbControl.Focus));
wbControl.Document.Title = "blah"; // this is the line that throws
Is this the right pattern to follow?

No. In this example, only wbControl.Focus() is executed on the UI thread;
the Document.Title still runs on the worker thread. This should be:

wbControl.Invoke((MethodInvoker)delegate {
wbControl.Focus();
wbControl.Document.Title = "blah";
});

When you are on the wrong thread (i.e. in an async-callback), the *only*
things you are really allowed to do is to query InvokeRequired, or execute
Invoke or BeginInvoke. This is due to thread affinity.

Marc
 
P

Peter Duniho

[...]
When you are on the wrong thread (i.e. in an async-callback), the *only*
things you are really allowed to do is to query InvokeRequired, or
execute
Invoke or BeginInvoke. This is due to thread affinity.

Is _that_ what it is? I've yet to see a clear document from Microsoft
explaining why they require the use of Invoke() or BeginInvoke() (the
native Win32 API has a similar requirement, but it's handle automatically
by the OS). Is it really because the GUI thread has been assigned a
specific CPU (which is what "thread affinity" means), and executing GUI
class code on a different CPU would mess things up?

If that's true, where did you find that out, and why have you been keeping
a secret all this time!? :)

Pete
 
M

Marc Gravell

assigned a specific CPU (which is what "thread affinity" means)
Surely that would be processor affinity

I don't think there's any secret that most UI implementations exhibit thread
affinity? Please correct me if I'm using the wrong term [but I'm not sure
that I am...]

Marc
 
P

Peter Duniho

Surely that would be processor affinity

Hmmm...well, I've always used the terms interchangeably. But I've been
known to be sloppy in my use of nomenclature.
I don't think there's any secret that most UI implementations exhibit
thread
affinity? Please correct me if I'm using the wrong term [but I'm not sure
that I am...]

So if I understand your use of the term, all you're saying is that there's
some affinity of the GUI data structures to the thread on which they are
created?

Okay, well...if that's the case, I guess that doesn't actually provide
more insight after all. I mean, that's basically a general description of
the need to use Invoke(), but it doesn't explain why that affinity exists
and, most importantly, why the application code needs to care.

After all, the same affinity exists in the unmanaged API, but Windows
handles it automatically for you in at least some cases (anything that
goes through the window proc is either just posted to the message queue
and pumped in the correct thread, or is sent via SendMessage() which does
the necessary thread marshaling if required).

What I've always been curious about is why .NET doesn't just always wrap
this up and hide it from you. It knows what's allowed across threads and
what's not (after all, it throws an exception when you do it wrong). It
seems like it could have been written to just automatically marshal across
the threads as necessary.

It's that latter question I thought your comment was answering, but I
guess it wasn't. I simply misunderstood what you wrote (my fault).

Oh well...sorry for the fire drill. :) Nothing to see here. Please
disperse.

Pete
 
M

Marc Gravell

I don't pretend that it is an answer to "why", but perhaps one good thing
about this approach is that it avoids accidental and overly inefficient
code - i.e.

x.Foo = 123;
// ... lots more, perhaps involving a loop
x.Bar = "abc";

If each of those lines (executed on a worker thread) silently did an
Invoke() behind your back, you could get some *seriously* poor performance.
However, wrap that up a bit (for not much extra work) and it should perform
OK:

x.Invoke((MethodInvoker) {
x.Foo = 123;
// ... lots more, perhaps involving a loop
x.Bar = "abc";
});

I suspect that this is more by accident than by intent, however...

Marc
 
W

Willy Denoyette [MVP]

Marc Gravell said:
assigned a specific CPU (which is what "thread affinity" means)
Surely that would be processor affinity

I don't think there's any secret that most UI implementations exhibit
thread affinity? Please correct me if I'm using the wrong term [but I'm
not sure that I am...]

Marc

Note that the OP's issue is not related to a cross-thread UI access, the
"InvalidCastException" is the result of a failing "QueryInterface" call
(which really is an implicit cast). The reason for this is that the RCW
(wrapping the Webbrowser AX object) is accessed from a thread (a thread pool
thread) other than the RCW "creator's" thread, whatever that thread might be
as long as it's an STA thread.
Now, as both threads run in different COM apartments, the QI call must be
marshaled from the TP's MTA thread to the object's STA thread, the CLR
needs some marshaling support in the form of a typelib, which is not
available for this wrapped Webbrowser AX control, hence the failing QI which
finally results in an "InvalidCastException " thrown in .NET.

Willy.
 
W

Willy Denoyette [MVP]

Pete, see inline.
Willy.

Peter Duniho said:
Surely that would be processor affinity

Hmmm...well, I've always used the terms interchangeably. But I've been
known to be sloppy in my use of nomenclature.
I don't think there's any secret that most UI implementations exhibit
thread
affinity? Please correct me if I'm using the wrong term [but I'm not sure
that I am...]

So if I understand your use of the term, all you're saying is that there's
some affinity of the GUI data structures to the thread on which they are
created?

Most "User"object handles (but not all) have thread affinity, "Window"
handles (HWND's) have thread affinity, that means that they have to be
accessed by the thread that owns the handle (the creator of the Window
handle). Windows itself does not enforce this rule, accessing HWND's (all
user interface elements are HWND based) from other thread's than the
creator, may yield unexpected results.

Okay, well...if that's the case, I guess that doesn't actually provide
more insight after all. I mean, that's basically a general description of
the need to use Invoke(), but it doesn't explain why that affinity exists
and, most importantly, why the application code needs to care.

After all, the same affinity exists in the unmanaged API, but Windows
handles it automatically for you in at least some cases (anything that
goes through the window proc is either just posted to the message queue
and pumped in the correct thread, or is sent via SendMessage() which does
the necessary thread marshaling if required).

Not true, Windows does not handle this automatically, when a thread other
than the UI thread (the one pumping message and dispatching messages to it's
WndProc) calls a function "directly", that is, without posting a message to
the owning thread, you "may" have violated the affinity rule.

What I've always been curious about is why .NET doesn't just always wrap
this up and hide it from you. It knows what's allowed across threads and
what's not (after all, it throws an exception when you do it wrong).

It does not throw an exception in release builds, only debug builds have
code injected (a 'costly' feature )that check whether the caller thread is
the same a the owning HWND's thread. Note, that accessing the UI elements
(the HWND's) for reading (say reading a property) is not really a
'violation' of the HWND's access rule, the "debug injected" code however
treats all accesses, read and write, from another thread as "illegal" when
not marshaled.

seems like it could have been written to just automatically marshal across
the threads as necessary.
Note that not all UI accesses are thread affinitized, some UI elements may
be accessed cross thread without the need to marshal the call. Also, .NET
doesn't have an idea whether you want to access the UI synchronously or
asynchronously, so it cannot know what marshaling code it must inject.
 
P

Peter Duniho

Most "User"object handles (but not all) have thread affinity, "Window"
handles (HWND's) have thread affinity, that means that they have to be
accessed by the thread that owns the handle (the creator of the Window
handle). Windows itself does not enforce this rule, accessing HWND's
(all user interface elements are HWND based) from other thread's than
the creator, may yield unexpected results.

I assume you mean for calls to functions list SetWindowLong(), etc.? The
docs are clear that PostMessage() and SendMessage() both take care of
threading issues (those were the examples I was talking about).
Not true, Windows does not handle this automatically, when a thread
other than the UI thread (the one pumping message and dispatching
messages to it's WndProc) calls a function "directly", that is, without
posting a message to the owning thread, you "may" have violated the
affinity rule.

I'm not entirely sure what you mean here. If I write code that calls my
window proc literally directly, then I'm not necessarily using the HWND at
all. Granted, it depends on what the window proc does for the given
message ID and parameters.

Are you saying that for internal features of a window class, the window
proc might be using thread-local storage or some other feature that causes
the data to be tied to a specific thread?

Of course, I've always thought that calling SendMessage() was the only
approved way of calling the window proc, but you're right...I guess
there's nothing to stop you from calling it directly.
What I've always been curious about is why .NET doesn't just always
wrap this up and hide it from you. It knows what's allowed across
threads and what's not (after all, it throws an exception when you do
it wrong).

It does not throw an exception in release builds, only debug builds have
code injected (a 'costly' feature )that check whether the caller thread
is the same a the owning HWND's thread. [...]

I'm not sure what you mean here. I get the exception whether I've
compiled a debug or release build.

It's true that the exception only appears to be thrown if the debugger is
present. Did you mean that only when the debugger is injected does the
check get "injected"? If so, could you provide more details on that? I
wasn't aware that the code being executed was different depending on the
presence of the debugger (other than the "vshost" stuff, of course). Does
Microsoft actually document this behavior? Or is this something you just
"have to know"?
Note that not all UI accesses are thread affinitized, some UI elements
may be accessed cross thread without the need to marshal the call. Also,
.NET doesn't have an idea whether you want to access the UI
synchronously or asynchronously, so it cannot know what marshaling code
it must inject.

Well, I would expect it to assume synchronous, since that's what the code
would imply if written without any explicit marshaling.

Anyway, thanks for the comments.

Pete
 
W

Willy Denoyette [MVP]

Peter Duniho said:
I assume you mean for calls to functions list SetWindowLong(), etc.? The
docs are clear that PostMessage() and SendMessage() both take care of
threading issues (those were the examples I was talking about).

No, I'm not talking about these. I'm talking about accessing UI elements
that are backed by HWND from threads other than the thread that created the
HWND (HWND handle = CreateWindow(..)). That means that "handle" should only
be accessed from the thread that runs the message loop associated to the
HWND, or more precisely from the WndProc that was registered when the Window
class was registered. Note that there is no real *danger* in accessing
HWND's from non-owning threads, the system/application will not die if you
do, however, the application may not behave as expected and you may leak
handles when you do. For instance a DestroyWindow(handle) call from another
thread than the creator, will not close the handle, so here you leak, and
finally your process may die because you have exhausted the available "user"
handles.

I'm not entirely sure what you mean here. If I write code that calls my
window proc literally directly, then I'm not necessarily using the HWND at
all. Granted, it depends on what the window proc does for the given
message ID and parameters.

Not sure if you are talking about C# and .NET here, anyway, you don't call
WndProc procedures directly from other threads than the thread that runs the
message loop, don't you?
Here I mean that if you have something like the following then you are in
violation with the HWND affinity rules:

// C# code ahead
// WndProc associated with the UI thread
protected override void WndProc(ref Message m)
{
if(m.Msg == 0x1234)
{
label.Text = "Oh no, this is illegal"; // label is a static
control backed by a HWND
}
base.WndProc(ref m);
}

....
// Other thread ...
someForm.WndProc(ref m);

Here, the 'label' control is backed by an HWND and it's 'Text' property is
accessed using it's HWND, HWND's have thread affinity and therefore should
only be accessed from the same thread that owns the handle. So, here we are
violating the "affinity rule" when setting the Text property.

Note that the same as above is invalid when the other thread directly calls
"label.Text = ....";

Are you saying that for internal features of a window class, the window
proc might be using thread-local storage or some other feature that causes
the data to be tied to a specific thread?

Of course, I've always thought that calling SendMessage() was the only
approved way of calling the window proc, but you're right...I guess
there's nothing to stop you from calling it directly.
What I've always been curious about is why .NET doesn't just always
wrap this up and hide it from you. It knows what's allowed across
threads and what's not (after all, it throws an exception when you do
it wrong).

It does not throw an exception in release builds, only debug builds have
code injected (a 'costly' feature )that check whether the caller thread
is the same a the owning HWND's thread. [...]

I'm not sure what you mean here. I get the exception whether I've
compiled a debug or release build.

It's true that the exception only appears to be thrown if the debugger is
present. Did you mean that only when the debugger is injected does the
check get "injected"? If so, could you provide more details on that?

That's exactly what I meant, you don't need the "debug" build, whenever you
run under the (VS) debugger, you are running in a context where the CLR
injects some MDA's (Managed Debugging Assistant). Please search MSDN for
details on all possible MDA's.
I wasn't aware that the code being executed was different depending on the
presence of the debugger (other than the "vshost" stuff, of course). Does
Microsoft actually document this behavior? Or is this something you just
"have to know"?

I'm not aware of such document, all I know is that the CLR knows that a
managed debugger is attached, I know that V2 of the CLR has a number of
MDA's (run-time probes) that may be active by default while others are
activated when running under the debugger. I know that the message loop and
WndProc are slightly modified when running under the debugger, for instance
to "handle" "unhandled "exceptions (no pun intended :))
I also know that all debuggers (whatever managed/unmanaged) may interfere
with and change the behavior of a program when running under a debugger...
Well, I would expect it to assume synchronous, since that's what the code
would imply if written without any explicit marshaling.
And if you want an asynchronous dispatch? Anyway, inject cross-thread
handling code is way to expensive, performance wise, to be done for each
separate UI element you are touching.
You would not write something similar like this I suppose:

Invoke((MethodInvoker) {
label1.Text = "SomeText";
}
Invoke((MethodInvoker) {
label2.Text = ....;
}
Invoke((MethodInvoker) {
label3.Text = ....;
}

instead you will group all UI update stuff in a single code block that you
will marshal to the UI thread.

Anyway, thanks for the comments.

You're welcome ;-)

Willy.
 
W

Willy Denoyette [MVP]


Oh, now I see that you meant to say "calls to functions *like*
SetWindowLong" instead of "calls to functions *list* SetWindowLong", so, I
guess that below answer may be quite confusing.
I'm indeed talking about the "Windows Functions" like SetWindowsText,
GetWindowText, SetWindowLong, SetWindowPosition..., these are the API's
wrapped by the control classes.
So, a property setter like: "label.Text ="blabla", boils down to a
'SetWindowText(handleOfLabel, "blabla");' function call, where
"handleOfLabel" is the HWND of "label".
So, this function should only be called from the thread that owns this HWND,
which is the thread that called CreateWindow(...) to create the label.

No, I'm not talking about these. I'm talking about accessing UI elements
that are backed by HWND from threads other than the thread that created
the HWND (HWND handle = CreateWindow(..)). That means that "handle" should
only be accessed from the thread that runs the message loop associated to
the HWND, or more precisely from the WndProc that was registered when the
Window class was registered. Note that there is no real *danger* in
accessing HWND's from non-owning threads, the system/application will not
die if you do, however, the application may not behave as expected and you
may leak handles when you do. For instance a DestroyWindow(handle) call
from another thread than the creator, will not close the handle, so here
you leak, and finally your process may die because you have exhausted the
available "user" handles.

Willy.
 
P

Peter Duniho

Oh, now I see that you meant to say "calls to functions *like*
SetWindowLong" instead of "calls to functions *list* SetWindowLong"

Yes, sorry. I didn't even notice my typo when reading your original
reply. While a bit of a tangent, the other information was informative
anyway, so no big deal. :)

Anyway, I guess the bottom line here is that a) the unmanaged API doesn't
actually handle cross-thread access except in a very specific case
(SendMessage()), and b) in either case (managed and unmanaged) there's a
potential and significant performance risk for handling cross-thread
access transparently.

I think those are both strong enough reasons to justify this limitation
existing in .NET, rather than it hiding the underlying architecture from
you.

Thanks,
Pete
 
A

Artie

Ah - confusion due to the very confusing Delegate.[Begin]Invoke vs
Control.[Begin]Invoke; I meant the latter, which pushes work back to
the UI thread. Your EndInvoke usage sounds like Delegate.EndInvoke...

What I meant was something like:

void MyCallback(IAsyncResult result)
{
// (note: this bit runs on a worker thread)
// TODO: usual stuff to get the answer from result

// now jump to UI thread before touching controls
this.Invoke((MethodInvoker)delegate
{
// (note: this bit runs on the UI thread)
// TODO: things that impact your WebBrowser, e.g.
webBrowser1.Left += 5;
});
}

Sorry for late reply Marc, been on holiday.

Anyway, thanks again for your help, this worked in the end, as you
suggested:

webBrowser1.Invoke((MethodInvoker)delegate
{
Focus();
webBrowser1.Document.OpenNew(true);
webBrowser1.Document.Write( "stuff" );
});
 

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