difference between STA and MTA

J

Jazper Manto

hi

i read thousands of millions of articles about STA and MTA :). everything
very theoretical and i don't get it...
could anybody explain me the [STAThread] and [MTAThread] on a nice, small
and simple example where i really can see the difference between these two
methods...

they're all talking about 2 message loops and com interop... but what
benefit does i get when using either STA or MTA?

thanx, jazper
 
R

Richard Blewett [DevelopMentor]

Wow thousands of millions is alot of articles ;-)

The reason articles refer to COM interop when they talk about STAThread and MTAThread is because those attributes only have any relevance when you are performing COM interop. They have no impact whatsoever when no COM interop occurs.

So do you understand STAs and MTA in COM terms?

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

hi

i read thousands of millions of articles about STA and MTA :). everything
very theoretical and i don't get it...
could anybody explain me the [STAThread] and [MTAThread] on a nice, small
and simple example where i really can see the difference between these two
methods...

they're all talking about 2 message loops and com interop... but what
benefit does i get when using either STA or MTA?

thanx, jazper
 
J

Joakim Karlsson

As Richard said, STA's and MTA's are artifacts of COM.

In the early days of 16 bit Windows there was only one thread in all of
the operating system, so no one developing COM-objects then needed to
worry about if their code was called from multiple threads. No one
needed synchronization objects such as events and mutexes. Good times
all around :)

When Win32 came along you suddenly found yourself with all these threads
that could potentially call into your nice piece of code that was only
designed to be called from one and only one thread. What to do? There
had been a lot of COM-objects developed for 16-bit Windows that was used
in thousands of millions of applications (;)) and it was not feasible to
just throw them away because we now finally could do multitasking.

The solution was apartments. COM applications in 16-bit Windows already
had to perform some initialization prior to begin using COM, they had to
call the method CoInitialize(). So here was a way to identify
COM-applications that don't know anything about threading. If you call
CoInitialize you don't know what threads are and if you have created an
object on a thread, you assume that all calls to that object come in on
the same thread. Cool!

The requirement that you need to initialize COM before you could use any
of it was kept, but now you had to perform that initialization on every
thread that was about to use or create a COM object. Those applications
that was designed with threading in mind could state that by calling a
new method, CoInitializeEx, and state if they allowed calls coming in
from any thread or from just the one thread.

Any thread that calls CoInitialize() is said to enter the Single
Threaded Apartment (STA). In that apartment there can live only one
thread. If someone calls an object that is created on a thread in an STA
from another thread, the COM-runtime is said to marshal the code from
one thread to the other. This is accomplished by posting windows
messages to the STA thread. Windows messages will be put in the threads
message queue, which gives the thread a chance to handle them nice and
easy one by one. This is why a thread in an STA must contain a message pump.

A thread can call CoInitializeEx and state that the code here can handle
calls from multiple threads, that it has all the mutexes and critical
sections or whatever in check. These threads are said to enter the Multi
Threaded Apartment. An object created by a thread in the MTA can be
called directly by code on any other thread that is also in the MTA. No
marshalling and windows messaging voodoo necessary.

Pheew!

So when a .NET application is about to call a COM-object via interop, it
checks the current threads apartment state, which can either be MTA,
STA, or unknown. If the threads apartment state is STA, CoInitialize()
is called before interop is performed. If MTA or Unknown is set,
CoInitializeEx() is called with the multithreaded parameter. This
enables the COM-runtime to use marshalling where needed and skip it
where it is not needed.

The thread's apartment state can be set by using the
Threading.ApartmentState property for any thread or by applying the
STAThread or MTAThread to the application's Main method.

Jeeesh, what a long post! There are a lot more details to it, but I
think this covers the basics.

Regards,
Joakim

Wow thousands of millions is alot of articles ;-)

The reason articles refer to COM interop when they talk about STAThread and MTAThread is because those attributes only have any relevance when you are performing COM interop. They have no impact whatsoever when no COM interop occurs.

So do you understand STAs and MTA in COM terms?

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

hi

i read thousands of millions of articles about STA and MTA :). everything
very theoretical and i don't get it...
could anybody explain me the [STAThread] and [MTAThread] on a nice, small
and simple example where i really can see the difference between these two
methods...

