Are ActiveX servers produced using Delphi (3 C/S with all update/patches
I have written a simple ActiveX server which has only one member function.
All data in that member function are local variables. Should I still enclose
the code in a TCriticalSection?
The server is used by a partner of ours within their ISAPI extension (using
Visual C++). The problem we get is that frequently COM throws a
E_RPC_WRONG_THREAD ("The application called an interface marshaled for a
different thread") exception
According to our partner this behaviour indicates that our ActiveX server is
not thread-safe (Threads randomly exit before completing). They had the same
behaviour with DAO objects (not thread safe) and switched to ODBC classes
(thread safe) which solved their problems.
How can I make my simple server thread-safe?
Thanks in advance
Delphi defaults to producing activex server objects that are
processed/serviced on 1 main thread in 1 single threaded apartment (STA).
Thus, 1 main thread in 1 STA guarantees thread safety for both local and
global variables so you don't need a critical section for your method
Getting E_RPC_WRONG_THREAD does not mean that your ActiveX server is not
thread-safe; in fact it has nothing to do with whether the server is thread
safe or not. It simply means that if they (the client) manipulate interface
pointers, which your Delphi server provides, across multiple threads in
SEPARATE APARTMENTS, they have to marshal that interface pointer from
apartment to apartment or else they will get the said error: "The
application called an interface marshaled for a different thread".
Again, you don't need to make your simple Delphi server thread-safe. It is
Brickhouse Data Systems, Inc
Here are a few indications for understanding COM threading model and the
error you encountered.
COM supports 3 different threading models :
- single threaded components,
- appartment threaded components,
- free threaded components.
For COM, the threading model is associated to a thread so that components
created by a thread inherit its threading model. In order to select a
threading model for a given thread you MUST call CoInitialize or
CoInitializeEx FOR EACH thread using COM (in the thread main proc).
On the one hand, when calling CoInitialize(nil) or CoInitializeEx with
apartment thread flag set, the corresponding thread is marked as apartment
which means that each call to the components it owns will be run by the
owner thread itself : only the owner thread will run its components methods
which protects them from re-entrance. This way your components don't have to
be thread safe. In this case you can't give directly an interface pointer on
a component to an other thread. The server thread must marshall the
interface and the client has to unmarshall it so that it has a valid
interface pointing on a proxy component rather than on the target component
itself. In order to achieve this you use two COM helper functions :
CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream.
On the other hand, when calling CoInitializeEx with free thread flag set,
the corresponding thread is marked as free threaded which means that the
components it creates can be run concurrently from several thread. In this
case your components MUST be thread safe and all the COM environment too
including class factories and modules exported functions (for DLLs) :
- function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult;
- function DllCanUnloadNow: HResult; stdcall;
- function DllRegisterServer: HResult; stdcall;
- function DllUnregisterServer: HResult; stdcall;
Delphi in version 3.0x (up to 3.02) only provides a convenient framework
for apartment threading model and does not support free threading model (for
example even _AddRef and _Release functions are not thread safe) . It means
that your activeX is implemented to be instanciated from an apartment thread
and DOES NOT HAVE TO BE THREAD SAFE (no CriticalSection or other synchro
object are required). Furthermore, it means that when an other thread has
to call one of its interface member function it MUST get through a
marshalling / unmarshalling process to get a valid interface pointer
otherwise it fires an E_RPC_WRONG_THREAD error.
Now let's go a step further in COM threading model discussion and look at
local and remote servers versus in-proc servers.
A Delphi local or remote exe server has a main thread which calls
CoInitialize which makes it an apartment. And as a default behaviour, every
COM component based on the delphi COM framework is created in this thread
and marked as apartment threaded. If you explicitly spawn new threads you
can initialize them as you want but you must get a marshalled interface to
call components created by your main thread.
If you want to create free threaded components it's far more difficult since
you can't rely directly on Delphi implementation and you have to rewrite
large parts of ComServ and subclass many of ComObj classes. I'm currently
working on this topic and I'll post more on this when I am over with tests.
From an in-proc server point of view (a DLL) you generaly don't handle
threads since you borrow the thread of your host exe except if you spawn a
thread dedicated to one instance of your components (which is the case of
apartment threaded factory posted in this newsgroup by Borland). So in this
case you should indicate in the registry which threading model your
component is designed for (see in registry key named ThreadingModel under
So for the problem you reported it is up to your partner to manage its
threads so that other threads than the thread which created your activeX get
a valid interface on your component through use of standard COM marshaling
feature since the component you expose supports apartment threading. Let's
also state that even the factory is an apartment threaded component which
must be handle with the same care !
For ending this discussion let's say that apartment threading model is
Microsoft recommended threading model for all internet extensions such as
MTS or other MS features.
I hope this can help you.
John Sotiropoulos a écrit dans le message
>Are ActiveX servers produced using Delphi (3 C/S with all update/patches
>applied) thread-safe? ...
I for one will be waiting anxiously for your post/article regarding writing
free-threaded COM servers in Delphi. I believe it is sorely needed for any
- Sultan Rehman
Further to this, is Delphi still OK for the apartment model if I create a
TForm from a COM Server? I'm trying to write a namespace extension and the
IShellView interface demands that I create a window.
Thanks for your (excellent) help.
These threads need to access these variables. Should they be treated in a
critical section of thread code?
>Further to this, is Delphi still OK for the apartment model if I create a
>TForm from a COM Server? I'm trying to write a namespace extension and the
>IShellView interface demands that I create a window.
There's no problem for using a Delphi form in a STA context, provided you
serialize all display stuff through use of synchronize. VCL is not reentrant
and when VCL resources are shared among several threads, display calls
should be synchronized.
Patrice CORTEEL - pcor...@itecor.com
Information Technology Software
30, quai de Dion Bouton
92806 PUTEAUX CEDEX
>What about global variables in the process that contains multi-STA threads?
>These threads need to access these variables. Should they be treated in a
>critical section of thread code?
They should or I'd rather say they must. Indeed, these global variables
belong to Delphi world and are not protected by COM apartment mechanism so
that there handling should be protected by critical sections.
You "might" run into some real obscure problems (such as access violations)
if the thread that handles your UI is the same thread that handles your STA
objects. In this case, the problem comes from the fact that COM serializes
method calls into the STA thread using the same message loop that your UI
is using and it is possible, for instance, that your object may be created
even before your form gets to completely initialize itself; if this happens
and say in your object's Initialize code, you try to access a form which
has not been fully initialized, you could possibly be accessing the form/or
controls on the form that aren't created yet.
Just something you should be aware of.
Brickhouse Data Systems, Inc
> Thank you for that detailed message - it really helps me with a problem I
> have here.
> Further to this, is Delphi still OK for the apartment model if I create a
> TForm from a COM Server? I'm trying to write a namespace extension and
> IShellView interface demands that I create a window.
If I understand correctly then the message pump is handled by Explorer so I
don't need to worry about serialiseing message handling on the form?
Thanks for your help,