STAThread vs non STAThread framework

R

rafalK

Hi All,
I have a big problem with STAThread attribute. I'm using XNA framework
connected with WinForms. XNA is working in non STAThread. I have a
problem with displaying CommonDialog forms e.g. OpenFileDialog. I
can't declare a main method as STAThread because of XNA framework. Is
there any way to declare form or some method in form as STAThread (I
think that a method from Form can invoke OpenFileDialog) maybe I'm
wrong.

Do you know how I can resolve a problem?

Thanks.
RK.
 
W

Willy Denoyette [MVP]

rafalK said:
Hi All,
I have a big problem with STAThread attribute. I'm using XNA framework
connected with WinForms. XNA is working in non STAThread. I have a
problem with displaying CommonDialog forms e.g. OpenFileDialog. I
can't declare a main method as STAThread because of XNA framework. Is
there any way to declare form or some method in form as STAThread (I
think that a method from Form can invoke OpenFileDialog) maybe I'm
wrong.

Do you know how I can resolve a problem?

Thanks.
RK.

A Windows Forms application MUST be tagged with the STAThread attribute, no
way around this.
Not sure what you mean with "XNA is working in non STA Thread" though, do
you mean you are running XNA code in a separate thread, or are you running
XNA code on the main thread?
In the latter case, you simply need to add STAThread to your main entry, in
the first case you'll have to delegate the calls to the form by means of
Control.Invoke/BeginInvoke whenever you need to access the From or one of
it's elements.


Willy.
 
J