they're all talking about 2 message loops and com interop... but what
benefit does i get when using either STA or MTA?

thanx, jazper
 
W

Willy Denoyette [MVP]

A few mistakes....
There's never been a version of COM for 16 bit windows. So there have never
been 16 bit applications based on COM.

Willy.

Joakim Karlsson said:
As Richard said, STA's and MTA's are artifacts of COM.

In the early days of 16 bit Windows there was only one thread in all of
the operating system, so no one developing COM-objects then needed to
worry about if their code was called from multiple threads. No one needed
synchronization objects such as events and mutexes. Good times all around
:)

When Win32 came along you suddenly found yourself with all these threads
that could potentially call into your nice piece of code that was only
designed to be called from one and only one thread. What to do? There had
been a lot of COM-objects developed for 16-bit Windows that was used in
thousands of millions of applications (;)) and it was not feasible to just
throw them away because we now finally could do multitasking.

The solution was apartments. COM applications in 16-bit Windows already
had to perform some initialization prior to begin using COM, they had to
call the method CoInitialize(). So here was a way to identify
COM-applications that don't know anything about threading. If you call
CoInitialize you don't know what threads are and if you have created an
object on a thread, you assume that all calls to that object come in on
the same thread. Cool!

The requirement that you need to initialize COM before you could use any
of it was kept, but now you had to perform that initialization on every
thread that was about to use or create a COM object. Those applications
that was designed with threading in mind could state that by calling a new
method, CoInitializeEx, and state if they allowed calls coming in from any
thread or from just the one thread.

Any thread that calls CoInitialize() is said to enter the Single Threaded
Apartment (STA). In that apartment there can live only one thread. If
someone calls an object that is created on a thread in an STA from another
thread, the COM-runtime is said to marshal the code from one thread to the
other. This is accomplished by posting windows messages to the STA thread.
Windows messages will be put in the threads message queue, which gives the
thread a chance to handle them nice and easy one by one. This is why a
thread in an STA must contain a message pump.

A thread can call CoInitializeEx and state that the code here can handle
calls from multiple threads, that it has all the mutexes and critical
sections or whatever in check. These threads are said to enter the Multi
Threaded Apartment. An object created by a thread in the MTA can be called
directly by code on any other thread that is also in the MTA. No
marshalling and windows messaging voodoo necessary.

Pheew!

So when a .NET application is about to call a COM-object via interop, it
checks the current threads apartment state, which can either be MTA, STA,
or unknown. If the threads apartment state is STA, CoInitialize() is
called before interop is performed. If MTA or Unknown is set,
CoInitializeEx() is called with the multithreaded parameter. This enables
the COM-runtime to use marshalling where needed and skip it where it is
not needed.

The thread's apartment state can be set by using the
Threading.ApartmentState property for any thread or by applying the
STAThread or MTAThread to the application's Main method.

Jeeesh, what a long post! There are a lot more details to it, but I think
this covers the basics.

Regards,
Joakim

Wow thousands of millions is alot of articles ;-)

The reason articles refer to COM interop when they talk about STAThread
and MTAThread is because those attributes only have any relevance when
you are performing COM interop. They have no impact whatsoever when no
COM interop occurs.

So do you understand STAs and MTA in COM terms?

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

hi
i read thousands of millions of articles about STA and MTA :).
everything
very theoretical and i don't get it...
could anybody explain me the [STAThread] and [MTAThread] on a nice,
small
and simple example where i really can see the difference between these
two
methods...
they're all talking about 2 message loops and com interop... but what
benefit does i get when using either STA or MTA?
thanx, jazper
 
R

Richard Blewett [DevelopMentor]

So the crucial thing is which apartment type should you choose for your code.

If you are doing Windows Forms programming *always* mark the main thread with the STAThread attribute. Some controls are wrappers around ActiveX controls and so COM interop with be being used. ActiveX controls are all STA based and IIRC don't support being hosted from an MTA thread.

Any other project you can omit the attribute unless it performs COM interop where you should really take control of your thread's apartment state. You want to make sure the thread enters the same typoe of apartment as the COMM object it is calling. There is a 1000 fold performance penalty for calling cross apartment compared with intra apartment calls

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

