Right, so the UI thread handles windows messages for *all forms? This
means all methods are synchronized, so that the close event handler
will never be executed when another method is in mid execution.
Yes, sort of. The above statement needs qualification, as forms that are
created in a thread other than the main UI thread are handled in that
other thread. But as long as you never create a new form from other than
the main UI thread, all of the code in all of the forms that is executed
in response to windows messages will be executed on the main UI thread.
For what it's worth, I never have understood the point in using
InvokeRequired. I always just invoke. There's no harm in doing so on the
form's creating thread, and in most cases I know that I'm calling from
another thread anyway. The one possible "downside" is that you need two
methods instead of one (the thread-safe one, and the one that actually
does work), but frankly I don't like the pattern where a single method
does two completely different things depending on some flag anyway
(InvokeRequired in this case). And if the work you're doing is reasonably
short, it's easy enough to use an anonymous delegate instead (so all the
code winds up contained in a single method...obviously you can use an
anonymous method with any length code, but I think it's more suitable for
blocks of code that are a half-dozen lines or less).
The data gets updated in another thread. This thread will invoke
something to tell the form to update its display with the new data.
So, I will have to call MyDataForm.InvokeRequired, to see if the UI
thread is running the method. If it isn't, I'll make it run it, using
MyDataForm.BeginInvoke.
Uh...okay. Seems like more trouble, but if that's the way you want to do
it, it should work.

See below for reasons you might want to
reconsider though...
I use BeginInvoke to avoid deadlocks. (The main form calls Join()
which waits for threads to end, which means a thread cannot be waiting
for the main form with Invoke, or else deadlock).
IMHO, it's generally a bad idea to use Join(). There are certainly some
cases where it's just the right thing, but I think for normal inter-thread
communications, it's not necessary and results in overly complicated
situations. For example:
The data passed has
a lock around the call. This is perfect for Invoke. But, since
BeginInvoke returns immediately, and the caller will unlock, by the
time the UI thread processes the method in the future, it will be
accessing data without a lock. So, I have to make a copy of the
data! And call BeginInvoke with that copy. Whew.
If you were using Invoke(), then you would be assured of synchronizing
access to the data via that mechanism. No locking required. Your code
would be much simpler, even accounting for whatever different mechanism
you require for the main form to be notified of the threads' completion.
Yes, given your use of Join(), using BeginInvoke() is the way around
having deadlocks. But you shouldn't have to use Join() in the first
place, and so the deadlocks you're fixing by using BeginInvoke() also
shouldn't be a problem.
In the form's methods, I can call this.IsDisposed to see if the form
exists or not? The main form caller will have to check if the form is
null or not, as well, before the call.
More complication. Note that the reason you are needing to check whether
the form is still around is because you're using BeginInvoke(). Your use
of BeginInvoke() also introduces a somewhat odd situation in that none of
the BeginInvoke() calls that you've made will actually execute until your
main thread leaves the Join() call. So not only does your form not get a
chance to do any updating for user feedback, you create a situation where
all of the possible feedback gets done all at once, regardless of the
intervals between when the processing has been completed, and all of that
feedback could conceiveably be executed *after* the form has been disposed.
If, instead, you used Invoke(), providing a different mechanism for the
main form to know the threads are done, the user would get timely feedback
as each thread finishes. In addition, if you provide a mechanism for the
threads to be interrupted (ie the main thread signals to them to exit
without finishing), then you could have a way for the user to just cancel
the processing altogether (which would be useful when the user closes the
form, or exits the application, or pushes a button that says "Stop
Processing", or whatever). You would want to be careful to not assume any
given thread actually got the signal; if you set it just as the thread was
exiting or updating the form, for example, it would be moot at that
point. But again, since you'd have a mechanism for each thread to notify
the main form about completion, this should not be a problem.
To avoid the main form from caring if this form exists, I could call a
static method. It will call InvokeRequired (to see if it's in the UI
thread), then BeginInvoke (to force it to be in the UI thread), and
only at this time, I can check a local variable that says if the form
exists or not (which is set on load and close). I cannot check it
before, because BeginInvoke delays the actual method execution.
Interesting. More complication.

Note that all of this complication
basically stems from your use of Join(), which is requiring the use of
asynchronous invokes on the main form, which is causing all of these
synchronization issues. Stop using Join(), and all of the sudden
everything can be synchronous again, and the issues go away.
Ok, thanks for the ideas, Nicholas
For what it's worth, I think you read a lot into Nicholas' reply that
wasn't necessarily there. He answered your specific question, and gave
you good advice regarding how to deal with the potential for the form
disappearing before some thread tries to access it in some way. But I
didn't see anything in his reply that said it was a good idea to get into
that situation if you can avoid it.
Pete