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.