As Richard said, STA's and MTA's are artifacts of COM. <snip: Joakim's excellent description of apartments omitted from brevity>
 
R

Richard Blewett [DevelopMentor]

Actually there was a 16 bit version of COM - is was mostly a bit of an oddity but it did exist (you can dig out various articles from the 90s that mention it).

Regards

Richard Blewett - DevelopMentor
http://ww.dotnetconsult.co.uk/weblog
http://ww.dotnetconsult.co.uk

A few mistakes....
There's never been a version of COM for 16 bit windows. So there have never
been 16 bit applications based on COM.

Willy.
 
J

Joakim Karlsson

OLE1 (and IIRC 16-bit OLE2) was around for 16-bit Windows. OLE1 actually
used DDE for it's communication if memory serves me.

But you are correct, I did take a few liberties in my post. What I
wanted to do was set the stage for why the STAThread and MTAThread are
there. There are much better articles and books if one wants to get in
to the details.

Regards,
Joakim
 
J

Jazper Manto

hi joakim

wow, thats a large post... thank you for your answer.

you said:
Any thread that calls CoInitialize() is said to enter the Single
Threaded Apartment (STA). In that apartment there can live only one
thread. If someone calls an object that is created on a thread in an STA
from another thread, the COM-runtime is said to marshal the code from
one thread to the other. This is accomplished by posting windows
messages to the STA thread. Windows messages will be put in the threads
message queue, which gives the thread a chance to handle them nice and
easy one by one. This is why a thread in an STA must contain a message pump.
A thread can call CoInitializeEx and state that the code here can handle
calls from multiple threads, that it has all the mutexes and critical
sections or whatever in check. These threads are said to enter the Multi
Threaded Apartment. An object created by a thread in the MTA can be
called directly by code on any other thread that is also in the MTA. No
marshalling and windows messaging voodoo necessary.


hmmm... i'm thinking..... hmmm... i didn't get all. sometimes i think my
brain is just a few sizes too small to get that. :)
you (or MS) said that apps which calls CoInitialize() are some kind of
"marked" as STA. however i can call the CreateThread() API and creates new
threads in my c++ app wheater i call CoInitialize() or CoInitializeEx() or
just nothing...

i wrote a c++ example... maybe when i understand it there, i'll understand
it in c# :) i uses CoInitialize() so my app will be STA right? however i
create some threads and start them. inside the threads i can use Semaphore
or Mutex (named or unnamed) to synch them. so why is my app STA? i created
several threads in it... it must by multithreaded and uses com...?

--- CODE ----------------
void threadfunc()
{
// using Semaphore or Mutex (named or unnamed) to synch the threads
//do something like DB query over COM (ado)
}

void main()
{
::CoInitialize();
for(int i=0; i < 10; i++)
{
DWORD dwThreadId = -1;
HANDLE hThread = ::CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)threadfunc, NULL, 0, &dwThreadId);
}
::CoUnInitialize();
}
 
J

Joakim Karlsson

Jazper said:
hmmm... i'm thinking..... hmmm... i didn't get all. sometimes i think my
brain is just a few sizes too small to get that. :)
you (or MS) said that apps which calls CoInitialize() are some kind of
"marked" as STA. however i can call the CreateThread() API and creates new
threads in my c++ app wheater i call CoInitialize() or CoInitializeEx() or
just nothing...

The call to CoInitialize(Ex) determines what apartment the calling
*thread* will live in. CoI(Ex) does not initialize any application wide
settings or anything.

In your code snippet the threadFunc method must call CoI(Ex) before it
uses any COM objects. The fact that the main thread has called
CoIinitialize() just puts the main thread in STA - not the worker thread.

And sure, you can create threads all you want without calling CoI(Ex),
but as soon you need to use a COM-object you need to have put the thread
creating the object in an apartment.
i wrote a c++ example... maybe when i understand it there, i'll understand
it in c# :) i uses CoInitialize() so my app will be STA right? however i
create some threads and start them. inside the threads i can use Semaphore
or Mutex (named or unnamed) to synch them. so why is my app STA? i created
several threads in it... it must by multithreaded and uses com...?

