Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Interop / June 2005

Tip: Looking for answers? Try searching our database.

COM/threading and difference between Service and Console App

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
PJChan - 23 Jun 2005 02:40 GMT
Hi, I've got an app that starts a thread to listen on a tcp port, then starts
another thread to accept the socket connection and exchange some data with
the client. This second thread creates a COM object for internal use.
My problem is that if I test with a console app, the thread that creates the
COM object seems to go to sleep and only wakes up when I stop the thread,
however I don't have this problem if I run the app as a windows service!
Is there a reason for this to happen? I suspect the issue has to do with
threading model and the COM object (legacy library that I can't look into),
but shouldn't I be seeing the same behaviour in both cases?
What tool would you suggest I use to dig into this?
Thanks
Signature

P.J. Chan

PJChan - 23 Jun 2005 08:43 GMT
Adding a little more background:
the console app creates thread A
Thread A creates thread B and then waits on a tcp connection
Thread B creates COM object and freezes

If the console app executes ThreadA.join(5000), the creation of the COM
object succeeds and execution of thread B completes! But I don't get the same
result if I simply do Thread.sleep(5000) from the console app.

Any idea what might be going on?

Signature

P.J. Chan

> Hi, I've got an app that starts a thread to listen on a tcp port, then starts
> another thread to accept the socket connection and exchange some data with
[quoted text clipped - 7 lines]
> What tool would you suggest I use to dig into this?
> Thanks
PJChan - 23 Jun 2005 09:13 GMT
Sorry, I keep missing bits of the story which might give someone a clue as to
what's happening ...

After starting thread A, the console app waits on Console.Readline. Thread A
can receive tcp msgs and start more Thread Bs as a result, all of them
freezing when creating the same COM obj, and all remaining frozen no matter
how many times thread A wakes up from tcp waiting.

But as soon as the console app calls ThreadA.join(5000) (I do it when
Readline reads a letter "b"), all of the frozen B threads continue in
parallel.

if instead of a console app I create thread A from a windows service (and
thread A does the same as always: wait on tcp and create B for each msg
received), thread B creates the COM obj with no problem. No need to do a
join.

The only obvious difference here is that the console app is waiting for
keyboard entry, while the service is doing whatever services do after being
started. The service is responsive to Stop, which causes all threads to be
stopped.

Anyhow, this is the full story this time. I hope someone can help
rgds
P.Chan

> Adding a little more background:
> the console app creates thread A
[quoted text clipped - 18 lines]
> > What tool would you suggest I use to dig into this?
> > Thanks
Willy Denoyette [MVP] - 23 Jun 2005 21:43 GMT
1. What is the 'threadingmodel' key of the COM object in the registry?
2. What is the apartment type your threads. (MTA or STA), if you don't
explicitly initialize them to join a STA their apartmentstate will be MTA.
Now suppose the answer to 1 is (the most obvious) "apartment", that means
the object must be instantiated/accessed on a STA thread. If the console
application has the STAThread attribute set on Main, your object will live
in the STA of the thread running Main() (called the main STA). That means
also that calls from other threads must be marshaled, that means the STA
thread must pump the message queue. Console.ReadLine() doesn't pump messages
it blocks, that means that calls into your COM object will block the callers
thread.
Thread.Join() however, pumps the message queue (the CLR has limited pumping
support for some other 'blocking' waits like Join). So, that explains the
console app. behavior.
Now back to your Service, suppose again the answer to 1 is 'apartment'. A
service in general has no [STAThread] attribute on Main() (the default in
VS), that means all threads will be MTA, unless you initialize them
differently. Here the COM runtime spins up a STA thread (OLE runtime
thread), called the "host STA" thread, creates the object and marshals the
interface pointer back to the callers MTA thread and spins up a message
pump. So again this explains why it doesn't block the callers thread in a
service. However running COM objects on the host STA thread is bad, it's a
performance killer and it doesn't scale (all calls from different threads
must be marshaled and are automatically serialized).

One thing you should do to solve all these problems is to initialize your
threads as STA before starting, this way the calls into COM are all direct
calls (no marshaling/mesage pumping needed)
Willy.

> Sorry, I keep missing bits of the story which might give someone a clue as
> to
[quoted text clipped - 56 lines]
>> > What tool would you suggest I use to dig into this?
>> > Thanks
PJChan - 24 Jun 2005 03:57 GMT
Willy, thankyou very much for your prompt response; it explained perfectly
the behaviour I'm seeing. You are spot on on all the assumptions.

When you say that I should initialize threads to STA, you mean the ones that
instantiate the object, right? (thread B in my text) Or do I have to do the
same with thread A, which creates thread Bs upon receiving TCP msgs?

So I've changed thread B to start with ApartmentState  set to STA (I
verified that initial ApartmentState is unknown and then it accepts STA as
its new value).

I still get the same results; is that what you would've expected given the
way the Console apps run the main STA (in this case blocking it with a
Readline)? Or was this supposed to fix the problem with the console app as
well?

In the end I'm using the console app only for testing, as this is quicker
than testing with the service, but my real target is the service, which seems
to be working fine after the change. Is there anything I can do to verify for
curiosity's sake that the COM object is not running on the host STA anymore?

Thanks again
PJ Chan

Signature

P.J. Chan

> 1. What is the 'threadingmodel' key of the COM object in the registry?
> 2. What is the apartment type your threads. (MTA or STA), if you don't
[quoted text clipped - 86 lines]
> >> > What tool would you suggest I use to dig into this?
> >> > Thanks
Willy Denoyette [MVP] - 24 Jun 2005 11:38 GMT
You have to make sure there is no marshaling involved, that is you may not
use the COM interface from another thread than from the creator thread.

