The threading models in COM provide the mechanism for components that usedifferent threading architectures to work together. They also providesynchronization services to components that require them. For example, aparticular object may be designed to be called only by a single thread andmay not synchronize concurrent calls from clients. If such an object iscalled concurrently by multiple threads, it crashes or causes errors. COMprovides the mechanisms for dealing with this interoperability of threadingarchitectures.
Even thread-aware components often need synchronization services. Forexample, components that have a graphical user interface (GUI), such asOLE/ActiveX controls, in-place active embeddings, and ActiveX documents,require synchronization and serialization of COM calls and window messages.COM provides these synchronization services so these components can bewritten without complex synchronization code.
An "apartment" has several inter-related aspects. First, it is a logicalconstruct for thinking about concurrency, such as how threads relate to aset of COM objects. Second, it is a set of rules programmers must obey toreceive the concurrency behavior they expect from COM environment. Finally,it is system-supplied code that helps programmers manage thread concurrencywith respect to COM objects.
The term "apartment" comes from a metaphor in which a process is conceivedas a totally discrete entity, such as a "building" that is subdivided intoa set of related but different "locales" called "apartments." An apartmentis a "logical container" that creates an association between objects and,in some cases, threads. Threads are not apartments, although there may be asingle thread logically associated with an apartment in the STA model.Objects are not apartments, although every object is associated with oneand only one apartment. But apartments are more than just a logicalconstruct; their rules describe the behavior of the COM system. If therules of the apartment models are not followed, COM objects will notfunction properly.
A Single-threaded apartment (STA) is a set of COM objects associated with aparticular thread. These objects are associated with the apartment by beingcreated by the thread or, more precisely, being first exposed to the COMsystem (typically by marshaling) on the thread. AN STA is considered aplace where an object or a proxy "lives." If the object or proxy needs tobe accessed by another apartment (in the same or a different process), itsinterface pointer must be marshaled to that apartment where a new proxy iscreated. If the rules of the apartment model are followed, no direct callsfrom other threads in the same process are allowed on that object; thatwould violate the rule that all objects within a given apartment run on asingle thread. The rule exists because most code running in an STA willfail to function properly if run on additional threads.
The thread associated with an STA must call CoInitialize orCoInitializeEx(NULL, COINIT_APARTMENTTHREADED) and must retrieve anddispatch window messages for the associated objects to receive incomingcalls. COM dispatches and synchronizes calls to objects in an STA usingwindow messages, as described later in this article.
The "main STA" is the thread that calls CoInitialize orCoInitializeEx(NULL,COINIT_APARTMENTTHREADED) first within a given process.The main STA of a process must remain alive until all COM work is completedbecause some in-proc objects are always loaded in the main STA, asdescribed later in this article.
Windows NT 4.0 and DCOM95 introduce a new type of apartment called themulti-threaded apartment (MTA). An MTA is a set of COM objects associatedwith a set of threads in the process such that any thread can call anyobject implementation directly without the interposition of system code.Interface pointers to any object in the MTA may be passed among the threadsassociated with the MTA without having to be marshaled. All threads in theprocess that call CoInitializeEx(NULL, COINIT_MULTITHREADED) are associatedwith the MTA. Unlike the STA described above, the threads in an MTA do notneed to retrieve and dispatch window messages for the associated objects toreceive incoming calls. COM does not synchronize calls to objects in anMTA. Objects in an MTA must protect their internal state from corruption bythe interaction of multiple simultaneous threads and they cannot make anyassumptions about the content of Thread-Local Storage remaining constantbetween different method invocations.
A process can have any number of STAs, but, at most, can have one MTA. TheMTA consists of one or more threads. STAs have one thread each. A threadbelongs, at most, to one apartment. Objects belong to one and only oneapartment. Interface pointers should always be marshaled between apartments(although the result of marshaling may be a direct pointer rather than aproxy). See information below on CoCreateFreeThreadedMarshaler.
A process chooses one of the threading models provided by COM. An STA modelprocess has one or more STAs and does not have an MTA. An MTA model processhas one MTA with one or more threads and does not have any STAs. A mixed-model process has one MTA and any number of STAs.
Single-threaded Apartment Model
The thread of an STA must call CoInitialize or CoInitializeEx(NULL,COINIT_APARTMENTTHREADED) and must retrieve and dispatch window messagesbecause COM uses window messages to synchronize and dispatch the deliveryof calls to an object in this model. See the REFERENCES section below formore information.
Server that Supports STA Model:
In the STA model, calls to an object are synchronized by COM in the samemanner as window messages posted to a window are synchronized. Calls aredelivered using window messages to the thread that created the object.Consequently, the object's thread must call Get/PeekMessage andDispatchMessage to receive calls. COM creates a hidden window associatedwith each STA. A call to an object from outside the STA is transferred byCOM runtime to the object's thread using a window message posted to thishidden window. When the thread associated with the object's STA retrievesand dispatches the message, the window procedure for the hidden window,also implemented by COM, receives it. The window procedure is used by theCOM runtime to "hook" the thread associated with the STA because the COMruntime is on both sides of the call from the COM-owned thread to the STA'sthread. The COM runtime (now running in the STA's thread) calls "up" via aCOM-supplied stub into the corresponding interface method of the object.The execution path returning from the method call reverses the "up" call;the call returns down to the stub and the COM runtime, which passes controlback to the COM runtime thread via a window message, which then returnsthrough the COM channel to the original caller.
When multiple clients call an STA object, the calls are automaticallyqueued in the message queue by the transfer of control mechanism used inthe STA. The object receives a call each time its STA retrieves anddispatches messages. Because the calls are synchronized by COM in thismanner, and because the calls are always delivered on the single threadassociated with the object's STA, the object's interface implementations donot need to provide synchronization.NOTE
: The object can be re-entered if an interface methodimplementation retrieves and dispatches messages while processing a methodcall, causing another call to be delivered to the object by the same STA. Acommon way in which this occurs is if an STA object makes an out-going(cross-apartment/cross-process) call using COM. This is identical to themanner in which a window procedure can be re-entered if it retrieves anddispatches messages while processing a message. COM does not prevent re-entrance on the same thread but does prevent concurrent execution. Italso provides a means by which COM-related reentrancy can be managed. Seethe REFERENCES section below for more information. The object is not re-entered if method implementations do not call out of its apartment orotherwise retrieve and dispatch messages.
Client Responsibilities in the STA Model:
Client code running in a process and/or thread that uses the STA model mustmarshal interfaces of an object between apartments by usingCoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream.For example, if Apartment 1 in the client has an interface pointer, andApartment 2 requires use of it, Apartment 1 must marshal the interfaceusing CoMarshalInterThreadInterfaceInStream. The stream object returned bythis function is thread-safe and its interface pointer should be stored ina direct memory variable accessible by Apartment 2. Apartment 2 must passthis stream interface to CoGetInterfaceAndReleaseStream to unmarshal theinterface on the underlying object and get back a pointer to a proxythrough which it can access the object.
The main apartment of a given process should remain alive until the clienthas completed all COM work because some in-proc objects are loaded in themain-apartment. (More information is detailed below).
Multi-threaded Apartment Model
An MTA is the collection of objects created or exposed by all the threadsin the process that have called CoInitializeEx(NULL, COINIT_MULTITHREADED).NOTE
: Current implementations of COM allow a thread that does notexplicitly initialize COM to be a part of the MTA. A thread that does notinitialize COM is part of the MTA only if it starts using COM after atleast one other thread in the process has previously calledCoInitializeEx(NULL, COINIT_MULTITHREADED). (It is even possible that COMitself may have initialized the MTA when no client thread has explicitlydone so; for example, a thread associated with an STA callsCoGetClassObject/CoCreateInstance[Ex] on a CLSID that is marked"ThreadingModel=Free" and COM implicitly creates an MTA into which theclass object is loaded.) See the information on threading modelinteroperability below.
However, this is a configuration that might cause problems, such asaccess violations, under certain circumstances. Therefore, it is stronglyrecommended that each thread that needs to do COM work initialize COM bycalling CoInitializeEx and then, on completion of COM work, callCoUninitialize. The cost of "unnecessarily" initializing an MTA is minimal.
MTA threads do not need to retrieve and dispatch messages because COM doesnot use window messages in this model to deliver calls to an object.
Server that Supports the MTA Model:
In the MTA model, calls to an object are not synchronized by COM. Multipleclients can concurrently call an object that supports this model ondifferent threads, and the object must provide synchronization in itsinterface/method implementations using synchronization objects such asevents, mutexes, semaphores, etc. MTA objects can receive concurrent callsfrom multiple out-of-process clients through a pool of COM-created threadsbelonging to the object's process. MTA objects can receive concurrent callsfrom multiple in-process clients on multiple threads associated with theMTA.
Client Responsibilities in the MTA Model:
Client code running in a process and/or thread that uses the MTA model doesnot have to marshal interface pointers of an object between itself andother MTA threads. Rather, one MTA thread can use an interface pointerobtained from another MTA thread as a direct memory pointer. When a clientthread makes a call to an out-of-process object, it suspends until the callhas completed. Calls may arrive on objects associated with the MTA whileall application-created threads associated with the MTA are blocked on out-going calls. In this case and in general, incoming calls are delivered onthreads provided by the COM runtime. Message filters (IMessageFilter) arenot available for use in the MTA model.
A process that supports the mixed-threading model will use one MTA and oneor more STAs. Interface pointers must be marshaled between all apartmentsbut can be used without marshaling within the MTA. Calls to objects in anSTA are synchronized by COM to run on only one thread while calls toobjects in the MTA are not. However, calls from an STA to an MTA normallygo through system-provided code and switch from the STA thread to an MTAthread before being delivered to the object.NOTE
: See the SDK documentation on CoCreateFreeThreadedMarshaler() and thediscussion of that API below for information on cases where direct pointerscan be used, and how an STA thread can call directly into an object firstassociated with the MTA and, vice versa, from multiple apartments.
Choosing the Threading Model
A component can choose to support the STA model, the MTA model, or acombination of the two by using the mixed-threading model. For example, anobject that does extensive I/O can choose to support MTA to provide maximumresponse to clients by allowing interface calls to be made during I/Olatency. Alternatively, an object that interacts with the user almostalways chooses to support STA to synchronize incoming COM calls with itsGUI operations. Supporting the STA model is easier because COM providessynchronization. Supporting the MTA model is more difficult because theobject must implement synchronization, but response to clients is betterbecause synchronization is used for smaller sections of code, rather thanfor the whole interface call as provided by COM.
The STA model is also used by Microsoft Transaction Server (MTS, previouslycode-named "Viper"), and thus DLL-based objects planning to run within theMTS environment should use the STA model. Objects implemented for the MTAmodel will normally work fine in an MTS environment. However, they will runless efficiently because they will be using unnecessary threadsynchronization primitives.
Marking the Supported-threading Model of In-Proc Servers
A thread uses the MTA model if it calls CoInitializeEx(NULL,COINIT_MULTITHREADED) or uses COM without initializing it. A thread usesthe STA model if it calls CoInitialize or CoInitializeEx(NULL,COINIT_APARTMENTTHREADED).
The CoInitialize APIs provide apartment control for client code and forobjects that are packaged in .EXEs, because the COM runtime's startup codecan initialize COM in the desired fashion.
However, an in-proc (DLL-based) COM server does not callCoInitialize/CoInitializeEx because those APIs will have been called by thetime the DLL server is loaded. Therefore, a DLL server must use theregistry to inform COM of the threading model it supports so that COM canensure that the system operates in a way that is compatible with it. Anamed value of the component's CLSID\InprocServer32 key calledThreadingModel is used for this purpose as follows:
- ThreadingModel value not present: Supports single-threading model.
- ThreadingModel=Apartment : Supports STA model.
- ThreadingModel=Both : Supports STA and MTA model.
- ThreadingModel=Free : Supports only MTA.
: ThreadingModel is a named value, not a subkey of InprocServer32 asincorrectly documented in some earlier versions of the Win32 documentation.
Threading models of in-proc servers are discussed later in this article. Ifan in-proc server provides many types of objects (each with its own uniqueCLSID), each type can have a different ThreadingModel value. In otherwords, the threading model is per CLSID, not per code package/DLL. However,the API entry points necessary to "bootstrap" and query all in-procservers (DLLGetClassObject(), DLLCanUnloadNow()) must be thread-safe forany in-proc server that supports multiple threads (meaning a ThreadingModelvalue of Apartment, Both, or Free).
As previously stated, out-of-process servers do not mark themselves usingthe ThreadingModel value. Rather, they use CoInitialize or CoInitializeEx.DLL-based servers that expect to run out-of-process using the "surrogate"functionality of COM (such as the system-supplied surrogate DLLHOST.EXE)simply follow the rules for DLL-based servers; there are no specialconsiderations in that case.
When the Client and Object Use Different Threading Models
Interaction between a client and an out-of-process object is straightforward even when different threading models are used because the clientand object are in different processes and COM is involved in passing callsfrom the client to the object. Because COM is interposed between the clientand the server, it provides the code for interoperation of the threadingmodels. For example, if an STA object is called concurrently by multipleSTA or MTA clients, COM synchronizes the calls by placing correspondingwindow messages in the server's message queue. The object's STA receivesone call each time it retrieves and dispatches messages. All combinationsof threading-model interoperability are allowed and fully supported betweenclients and out-of-process objects.
Interaction between a client and an in-proc object that uses differentthreading models is more complicated. Although the server is in-proc, COMmust interpose itself between the client and the object in some cases. Forexample, an in-proc object designed to support the STA model may be calledconcurrently by multiple threads of a client. COM cannot allow the clientthreads to directly access the object's interface because the object is notdesigned for such concurrent access. Instead, COM must ensure that callsare synchronized and made only by the thread associated with the STA that"contains" the object. Despite the added complexity, all combinations ofthreading-model interoperability are allowed between clients and in-procobjects.
Threading Models in Out-of-Process (EXE-based) Servers
Following are three categories of out-of-process servers, each of which canbe used by any COM client regardless of the threading model used by thatclient:
- STA Model Server:
The server does COM work in one or more STAs. Incoming calls are synchronized by COM and delivered by the thread associated with the STA in which the object was created. Class-factory method calls are delivered by the thread associated with the STA that registered the class factory. The object and the class factory do not need to implement synchronization. However, the implementor must synchronize access to any global variables used by multiple STAs. The server must use CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStreamto marshal interface pointers, possibly from other servers, between STAs. The server optionally can implement IMessageFilter in each STA to control aspects of call delivery by COM. A degenerate case is the single-threading model server that does COM work in one STA.
- MTA Model Server:
The server does COM work in one or more threads, all of which belong to the MTA. Calls are not synchronized by COM. COM creates a pool of threads in the server process, and a client call is delivered by any of these threads. Threads do not need to retrieve and dispatch messages. The object and class factory must implement synchronization. The server does not need to marshal interface pointers between threads.
- Mixed Model Server:
See the section in this article titled "Mixed-threading model" for more information.
Threading Models in Clients
There are three categories of clients:
- STA model client:
The client does COM work in threads associated with one or more STAs. The client must use CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream to marshal interface pointers between STAs. A degenerate case is the single-threading model client that does COM work in one STA. The client's thread enters a COM provided message loop when it makes an outgoing call. The client can use IMessageFilter to manage callbacks and processing of window messages while waiting on out-going calls and other concurrency issues.
- MTA model client:
The client does COM work in one or more threads, all of which belong to the MTA. The client does not need to marshal interface pointers between its threads. The client cannot use IMessageFilter. The client's threads suspend when they makes a COM call to an out-of-process object and resumes when the call returns. Incoming calls arrive on COM-created and managed threads.
- Mixed model client:
See the section in this article titled "Mixed-threading model" for more information.
Threading Models in In-proc (DLL-based) Servers:
There are four categories of in-proc servers, each of which can be used byany COM client regardless of the threading model used by that client.However, in-proc servers must provide marshaling code for any custom (non-system-defined) interface they implement if they are to support threadingmodel interoperability because that typically requires that COM marshal theinterface between client apartments. The four categories are:
- In-proc Server that supports single threading ("main" STA)- no ThreadingModel value:
An object provided by this server expects to be accessed by the same client STA by which it was created. In addition, the server expects all its entry points, such as DllGetClassObject and DllCanUnloadNow, and global data to be accessed by the same thread (the one associated with the main STA). Servers that existed before the introduction of multi- threading in COM are in this category. These servers are not designed to be accessed by multiple threads so COM creates all objects provided by the server in the main STA of the process and calls to the objects are delivered by the thread associated with the main STA. Other client apartments gain access to the object through proxies. Calls from the other apartments go from the proxy to the stub in the main STA (inter- thread marshaling) and then to the object. This marshaling allows COM to synchronize calls to the object and calls are delivered by the STA in which the object was created. Inter-thread marshaling is slow relative to direct calling so it is recommended that these servers be rewritten to support multiple STAs (category 2).
- In-proc Server that supports the single-threaded apartment model (multiple STAs) - marked with ThreadingModel=Apartment:
An object provided by this server expects to be accessed by the same client STA by which it was created. Therefore, it is similar to an object provided by a single-threaded in-proc server. However, objects provided by this server can be created in multiple STAs of the process, so the server must design its entry points, such as DllGetClassObject and DllCanUnloadNow, and global data for multi-threaded use. For example, if two STAs of a process create two instances of the in-proc object simultaneously, DllGetClassObject may be called concurrently by both STAs. Similarly, DllCanUnloadNow must be written so the server is protected from being unloaded while code is still executing in the server.
If the server provides only one instance of the class factory to create all the objects, the class factory implementation must also be designed for multi-threaded use because it is accessed by multiple client STAs. If the server creates a new instance of the class factory each time DllGetClassObject is called, the class factory does not need to be thread-safe. However, the implementor must synchronize access to any global variables.
COM objects created by the class factory do not need to be thread-safe. However, access of global variables must be synchronized by the implementor. Once created by a thread, the object is always accessed through that thread and all calls to the object are synchronized by COM. Client apartments that are different than the STA in which the object was created must access the object through proxies. These proxies are created when the client marshals the interface between its apartments.
Any client that creates an STA object through its class factory gets a direct pointer to the object. This is different than single-threaded in-proc objects, where only the main STA of the client gets a direct pointer to the object and all other STAs that create the object gain access to the object through a proxy. Because inter-thread marshaling is slow relative to direct calling, speed can be significantly improved by changing a single-threaded in-proc server to support multiple STAs.
- In-proc Server that supports only MTA - marked with ThreadingModel=Free:
An object provided by this server is safe for the MTA only. It implements its own synchronization and is accessed by multiple client threads at the same time. This server may have behavior that is incompatible with the STA model. (For example, by its use of the Windows message queue in a way that breaks the message pump of an STA.) Also, by marking the threading model of the object as "Free," the implementor of the object is stating the following: this object can be called from any client thread, but this object also can pass interface pointers directly (without marshaling) to any threads that it created and these threads can make calls through these pointers. Thus, if the client passes an interface pointer to a client-implemented object (such as a sink) to this object, it can elect to call back through this interface pointer from any thread that it created. If the client is an STA, a direct call from a thread which is different from the thread that created the sink object will be in error (as demonstrated in 2 above). Hence COM always ensures that clients in threads associated with an STA gain access to this kind of in-proc object only through a proxy. Also, these objects should not aggregate with the free-threaded marshaler because that allows them to run directly on STA threads.
- In-proc Server that supports apartment model and free-threading - marked with ThreadingModel=Both:
An object provided by this server implements its own synchronization and is accessed concurrently by multiple client apartments. In addition, this object is created and used directly, instead of through a proxy, in STAs or the MTA of a client process. Because this object is used directly in STAs, the server must marshal interfaces of objects, possibly from other servers, between threads so its access to any object in a threading-appropriate way is guaranteed. Also, by marking the threading model of the object as "Both", the implementor of the object is stating the following: this object can be called from any client thread, but any callbacks from this object to the client will be done only on the apartment in which the object received the interface pointer to the callback object. COM allows such an object to be created directly in an STA as well as in an MTA of the client process.
Because any apartment that creates such an object always gets a direct pointer rather than a proxy pointer, ThreadingModel "Both" objects provide performance improvements over ThreadingModel "Free" objects when loaded in an STA.
Because a ThreadingModel "Both" object is also designed for MTA access (it is totally thread-safe internally), it can speed performance by aggregating with the marshaler provided by CoCreateFreeThreadedMarshaler. This system-supplied object is aggregated in to any calling objects and custom marshals direct pointers to the object into all apartments in the process. Clients in any apartment, whether an STA or MTA, may then access the object directly instead of through a proxy. For example, an STA model client creates the in-proc object in STA1 and marshals the object to STA2. If the object does not aggregate with the free-threaded marshaler, STA2 gains access to the object through a proxy. If it does, the free-threaded marshaler provides STA2 with a direct pointer to the object
NOTE: Care must be taken when aggregating with the free-threaded marshaler. As an example, assume that an object that is marked as ThreadingModel "Both" (and also aggregating with the free-threaded marshaler) has a data member that is an interface pointer to another object whose ThreadingModel is "Apartment". Then assume that an STA creates the first object and during creation, the first object creates the second object. As per the rules discussed above, the first object is now holding a direct pointer to the second object. Now assume that the STA marshals the interface pointer to the first object to another apartment. Because the first object aggregates with the free- threaded marshaler, a direct pointer to the first object is given to the second apartment. If the second apartment then calls through this pointer, and if this call causes the first object to call through the interface pointer to the second object, then an error has occurred, because the second object is not meant to be directly called from the second apartment. If the first object is holding a pointer to a proxy to the second object rather than a direct pointer, this will cause a different error. System proxies are also COM objects that are associated with one and only one apartment. They keep track of their apartment in order to avoid certain circularities. So an object calling out on a proxy associated with a different apartment than the thread on which the object is running will receive the RPC_E_WRONG_THREAD return from the proxy and the call will fail.
Threading Model Interoperability Between Clients and In-process Objects
All combinations of threading-model interoperability are allowed betweenclients and in-process objects.
COM allows all STA model clients to inter-operate with single-threadingin-proc objects by creating and accessing the object in the client's mainSTA and marshaling it to the client STA that called CoCreateInstance[Ex].
If an MTA in a client creates an STA model in-proc server, COM spins up a"host" STA in the client. This host STA creates the object and theinterface pointer is marshaled back to the MTA. Similarly, when an STAcreates an MTA in-proc server, COM spins up a host MTA in which the objectis created and marshaled back to the STA. Interoperability between thesingle-threading model and MTA model is handled similarly because thesingle-threading model is just a degenerate case of the STA model.
Marshaling code must be provided for any custom interface that an in-procserver implements if it wants to support interoperability that requires COMto marshal the interface between client apartments. See the REFERENCESsection below for more information.
Relationship Between Threading Model and Class Factory Object Returned
A precise definition of in-proc servers being "loaded into" apartments isexplained in the two following steps:
- If the DLL that contains the in-proc server class has not previously been loaded (mapped into the process address space) by the operating system loader, that operation is performed, and COM obtains the address of the DLLGetClassObject function exported by the DLL. If the DLL has previously been loaded by a thread associated with any apartment, this stage is skipped.
- COM uses the thread (or, in the case of the MTA, one of the threads) associated with the "loading" apartment to call the DllGetClassObject function exported by the DLL asking for the CLSID of the needed class. The factory object returned is then used to create instances of objects of the class.
The second step (the calling of DllGetClassObject by COM) occurs each and every time a client calls CoGetClassObject/CoCreateIntance[Ex], even from within the same apartment. In other words, DllGetClassObject may be called many times by a thread associated with the same apartment; it all depends on how many clients in that apartment are trying to get access to a class factory object for that class.
Authors of class implementations and, ultimately, the author of the DLLpackage of a given set of classes have complete freedom to decide whatfactory object to return in response to the DllGetClassObject functioncall. The author of the DLL package has the ultimate say because the code"behind" the single DLL-wide DllGetClassObject() entry point is what hasthe first and potentially final right to decide what to do. The threetypical possibilities are:
- DllGetClassObject returns a unique class factory object per calling thread (which means one class factory object per STA and potentially multiple class factories within the MTA).
- DllGetClassObject always returns the same class factory object, regardless of the identity of the calling thread or the kind of apartment associated with the calling thread.
- DllGetClassObject returns a unique class factory object per calling apartment (one per apartment in both STA and MTA).
There are other possibilities for the relationship between calls toDllGetClassObject and the class factory object actually returned (such as anew class factory object per call to DllGetClassObject) but they do notpresently appear to be particularly useful.
Summary of Client and Object Threading Models for In-Proc Servers
The following table summarizes the interaction between differentthreading models when a client thread first calls CoGetClassObject on aclass that is implemented as an in-proc server.
Kinds of clients/threads:
- client is running in a thread associated with the "main" STA (first thread to call CoInitialize or CoInitializeEx with COINIT_APARTMENTTHREADED flag)-call this STA0 (also called single- threading model).
- client is running in a thread associated with in any other STA [ASCII 150] call this STA*.
- client is running in a thread associated with in the MTA.
Kinds of DLL servers:
- Server has no ThreadingModel key -- call this "None."
- Server is marked "Apartment" -- call this "Apt."
- Server is marked "Free."
- Server is marked "Both."
When reading the table below, keep in the mind the definition made above of"loading" a server into an apartment.
Client Server Result------ ------ -----------------------------------------STA0 None Direct access; server loaded into STA0STA* None Proxy access; server loaded into STA0.MTA None Proxy access; server loaded into STA0; STA0 created automatically by COM if necessary;STA0 Apt Direct access; server loaded into STA0STA* Apt Direct access; server loaded into STA*MTA Apt Proxy access; server loaded into anSTA created automatically by COM.STA0 Free Proxy access; server is loaded into MTAMTA created automatically by COM if necessary.STA* Free Same as STA0->FreeMTA Free Direct accessSTA0 Both Direct access; server loaded into STA0STA* Both Direct access; server loaded into STA*MTA Both Direct access; server loaded into the MTA