Your code should then actually look something like this:

void thread()
{
// Put this thread in a new STA
::CoInitialize(0);

// Create and use som COM-objects.

::CoUnitialize();
}

void main()
{
for(int i=0; i < 10; i++)
{
// Start threads
}
}

Your worker threads has now told the runtime what apartment they should
live in.

Now, the implementor of the COM-object you are using has declared what
kind of threading scenarios it can handle. The COM-object can be
declared as 'I must live in an STA' or 'I must live in an MTA' or 'I can
live in both'.

When your thread wants to create an object the runtime checks what
apartment your thread lives in and compares that with the wishes of the
COM-object. If the object in question can live in the same type of
apartment as your thread, the code in your thread will get a direct
pointer to the newly created COM-object. If the apartments between your
thread and that of the COM-object mismatch, the code in your thread will
receive a pointer to a proxy instead. The proxy is responsible for
marshalling your calls to the COM-object.

Regards,
Joakim
 
J

Jazper Manto

In your code snippet the threadFunc method must call CoI(Ex) before it
uses any COM objects. The fact that the main thread has called
CoIinitialize() just puts the main thread in STA - not the worker thread.

aha. so my app can be multithreaded but is marked as STA for com
communication?? am i right to say STA and MTA are just some kind of marker
only for COM communication.
but what benefit will my app get when using
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
or
::CoInitializeEx(NULL, COINIT_MULTITHREADED);

- do i have to deal in another way with COM when using MTA instead of STA?
- does the behavior in my app changes in some way?
- do i have other or even new possibilities using MTA with com?

could you mention a concrete example when i would use MTA and when STA with
COM...

regards, jazper
 
R

Richard Blewett [DevelopMentor]

"Applications" are not STA or MTA. *Threads* elect which kind of apartment they want to enter by the call to CoInitialize(Ex).

If a thread elects to join the MTA then it lives with all the other threads that are in the MTA. If it elects to be in an STA then it gets its own encapsulated environment where it is the only thread operating.

Objects state which kind of apartment they wish to live in. They do this via the ThreadingModel registry value under the InprocServer32 key. If they elect to live in the MTA they can be accessed by any MTA thread directly at any time so they have to be threadsafe. If they elect to live in an STA then they neter an encapsulated environment where only one particular thread will access them.

So putting these two together (I'm purposefully ignoring the Neutral apartment as it doesn't really contribute much to this discussion).

If an MTA thread creates an MTA object it gets direct access to the object with no marshalling
If an STA thread creates an STA object it gets locaded into the threads STA and theh thread gets direct access to the object with no marshalling
If an STA thread creats an MTA object then they live in separate apartments and a call from the STA thread must be marshalled to an MTA thread so the object can be invoked
If an MTA thread creates an STA the object gets loaded into a special STA called the Host STA. ALl calls from the MTA thread must get marshalled on to the host STA's thread so teh object can be invoked

HTH

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

hi joakim

wow, thats a large post... thank you for your answer.
 
J

Jazper Manto

oh, i see. with MTA/STA you just defines in which "room" your thread is
located. with MTA the thread is going to the "public room" and with the STA
the thread is going to his "own room" where just he and his STA child
threads can speak together... correct? so the difference between STA and MTA
is just the "speaking" with each other in the form of a message pump?

if thats correct, i'm close to understand it. however i can hardly think of
any explicit example where i can or have to use MTA then?
an app which opens and controls the Excel (for example) can be in STA and i
can communicate with the created object without any problems.

can you tell me an example where i can see the use of it?
thanx very much,
regards, jazper
 
J

Joakim Karlsson

Jazper said:
oh, i see. with MTA/STA you just defines in which "room" your thread is
located. with MTA the thread is going to the "public room" and with the STA
the thread is going to his "own room" where just he and his STA child
threads can speak together... correct? so the difference between STA and MTA
is just the "speaking" with each other in the form of a message pump?