This scenario,
- Main thread creates and starts thread A, (Is your Main() attributed with
STAThread ?)
- A creates B, and initializes the thread to enter a STA before it starts B
- In B (now a STA thread) creates the COM object and only code in B accesses
it's interfaces.
should not block on lack of pumping messages.
If, however, you create an instance on B and use the interface from A, you
have to pump messages to prevent blocking.

If your Main() has the STAThread attribute, there won't be a Host-STA thread
in the process.
If there is no such attribute on Main() and you create an 'apartment' object
on the main thread or any other thread while there is not yet an STA thread
in the process, the COM object will end on the Host-STA thread.
There is a way to detect this by using a low level debugger, but if you
understand the above simple rules, there is no need to go down that path.

Willy.

> Willy, thankyou very much for your prompt response; it explained perfectly
> the behaviour I'm seeing. You are spot on on all the assumptions.
[quoted text clipped - 133 lines]
>> >> > What tool would you suggest I use to dig into this?
>> >> > Thanks
PJChan - 24 Jun 2005 13:43 GMT
Willy, yes, the console app is decorated with STAThread as you first
suspected (and the service app is not, as you also suggested).
I confirm that the Main creates A, then A creates B and sets its
ApartmentState to STA before starting it. B creates the COM obj, which is
private (to be precisse, B creates instances of various classes until one of
them creates the COM obj; all instances remain private within B).
B does use some variables that belong to an object created within A (that
object starts the thread B, and I use it to be able to pass parameters to the
B thread), but doesn't share anything back to A or the Main.
I still had the same problem, namely the console app gets blocked until I
use Join, while the service continues to work.
Can a console app be created without the STAThread decoration?

Thanks
Signature

P.J. Chan

> You have to make sure there is no marshaling involved, that is you may not
> use the COM interface from another thread than from the creator thread.
[quoted text clipped - 156 lines]
> >> >> > What tool would you suggest I use to dig into this?
> >> >> > Thanks
PJChan - 24 Jun 2005 14:09 GMT
Willy, I thought I should disclose a little more detail about the COM object
in case this may be affecting the behaviour.
The COM object is a VB6 wrapper around another COM DLL that is installed as
an MTS component. That second COM dll only works in NT, and is the legacy dll
that I ultimately need to preserve and the reason for me going through all
this trouble.
The wrapper allows me to develop and test in XP and other such versions of
Windows, then deploy to the target NT machine. Manually I tell the VB6 obj
whether it should instantiate the legacy COM (ie in production NT
environment) or just pretend it does (in non-NT test environment).
Here comes the interesting fact: the legacy COM has no Threading setting in
the registry. My understanding is that this is typical of MTS components.
Could this somehow be impacting the threading model/marshalling in my app?

Thanks once again
Signature

P.J. Chan

> Willy, yes, the console app is decorated with STAThread as you first
> suspected (and the service app is not, as you also suggested).
[quoted text clipped - 171 lines]
> > >> >> > What tool would you suggest I use to dig into this?
> > >> >> > Thanks
Willy Denoyette [MVP] - 24 Jun 2005 19:35 GMT
That's what I was afraid of (and guess I did ask about the threading model
of your COM objects).
Well, COM objects that have their "threadingmodel" empty are called single
threaded objects, all these objects live in a single STA "the host-STA"
served by a single thread.
In your console scenario the Main() procedure runs on a STA thread which
will serve as the Host-STA thread (the first thread in the process that
initialized a STA) for all the Single Threaded COM objects.

Lets look how things relate now;
Main thread -STA
Thread A - MTA
Thread B - STA creates --> VB6 STA COM object creates --> single threaded
(ST) COM object
the ST object will live in the Main STA threads apartment that means calls
must be marshaled and the Main STA thread must pump messages
(Console.ReadLine() doesn't pump , Thread.Join pumps).

This can be solved by, or
- pumping the main STA thread, you can use
System.Threading.CurrentThread.Join() instead of Console.ReadLine(), or
- by initializing the main thread as MTAThread, in this case the B thread
will become the Host STA and as it's the same thread as the COM objects
thread, no marshaling is required.

If you carefully study above, you can easily understand why it works in a
service right, and also why the change from MTA to STA for thread B didn't
change anything, right?

Willy.

> Willy, I thought I should disclose a little more detail about the COM
> object
[quoted text clipped - 250 lines]
>> > >> >> > What tool would you suggest I use to dig into this?
>> > >> >> > Thanks
PJChan - 27 Jun 2005 15:08 GMT
Willy, yes everything has become clear; thanks. I guess my last question is
whether there could be any side effects (other than things working the way I
wanted in the first place) in turning the main of the console app from STA to
MTAThread. Is there a particular reason for STA being the default?

Thanks for your help; this has been very interesting.
Signature

P.J. Chan

> That's what I was afraid of (and guess I did ask about the threading model
> of your COM objects).
[quoted text clipped - 264 lines]
> >> > >> >> > internal
> >> > >> >> > use.
Willy Denoyette [MVP] - 28 Jun 2005 12:20 GMT
For a VS C# console application the default is no attribute on Main().
For a windows application the default is [STAThread] and should remain like
this.

Willy.

> Willy, yes everything has become clear; thanks. I guess my last question
> is
[quoted text clipped - 327 lines]
>> >> > >> >> > internal
>> >> > >> >> > use.

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.