Jon Skeet [C# MVP]

A Windows Forms application MUST be tagged with the STAThread
attribute, no way around this.

Out of interest, what are the problems one might find without this? For
test purposes I've often created forms which do very little, and rarely
bothered to put the STAThread attribute on them. They've been fine -
but I quite understand that either this was through chance or because I
wasn't using particular components.
 
M

Marc Gravell

The only times I've needed it are when:
* using the Clipboard object
* using ActiveX controls / components

Other than that, I have never seen any issues in the wild when
omitting this...

Marc
 
R

rafalK

I'm running XNA code in main thread and also Form is starting from
main thread. I'm working now without STAThread in main entry and it
works. But as I wrote some is not working. XNA (game library) can't
use STAThread declared in main thread because some components will not
work.
Do You know any way to get round problem ?
 
M

Marc Gravell

Use different threads for any code that /must/ be STA, and code that
/must not/ be STA?

Marc
 
W

Willy Denoyette [MVP]

Jon Skeet said:
Out of interest, what are the problems one might find without this? For
test purposes I've often created forms which do very little, and rarely
bothered to put the STAThread attribute on them. They've been fine -
but I quite understand that either this was through chance or because I
wasn't using particular components.


Jon,

A couple of the Windows Forms controls are simple wrappers around ActiveX
COM controls, notably all the "Dialog Controls" are AX control wrappers, one
of them being the OpenFileDialog as used by the OP.
Such controls need a *pumping* STA thread to run on.
Now, if you don't initialize your UI thread to enter an STA, the CLR will
initialize the thread to enter the MTA whenever you call into COM for the
first time, that is when you create an instance of say OpenFileDialog. Note
that the v2 SP1 CLR initializes the thread to enter the MTA (if not marked
otherwise) even before it starts the main thread.
FileDialog (the wrapped AX dialog) as all other dialogs, is marked as
requiring an STA, so it can't get instantiated on the calling MTA thread,
COM takes care of this by creating a new thread that gets initialized to
enter an STA and creates the instance of the dialog on this thread and
returns a proxy to the caller.
That mean you'll end with the following (important for this discussion)
threads in a Forms application that doesn't mark it's main UI thread as
STAThread.

Main thread (UI) MTA.
GDI Thread MTA (used by Forms)
STA thread created by COM hosting the FileDialog AX control.

So far so good, the control runs on a compatible thread, and the caller
receives a proxy (a marshaled Interface Pointer). The problem however is
that the STA thread created by COM on your behalf isn't pumping messages,
that means that your OpenFileDialog cannot communicate with the callers
thread. The result is that the dialog cannot be used,it will not even
show-up (I guess) and the UI thread deadlocks.
Note also that the Finalizer (MTA) thread cannot access the controls STA
thread whenever he needs to run the Control's Finalize method. This is no
big deal in this scenario, as the control is non-functional anyway, but it's
a big issue when you have to deal with non-visual AX controls.

Willy.
 
W

Willy Denoyette [MVP]

rafalK said:
I'm running XNA code in main thread and also Form is starting from
main thread. I'm working now without STAThread in main entry and it
works. But as I wrote some is not working. XNA (game library) can't
use STAThread declared in main thread because some components will not
work.
Do You know any way to get round problem ?


Mind to share with us what XNA components do not work from an STA. A
component like XNA should handle it's own threading requirements, that is
when it doesn't run on a compatible thread it should create his own thread
and handle the marshaling.
That said, (AFAIK) XNA has no COM thread affinity, so it should run on any
thread STA or MTA, unless it is documented that it can't run on an STA
thread. This is different from Windows Forms, where this requires an STA to
run in.

Willy.
 
J

Jon Skeet [C# MVP]

Willy Denoyette said:
A couple of the Windows Forms controls are simple wrappers around ActiveX
COM controls, notably all the "Dialog Controls" are AX control wrappers, one
of them being the OpenFileDialog as used by the OP.
Such controls need a *pumping* STA thread to run on.

Right. Okay, so in many cases you'd get away with it not entirely
through chance but just by not using the controls which require the
pumping STA.
Now, if you don't initialize your UI thread to enter an STA, the CLR will
initialize the thread to enter the MTA whenever you call into COM for the
first time, that is when you create an instance of say OpenFileDialog. Note
that the v2 SP1 CLR initializes the thread to enter the MTA (if not marked
otherwise) even before it starts the main thread.

Yikes - that sounds like a significant change. My understanding was
that the CLR would only initialize a thread one way or another the
first time a method with STAThread or MTAThread was encountered on that
thread. In other words, your Main method could call something else
which in turn was decorated with STAThread. Am I right in interpreting
your comments to say this isn't the case with the v2 CLR SP1?
FileDialog (the wrapped AX dialog) as all other dialogs, is marked as
requiring an STA, so it can't get instantiated on the calling MTA thread,
COM takes care of this by creating a new thread that gets initialized to
enter an STA and creates the instance of the dialog on this thread and
returns a proxy to the caller.
That mean you'll end with the following (important for this discussion)
threads in a Forms application that doesn't mark it's main UI thread as
STAThread.

Main thread (UI) MTA.
GDI Thread MTA (used by Forms)
STA thread created by COM hosting the FileDialog AX control.

Why is the GDI thread not the main thread? I thought that
Application.Run just ran the message pump on the current thread? Or
does GDI just need two threads anyway, one for message pumping and one
for something else?
So far so good, the control runs on a compatible thread, and the caller
receives a proxy (a marshaled Interface Pointer). The problem however is
that the STA thread created by COM on your behalf isn't pumping messages,
that means that your OpenFileDialog cannot communicate with the callers
thread. The result is that the dialog cannot be used,it will not even
show-up (I guess) and the UI thread deadlocks.
Note also that the Finalizer (MTA) thread cannot access the controls STA
thread whenever he needs to run the Control's Finalize method. This is no
big deal in this scenario, as the control is non-functional anyway, but it's
a big issue when you have to deal with non-visual AX controls.

Right. Thanks for clearing that up. Admittedly it's open up other cans
of worms in terms of my understanding of WinForms, but there we go...
 
R

rafalK

That said, (AFAIK) XNA has no COM thread affinity, so it should run on any
thread STA or MTA, unless it is documented that it can't run on an STA
thread. This is different from Windows Forms, where this requires an STA to
run in.

Willy, so You want to say that main thread XNA can be declared as STA
thread?
I'm not familiar with XNA so good to say "hey, this object is using
COM", but I read on XNA forum some XNA components are working in MTA
e.g. ContentManager wich load resources (Textures, models) or XACT
(plays sounds)
But as I wrote I'm not sure.
Are You sure I can declare XNA main thread as STA thread? I didn't
find it is documented anywhere.
 
W

Willy Denoyette [MVP]

Jon Skeet said:
Right. Okay, so in many cases you'd get away with it not entirely
through chance but just by not using the controls which require the
pumping STA.

If you only use controls that are *not* COM based, *and* If you don't use
"cust and paste" support in Windows Forms, which is OLE based (OLE is a COM
based technology), then you can safely ignore the STAThread attribute on
main, but why would you?
The problem is that you can't possibly know which controls are COM based and
which are not, so STATHread is a safe bet when you have to deal with Forms
and V3's WPF.
Yikes - that sounds like a significant change. My understanding was
that the CLR would only initialize a thread one way or another the
first time a method with STAThread or MTAThread was encountered on that
thread. In other words, your Main method could call something else
which in turn was decorated with STAThread. Am I right in interpreting
your comments to say this isn't the case with the v2 CLR SP1?
The current version of the CLR initilizes COM at the first call into a RCW,
say from the main UI thread. If the main entry is not tagged with a thread
attribute, the RCW will initialize the thread to enter the MTA, else it will
initialize the thread to enter the apartment that is specified by the
attribute.
The problem with the current version is that initializing at first call
might be too late , therefore the CLR team took the decision to initialize
the thread before calling main. That means that the UI thread is always
initialize when main starts executing, and there is no surprise here,
STAThread initializes the thread to enter an STA, all other cases initialize
the thread to enter the MTA.

Why is the GDI thread not the main thread? I thought that
Application.Run just ran the message pump on the current thread? Or
does GDI just need two threads anyway, one for message pumping and one
for something else?

GDI creates and manages it's own thread, this is not a CLR thread , it
becomes a CLR thread when this thread calls back into the main thread.

Right. Thanks for clearing that up. Admittedly it's open up other cans
of worms in terms of my understanding of WinForms, but there we go...
Well, I guess it's just a matter of respecting a number of simple rules
(this is in general what Software development is all about), if you don't
then you better know what's going on under the covers.

Willy.
 
W

Willy Denoyette [MVP]

rafalK said:
Willy, so You want to say that main thread XNA can be declared as STA
thread?
I'm not familiar with XNA so good to say "hey, this object is using
COM", but I read on XNA forum some XNA components are working in MTA
e.g. ContentManager wich load resources (Textures, models) or XACT
(plays sounds)
But as I wrote I'm not sure.
Are You sure I can declare XNA main thread as STA thread? I didn't
find it is documented anywhere.


Well, honestly I don't know that much about XNA, all I know is that Windows
Forms need an STA thread to run, especially if you are using the "Dialog
Controls". My guess is that XNA is COM agnostic, that means it has not
specific COM threading requirements, however, if it does, it should be
documented, and if it requires an MTA (which I doubt) then you have a small
problem that can easily be solved by running XNA on a separate (MTA) thread.


Willy.
 
J

Jon Skeet [C# MVP]

Willy Denoyette said:
If you only use controls that are *not* COM based, *and* If you don't use
"cust and paste" support in Windows Forms, which is OLE based (OLE is a COM
based technology), then you can safely ignore the STAThread attribute on
main, but why would you?

In my case it's simply been forgetfulness for throwaway apps.
The problem is that you can't possibly know which controls are COM based and
which are not, so STATHread is a safe bet when you have to deal with Forms
and V3's WPF.

Fair enough.
The current version of the CLR initilizes COM at the first call into a RCW,
say from the main UI thread. If the main entry is not tagged with a thread
attribute, the RCW will initialize the thread to enter the MTA, else it will
initialize the thread to enter the apartment that is specified by the
attribute.
The problem with the current version is that initializing at first call
might be too late , therefore the CLR team took the decision to initialize
the thread before calling main. That means that the UI thread is always
initialize when main starts executing, and there is no surprise here,
STAThread initializes the thread to enter an STA, all other cases initialize
the thread to enter the MTA.

Hmm... that still sounds like a breaking change where previously you
might have had:

Main (no attribute) calling...
StartUI (STAThread) calling...
OtherStuff

I *think* that would have previously used an STAThread, but is now
using an MTAThread (and is therefore incorrect). Or am I just wrong
about it previously using an STAThread in that case?
GDI creates and manages it's own thread, this is not a CLR thread , it
becomes a CLR thread when this thread calls back into the main thread.

Blick. Hopefully Joe Duffy's new book will explain all this when it
comes out :) I'm woefully uninformed when it comes to WinForms
threading. General threading principles I'm fine with, but this is
somewhat different.
Well, I guess it's just a matter of respecting a number of simple rules
(this is in general what Software development is all about), if you don't
then you better know what's going on under the covers.

True.
 
W

Willy Denoyette [MVP]

Jon Skeet said:
In my case it's simply been forgetfulness for throwaway apps.

No big deal in this case, as long as you keep in mind that in case of a
Forms application, that you are creating at least one additional thread
possibly two, that means 1 or 2MB additional stack space used (4 or 8MB on
X64).

Fair enough.


Hmm... that still sounds like a breaking change where previously you
might have had:

Main (no attribute) calling...
StartUI (STAThread) calling...
OtherStuff

I *think* that would have previously used an STAThread, but is now
using an MTAThread (and is therefore incorrect). Or am I just wrong
about it previously using an STAThread in that case?
I'm afraid you are wrong here , the main thread is "non-initialized" at the
start of an non-attributed Main, such main thread initializes to enter the
MTA whenever some code running in this thread calls into COM.
The only difference is that now(V2 SP1), the main thread will enter an STA
when the attribute is set, else it enters the MTA, *before* calling Main.

Blick. Hopefully Joe Duffy's new book will explain all this when it
comes out :) I'm woefully uninformed when it comes to WinForms
threading. General threading principles I'm fine with, but this is
somewhat different.

I don't know whether Joe will talk about this unmanaged GDI++ stuff, note
that Windows Forms has some more (unmanaged) thread magic going on under
the covers, especially when you are calling into OLE stuff or AX controls
(like Dialogs) from a wrong (non STA) thread.


Willy.
 
J

Jon Skeet [C# MVP]

I'm afraid you are wrong here , the main thread is "non-initialized" at the
start of an non-attributed Main, such main thread initializes to enter the
MTA whenever some code running in this thread calls into COM.
The only difference is that now(V2 SP1), the main thread will enter an STA
when the attribute is set, else it enters the MTA, *before* calling Main.

Let me clarify - because I now believe I was wrong, but I may also have
put it badly before :)

I thought it would be uninitialized during Main, but on the call to
StartUI (decorated with the STAThread attribute) it would become
initialized as STA.

Now I've just read in the STAThreadAttribute this bit:

<quote>
Apply this attribute to the entry point method (the Main() method in C#
and Visual Basic). It has no effect on other methods.
</quote>

That proves me wrong pretty categorically. Thanks for being patient
with my ignorance :)
I don't know whether Joe will talk about this unmanaged GDI++ stuff, note
that Windows Forms has some more (unmanaged) thread magic going on under
the covers, especially when you are calling into OLE stuff or AX controls
(like Dialogs) from a wrong (non STA) thread.

Right. I'd be surprised if he left it out entirely - but we'll have to
see. It's definitely on my "must buy" list.
 
W

Willy Denoyette [MVP]

Jon Skeet said:
Let me clarify - because I now believe I was wrong, but I may also have
put it badly before :)

Maybe I wasn't clear when explaining ;-)
I thought it would be uninitialized during Main, but on the call to
StartUI (decorated with the STAThread attribute) it would become
initialized as STA.
A decorated Main (On a pre-SP1) gets initialized (STA or MTA) just before
the CLR calls Main. A non-decorated Main gets MTA initialized by Forms code
(when calling into COM).
On SP1 the CLR initializes the thread quite a bit before calling Main. A
non-decorated Main enters the MTA, a decorated Main gets initialized
depending it's attribute value. In short, on SP1 you are certain that your
Main function is initialized on entry.
Right. I'd be surprised if he left it out entirely - but we'll have to
see. It's definitely on my "must buy" list.

Lets hope it doesn't take that that long or we will have to write something
ourselves :), anyway I have it on my list too.

Willy.
 

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