Close, the analogy of the MTA as a public room is a good one, change
that to a 'public apartment' and you get the naming. However, there can
only be one thread per STA. Communication between threads that each
live in STA's must still be marshalled. Every thread that calls
CoInitializeEx(COINIT_MULTITHREADED) ends up in the same apartment and
can communicate with each other directly. Every thread that calls
CoInitialize() or CoInitialize(COINIT_APARTMENTTHREADED), which is the
same thing, ends up in a *new* STA. All cross apartment communication
must be marshalled and this includes communication between different STA's.
if thats correct, i'm close to understand it. however i can hardly think of
any explicit example where i can or have to use MTA then?
an app which opens and controls the Excel (for example) can be in STA and i
can communicate with the created object without any problems.

If you are using an object that is marked as being able to live in the
MTA, and you want to use that object from multiple threads, you will be
able to use that object without having to marshal every call to and from
that object, and, as Richard said, the performance gain is anything but
trivial.

Some objects may even be marked as MTA-only, in which case you have to
create them from another MTA thread if you don't want marshalling to get
in the way.
 
J

Jazper Manto

Some objects may even be marked as MTA-only, in which case you have to
create them from another MTA thread if you don't want marshalling to get
in the way.

thank you again for your quick and good explained answer!

so the communication between public appartment and the threads own
appartment is called marshalling. as far as i understand is marshalling like
a telephone between own appartment and public appartment. the only
difference between public and own apparment is that it costs performance to
"call" to the other "room". or the other thing you said is that some objects
are marked as MTA only and can not talk to the phone to communicate with the
private appartment. so you have to create some kind of a dragoman in the
public appartment which can speak directly to that object and give answer to
the private appartment... is that analogy correct?

if yes, here my last question to understand the situation:
you said, when i have a thread in the STA which wants to communicate with an
object in the MTA (not marked as MTA only!), it has to be marshalled... who
is doing that marshalling between the appartments? the compiler, the OS or
do i have to do that on my own? are there any windows APIs which i can do
the that, if i have to do the marshalling on my own?

regards, jazper
 
R

Richard Blewett [DevelopMentor]

Couple of things.

There is no concept of a "public apartment". Every thread must enter an apartment when it performs COM work - thats what the call to CoInitialize(Ex) is for. So a thread initializes itself into its own STA or it joins the proces-wide MTA.

COM types declare their apartment type via their ThreadingModel value under the InprocServer32 key in the registry this can take the following values

Apartment - the objects will be created in an STA
Free - the object will be created in the MTA
Neutral - the object will be created in the neutral apartment (we can probab ly forget about this one for this discussion)
Both - the object will be created in the apartment of the thread that creates the object (STA or MTA)
<absent> if this value is not there the object will be created in the first STA created in the process (known as the Main STA)

So lets look at a few scenarios:

STA thread creates STA object: the object is created in the threads STA, there is no marshalling and all calls to the object are made on the STA thread.
MTA thread creates MTA object: the object is created in teh MTA, there is no marshalling and the objects methos run on random threads
STA thread creates MTA object: The object is created in the MTA and the thread receives a proxy to the object. All calls are marshalled between the two threads via the proxy and COM infrastructure.
MTA thread creates STA object: The object is created in a special STA called the Host STA and the thread receives a proxy to the object. All alls are marshalled between an MTA thread and the Host STA thread.

Exactly what the marshalling entails depends on what type of apartment transitionis being executed, but assuming the object is not in the neutral apartment it always involves at least two thread switches.

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk
Some objects may even be marked as MTA-only, in which case you have to
create them from another MTA thread if you don't want marshalling to get
in the way.

thank you again for your quick and good explained answer!

so the communication between public appartment and the threads own
appartment is called marshalling. as far as i understand is marshalling like
a telephone between own appartment and public appartment. the only
difference between public and own apparment is that it costs performance to
"call" to the other "room". or the other thing you said is that some objects
are marked as MTA only and can not talk to the phone to communicate with the
private appartment. so you have to create some kind of a dragoman in the
public appartment which can speak directly to that object and give answer to
the private appartment... is that analogy correct?

if yes, here my last question to understand the situation:
you said, when i have a thread in the STA which wants to communicate with an
object in the MTA (not marked as MTA only!), it has to be marshalled... who
is doing that marshalling between the appartments? the compiler, the OS or
do i have to do that on my own? are there any windows APIs which i can do
the that, if i have to do the marshalling on my own?

regards, jazper
 
W

Willy Denoyette [MVP]

OLE1 was not based on COM, that is it did not follow the COM binary
standards. And more importantly, it did not have a notion of apartments as
there where even no threads on 16 bit windows.

Willy.
 
W

Willy Denoyette [MVP]

Richard,

The only thing I know which looks like a 16 bit version of COM is OLE1 and
OLE2, but as far as I know these puppies where developed by the Office team
and do not follow the COM specifications released back in 1993.
The Windows 3.1 and 3.11 OLE2 system component was not apartment/thread
(obviously the OS had no threads) aware, CoInitialize did not exists
(OleInitialize did). Reasons for me not to consider this as a 16 bit COM.

Willy.
Note: that the later completely redesigned OLE32 was COM based.
 
J

Jazper Manto

There is no concept of a "public apartment". Every thread must enter an
apartment when it performs COM work - thats what the call to
CoInitialize(Ex) is for. So a thread initializes itself into its own STA or
it joins the proces-wide MTA.

it was just an analogy to understand the difference.
so the communication between the 2 appartment models is called marshalling.
who is doing that marshalling between the appartments? the compiler, the OS
or do i have to do that on my own? are there any windows APIs which i can do
the that, if i have to do the marshalling on my own?

BTW:
on nearly every post i made i asked for a concrete example... (i mean an
example i would face in a real application).
is there no concrete example i can see why this all is good for? if not i
wonder why this is needed then...
please give me 1 example... only one concrete example... please... :)

regards, jazper
 
J

Joakim Karlsson

I am sure you are correct, but these kind of details dos not help the
original poster whatsoever. I never stated that apartments were around
on 16-bit Windows, actually I said the opposite.

Regards,
Joakim
 
R

Richard Blewett [DevelopMentor]

OK concrete examples:

If you are writing an application that needs to utilize a legacy VB6 component you need to be aware of apartment issues. If the VB6 component has a chatty interface say using properties (as often VB6 components would as their whole world was in the same STA in general) then if you don't set the calling thread to also be STA based (say by using the STAThread attribute) then you may well notice a performance impac then you try to use the component (I've also seen a performance hit when using classic ADO - which by default is apartment threaded IIRC - from an MTA thread in terms of perfomance as you have to cross the apartment boundary for every field access of every row in a recordset.

If you are writing a GUI application that uses ActiveX controls then unless your UI thread is STA based it will fail to work correctly.

As far as MTA based components go, it was fairly rare to make a component MTA based as once you had gone to the trouble to make your component threadsafe (which was required for MTA components there was often no hard in also letting the component run in an STA if it had to (there were issues if you issued blocking calls such as WaitForSingleObject in an STA however) so often componants taht could run in the MTA were often marked with a threading model of "Both" so they would be used efficiently by any thread.

Who performs the marshalling? In most situations the COM runtime does. At least if the thread that creates an object is the one calling that object then the COM runtime handles the marshalling. The only issue comes when you want to hand a refernence to a COM object to a thread other than the one that created it. In this case the interface pointer must be marshalled manually using either CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream of the Global Interface Table

Regards

Richatrd Blewett - DevelopMentor
http://www.dotnetconsuolt.co.uk/weblog
http://www.dotnetconsuolt.co.uk
There is no concept of a "public apartment". Every thread must enter an
apartment when it performs COM work - thats what the call to
CoInitialize(Ex) is for. So a thread initializes itself into its own STA or
it joins the proces-wide MTA.

it was just an analogy to understand the difference.
so the communication between the 2 appartment models is called marshalling.
who is doing that marshalling between the appartments? the compiler, the OS
or do i have to do that on my own? are there any windows APIs which i can do
the that, if i have to do the marshalling on my own?

BTW:
on nearly every post i made i asked for a concrete example... (i mean an
example i would face in a real application).
is there no concrete example i can see why this all is good for? if not i
wonder why this is needed then...
please give me 1 example... only one concrete example... please... :)

regards, jazper



--
No virus found in this incoming message.
Checked by AVG Anti-Virus.
Version: 7.0.300 / Virus Database: 265.6.13 - Release Date: 16/01/2005



[microsoft.public.dotnet.languages.csharp]
 

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