Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: creation and usage of a ComServer within several threads in an ISAPI



Permlink Replies: 12 - Last Post: Sep 21, 2015 2:35 AM Last Post By: Mathias Pannier Threads: [ Previous | Next ]
Mathias Pannier

Posts: 38
Registered: 4/12/01
creation and usage of a ComServer within several threads in an ISAPI
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 9, 2015 6:17 AM
I'm working on a web server application which I can use as an ISAPI DLL in IIS or as a self hosting console application via TIdHTTPWebBrokerBridge.
Within this application I will use a ComServer (exe). The creation and the start (of the process) of this ComServer is not very fast. So I created a pool. If a ComServer is needed the client asks the pool. If a ComServer (within the pool) is "free", then it is used. If the pool is empty or all ComServers are in use it will create a new one.
I know the usage of a ComServer (interface) across several threads is not allowed and I must use CoMarshalInterThreadInterfaceInStream.
But my problem is that I have no main thread which creates a pool with 10 or more ComServer. The ComServer is created if needed (within one thread). How to use CoMarshalInterThreadInterfaceInStream in this scenario?

A simple example to reproduce an error message that describe my problem:

1. have a global variable to a ComServer (interface)
2. create a thread; in the execute method create the ComServer and use it
3. terminate the thread
4. use the ComServer (from 1) again (without new creation)
-> EOleException Object is not connected to the server

An other example:

1. have an empty pool of ComServer (interface)
2. create thread 1 and use a ComServer (interface) from the pool (within the pool the ComServer is created)
3. terminate the thread
4. create a new thread 2 and use a ComServer (interface) from the pool (the ComServer from 2. is free and can be used)
-> only sometimes I get an exception; but I know this is not allowed

In my ISAPI DLL and in my self hosting console application I did not know which thread creates the ComServer and I don't know if this thread is still alive or which thread is the next which will use the ComServer.

I hope I could describe my problem and I hope someone has any suggestions?

Regards
Mathias Pannier
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: creation and usage of a ComServer within several threads in an ISAPI
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 9, 2015 7:58 AM   in response to: Mathias Pannier in response to: Mathias Pannier
Mathias wrote:

I know the usage of a ComServer (interface) across several threads is
not allowed and I must use CoMarshalInterThreadInterfaceInStream.

That depends on the COM object's threading model. Multi-threaded/Free-Threaded
COM objects can be used across thread/apartment boundaries without marshaling.

Also, IGlobalInterfaceTable is a bit easier to use than CoMarshalInterThreadInterfaceInStream().

But my problem is that I have no main thread which creates a pool with
10 or more ComServer. The ComServer is created if needed (within one
thread).

Why can't you spawn the pool thread in the same thread that is activating
the ISAPI server?

1. have a global variable to a ComServer (interface)

2. create a thread; in the execute method create the ComServer and use it

3. terminate the thread

That is not a pooling model. And you can't use a global in that scenario,
as that will be a race condition if you start multiple threads. The variable
needs to be local to the thread that uses it.

4. use the ComServer (from 1) again (without new creation)
-> EOleException Object is not connected to the server

If the COM object is Apartment threaded, it is tied to the thread that creates
it.

1. have an empty pool of ComServer (interface)

2. create thread 1 and use a ComServer (interface) from the pool
(within the pool the ComServer is created)

3. terminate the thread

4. create a new thread 2 and use a ComServer (interface) from the pool
(the ComServer from 2. is free and can be used)

-> only sometimes I get an exception; but I know this is not allowed

It is if you marshal the COM object. The thread that creates the COM objects
can use IGlobalInterfaceTable or CoMarshalInterThreadInterfaceInStream(),
and the thread that pulls out the COM object can use IGlobalInterfaceTable
or CoGetInterfaceAndReleaseStream() to receive it.

In my ISAPI DLL and in my self hosting console application I did not
know which thread creates the ComServer and I don't know if this
thread is still alive or which thread is the next which will use the
ComServer.

If you are going to use a pool, you have to keep the creating thread alive
for the lifetime of the pool. In this scenario, IGlobalInterfaceTable is
easier to use, as it handles with thread boundaries for you. With CoMarshalInterThreadInterfaceInStream(),
you have to do it manually.

--
Remy Lebeau (TeamB)
Mathias Pannier

Posts: 38
Registered: 4/12/01
Re: creation and usage of a ComServer within several threads in an ISAPI
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 10, 2015 12:53 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Hello,

thank You for Your reply.

Remy Lebeau (TeamB) wrote:
Mathias wrote:

I know the usage of a ComServer (interface) across several threads is
not allowed and I must use CoMarshalInterThreadInterfaceInStream.

That depends on the COM object's threading model. Multi-threaded/Free-Threaded
COM objects can be used across thread/apartment boundaries without marshaling.

The ComServer I use has the following "signature":

TAutoObjectFactory.Create(ComServer, TBVClient, Class_BVClient,
ciSingleInstance, tmApartment);

Internally the ComServer is not thread safe.

Also, IGlobalInterfaceTable is a bit easier to use than CoMarshalInterThreadInterfaceInStream().

I did not heard / read about IGlobalInterfaceTable. How does it works/how can I use it?

But my problem is that I have no main thread which creates a pool with
10 or more ComServer. The ComServer is created if needed (within one
thread).

Why can't you spawn the pool thread in the same thread that is activating
the ISAPI server?

I'm not sure if I understand Your question correctly. My Pool is a global var. It is created in the initialization section of the unit (in the main thread?). The first Time the pool is empty. The items of the pool are dynamically created and used within the thread which work off the current request. Did You mean I schould create the pool with X items and only use this items (no auto grow)?

1. have a global variable to a ComServer (interface)

2. create a thread; in the execute method create the ComServer and use it

3. terminate the thread

That is not a pooling model. And you can't use a global in that scenario,
as that will be a race condition if you start multiple threads. The variable
needs to be local to the thread that uses it.

You are right. It is only a simple example to illustrate my problem.

4. use the ComServer (from 1) again (without new creation)
-> EOleException Object is not connected to the server

If the COM object is Apartment threaded, it is tied to the thread that creates
it.

It is Apartment threaded. So I have no chance to use it in any other thread?

1. have an empty pool of ComServer (interface)

2. create thread 1 and use a ComServer (interface) from the pool
(within the pool the ComServer is created)

3. terminate the thread

4. create a new thread 2 and use a ComServer (interface) from the pool
(the ComServer from 2. is free and can be used)

-> only sometimes I get an exception; but I know this is not allowed

It is if you marshal the COM object. The thread that creates the COM objects
can use IGlobalInterfaceTable or CoMarshalInterThreadInterfaceInStream(),
and the thread that pulls out the COM object can use IGlobalInterfaceTable
or CoGetInterfaceAndReleaseStream() to receive it.

Ok. The pool has no direct interface to the ComServer but only the Pointer from CoMarshalInterThreadInterfaceInStream, right?

In my ISAPI DLL and in my self hosting console application I did not
know which thread creates the ComServer and I don't know if this
thread is still alive or which thread is the next which will use the
ComServer.

If you are going to use a pool, you have to keep the creating thread alive
for the lifetime of the pool. In this scenario, IGlobalInterfaceTable is
easier to use, as it handles with thread boundaries for you. With CoMarshalInterThreadInterfaceInStream(),
you have to do it manually.

As write above the pool and his creator thread is alive, but the creator thread of the items in the pool are not. Example with the console application using TIdHTTPWebBrokerBridge:
For each request TIdHTTPWebBrokerBridge creates a working thread. Within this thread the request can be worked off. Within this thread I ask the pool. The first time the pool is empty. A new item (interface to ComServer) is created and used. After that the thread is terminated. The next request will spawen a new thread. Within this new thread I ask the pool. In the pool is one free item that can be used. But this item was created by an other thread and this thread was terminated.

Edited by: Mathias Pannier on Sep 10, 2015 12:53 AM
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: creation and usage of a ComServer within several threads in anISAPI [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 10, 2015 1:29 PM   in response to: Mathias Pannier in response to: Mathias Pannier
Mathias wrote:

The ComServer I use has the following "signature":

TAutoObjectFactory.Create(ComServer, TBVClient, Class_BVClient,
ciSingleInstance, tmApartment);

Internally the ComServer is not thread safe.

Apartment-threaded COM objects need to be marshaled across apartment boundaries.

I did not heard / read about IGlobalInterfaceTable. How does it
works/how can I use it?

IGlobalInterfaceTable interface
https://msdn.microsoft.com/en-us/library/windows/desktop/ms678517.aspx

Accessing Interfaces Across Apartments
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682353.aspx

In a nutshell, after you create an instance of the COM object, instantiate
the IGlobalInterfaceTable interface via CoCreateInstance() and put the COM
object into it via the IGlobalInterfaceTable.RegisterInterfaceInGlobal()
method. A simple DWORD cookie is returned, which you can store wherever
you want. After that, any thread that wants to use the COM object can instantiate
the IGlobalInterfaceTable interface and call the IGlobalInterfaceTable.GetInterfaceFromGlobal()
method, passing it the cookie. Appropriate marshaling is handled automatically,
and when that thread is done using the COM object then it can simply Release()
the COM object normally. When all threads have finished using the COM object
and you are ready to remove it from the table, any thread can call the IGlobalInterfaceTable.RevokeInterfaceFromGlobal()
method, passing it the cookie.

I'm not sure if I understand Your question correctly. My Pool is a
global var. It is created in the initialization section of the unit
(in the main thread?).

I probably would not start the pool thread there. Bu whatever, you have
a thread running by the time the ISAPI threads start running.

The first Time the pool is empty. The items of the pool are dynamically
created and used within the thread which work off the current request.

That is fine. The pool thread can keep a list of registered cookies, and
a list of available cookies.

Whenever a new COM object instance is created, register it in the IGlobalInterfaceTable
and put the returned cookie in the registered list and available list.

When another thread wants a COM object from the pool, look in the available
list first. If a cookie is not available yet, either wait a short time for
one to become available, or else create a new COM object (if your pool max
has not been reached yet) and put it in the pool. Either way, once you can
grab an available cookie, call IGlobalInterfaceTable.GetInterfaceFromGlobal()
and move on. When the thread is done using the COM object, Release() it
and put its cookie back in the available list.

When the pool thread is terminated, it can revoke all of the registered cookies.

Did You mean I schould create the pool with X items and only use this
items (no auto grow)?

You can certainly create the pool with an initial number of COM objects.
And you can certainly create objects on an as-needed basis, too. Maybe
have the pool thread create a small number of objects initially, and then
monitor the available list and create new objects in the background until
the registered list reaches a max count. Either way, just be sure not to
grow the pool too high. Do set a reasonable max. When a threads needs a
COM object and none are available, it should either wait for a COM object
to become available, or fail its current operation and try again later.

It is Apartment threaded. So I have no chance to use it in any other
thread?

You CAN use it, you just have to marshal it.

Ok. The pool has no direct interface to the ComServer but only the
Pointer from CoMarshalInterThreadInterfaceInStream, right?

The pool should be the one creating the objects in the first place. Other
threads simply go to the pool to retreive existing objects from the pool,
and put the objects back into the pool when finished.

For each request TIdHTTPWebBrokerBridge creates a working thread.
Within this thread the request can be worked off. Within this thread I
ask the pool. The first time the pool is empty. A new item (interface
to ComServer) is created and used. After that the thread is
terminated. The next request will spawen a new thread. Within this new
thread I ask the pool. In the pool is one free item that can be used.
But this item was created by an other thread and this thread was
terminated.

Then don't create the object in the context of the request thread to begin
with. Create the object in the context of the pool thread instead, and
have the request thread only retreive an object from the pool, use it,
and then put it back into the pool.

This is a good example of a scenario where IGlobalInterfaceTable will make
your life much easier than CoMarshalInterThreadInterfaceInStream(). However,
it is possible is use CoMarshalInterThreadInterfaceInStream() instead.
When the pool thread makes a COM object available, it can call CoMarshalInterThreadInterfaceInStream()
and store the resulting IStream in the available list. When a thread wants
an object, it retreives an available IStream and calls CoGetInterfaceAndReleaseStream(),
uses the COM object as needed, Release()'s it, and signals the pool thread
that the object is done. The pool thread can then check for that signal
and re-marshal the original COM object with CoMarshalInterThreadInterfaceInStream()
and store the new IStream in the available list.

--
Remy Lebeau (TeamB)
Mathias Pannier

Posts: 38
Registered: 4/12/01
Re: creation and usage of a ComServer within several threads in anISAPI [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 11, 2015 1:20 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Hello,

thank You very much for Your reply

Am 10.09.2015 22:29, schrieb Remy Lebeau (TeamB):
Then don't*create* the object in the context of the request thread to begin
with.Create the object in the context of the pool thread instead, and
have the request thread only*retreive* an object from the pool, use it,
and then*put it back* into the pool.

How? That means I have to send a message to the pool thread?

I have made a small example. Where can I upload that example with the
needed binaries?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: creation and usage of a ComServer within several threads in anISAPI[Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 11, 2015 11:32 AM   in response to: Mathias Pannier in response to: Mathias Pannier
Mathias wrote:

How? That means I have to send a message to the pool thread?

Please re-read my last reply more carefully. I outlined what you need to
do. I intentionally left out implementation details, because there are different
ways to implement the particular steps, depending on your actual needs.

I have made a small example. Where can I upload that example
with the needed binaries?

Don't post the binaries, just the source code. You can do that here. If
you really want to post binaries (which I don't think will be helpful), you
can post them in the Attachments forum on this server.

--
Remy Lebeau (TeamB)
Mathias Pannier

Posts: 38
Registered: 4/12/01
Re: creation and usage of a ComServer within several threads in anISAPI[Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 14, 2015 1:43 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Don't post the binaries, just the source code. You can do that here. If
you really want to post binaries (which I don't think will be helpful), you
can post them in the Attachments forum on this server.

I mean the binaries of the used ComServer and the complete source code
of my example. Here is only the source of my WebModuleUnit (maybe it is
enough):

unit WebModuleUnit1;

interface

uses System.SysUtils, System.Classes, Web.HTTPApp,
Pooling, //from Arcana Technologies
BlockViewer_TLB; //My ComServer

type
TWebModule1 = class(TWebModule)
procedure WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
private
public
end;

TBlockViewerPoolHelper = class(TObject)
public
Cookie: Cardinal;
end;

TGlobal = class(TObject)
private
FBlockViewerPool: TObjectPool; //Pooling.pas from Arcana Technologies
procedure DoOnCreateBlockViewer(Sender : TObject; var AObject :
TObject);
procedure DoOnDestroyBlockViewer(Sender : TObject; var AObject :
TObject);
public
constructor Create;
destructor Destroy; override;
function AcquireBlockViewer(var aOutBlockViewerPoolHelper:
TBlockViewerPoolHelper): TBVClient;
procedure ReleaseBlockViewer(aItem : TBVClient;
aBlockViewerPoolHelper: TBlockViewerPoolHelper);
end;

var
WebModuleClass: TComponentClass = TWebModule1;
Global: TGlobal;

const
CLSID_StdGlobalInterfaceTable : TGUID =
'{00000323-0000-0000-C000-000000000046}';

implementation

uses
Variants, ActiveX, Graphics;

{$R *.dfm}

function StreamToVariant(Stream: TStream): OleVariant;
var
p: Pointer;
begin
Result := VarArrayCreate([0, Stream.Size - 1], varByte);
p := VarArrayLock(Result);
try
Stream.Position := 0;
Stream.Read(p^, Stream.Size);
finally
VarArrayUnlock(Result);
end;
end;

procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
aBlockViewer: TBVClient;
aBlockViewerPoolHelper: TBlockViewerPoolHelper;
aStream: TMemoryStream;
ABitmap: TBitmap;
OLE_Stream: OLEVariant;
aDummy: WideString;
begin
aBlockViewer := Global.AcquireBlockViewer(aBlockViewerPoolHelper);
try
aStream := TMemoryStream.Create;
try
ABitmap := TBitmap.Create;
try
ABitmap.Width := 100;
ABitmap.Height := 100;
ABitmap.PixelFormat := pf24bit;
ABitmap.SaveToStream(AStream);
AStream.Position := 0;
OLE_Stream := StreamToVariant(AStream);

aBlockViewer.DoBlockViewer(aDummy, aDummy, OLE_Stream);
//do something with OLE_Stream ...
finally
ABitmap.Free;
end;
finally
aStream.Free;
end;
finally
Global.ReleaseBlockViewer(aBlockViewer, aBlockViewerPoolHelper);
end;

Response.Content :=
'<html><heading/><body>Webserver-Anwendung</body></html>';
end;

{ TGlobal }

constructor TGlobal.Create;
begin
inherited;
FBlockViewerPool := TObjectPool.Create;
FBlockViewerPool.PoolSize := 0; //if I start with 1 It works until
two concurrent pageviews (which creates a new item in thread)
FBlockViewerPool.AutoGrow := true;
FBlockViewerPool.OnCreateObject := DoOnCreateBlockViewer;
FBlockViewerPool.OnDestroyObject := DoOnDestroyBlockViewer;
FBlockViewerPool.Start;
end;

destructor TGlobal.Destroy;
begin
FBlockViewerPool.Stop;
FBlockViewerPool.Free;
inherited;
end;

procedure TGlobal.DoOnCreateBlockViewer(Sender: TObject; var AObject:
TObject);
var
GIT: IGlobalInterfaceTable;
BlockViewerIntf: IBVClient;
Cookie: Cardinal;
begin
CoCreateInstance(CLSID_StdGlobalInterfaceTable, nil, CLSCTX_ALL,
IGlobalInterfaceTable, GIT);
BlockViewerIntf := CoBVClient.Create;
GIT.RegisterInterfaceInGlobal(BlockViewerIntf, IID_IBVClient, Cookie);
GIT := nil;

AObject := TBlockViewerPoolHelper.Create;
TBlockViewerPoolHelper(AObject).Cookie := Cookie;
end;

procedure TGlobal.DoOnDestroyBlockViewer(Sender: TObject; var AObject:
TObject);
var
GIT: IGlobalInterfaceTable;
begin
CoCreateInstance(CLSID_StdGlobalInterfaceTable, nil, CLSCTX_ALL,
IGlobalInterfaceTable, GIT);
GIT.RevokeInterfaceFromGlobal(TBlockViewerPoolHelper(AObject).Cookie);
GIT:= nil;

AObject.Free;
end;

function TGlobal.AcquireBlockViewer(
var aOutBlockViewerPoolHelper: TBlockViewerPoolHelper): TBVClient;
var
GIT: IGlobalInterfaceTable;
BlockViewerIntf: IBVClient;
begin
CoInitialize(nil);
aOutBlockViewerPoolHelper :=
TBlockViewerPoolHelper(FBlockViewerPool.Acquire);

CoCreateInstance(CLSID_StdGlobalInterfaceTable, nil, CLSCTX_ALL,
IGlobalInterfaceTable, GIT);
GIT.GetInterfaceFromGlobal(aOutBlockViewerPoolHelper.Cookie,
IID_IBVClient, BlockViewerIntf);
GIT := nil;

Result := TBVClient.Create(nil);
Result.ConnectTo(BlockViewerIntf); //--> Exception
end;

procedure TGlobal.ReleaseBlockViewer(aItem: TBVClient;
aBlockViewerPoolHelper: TBlockViewerPoolHelper);
begin
aItem.Free;

FBlockViewerPool.Release(aBlockViewerPoolHelper);
CoUninitialize;
end;

initialization
Global := TGlobal.Create;

finalization
Global.Free;

end.

Mathias Pannier

Posts: 38
Registered: 4/12/01
Re: creation and usage of a ComServer within several threads in anISAPI [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 17, 2015 12:14 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Am 10.09.2015 22:29, schrieb Remy Lebeau (TeamB):
Then don't*create* the object in the context of the request thread to begin
with.Create the object in the context of the pool thread instead, and
have the request thread only*retreive* an object from the pool, use it,
and then*put it back* into the pool.

Again. How? I think this is my main problem. See my example in my other
post.

The request thread ask the pool. If no COM object is available a new COM
object is created and added to the IGlobalInterfaceTable. But the
"create" is called within the request thread. How can my "request
thread" tell the pool to create a new COM object within it's own thread?

Thanks again
Regards
Mathias Pannier
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: creation and usage of a ComServer within several threads in anISAPI[Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 18, 2015 11:04 AM   in response to: Mathias Pannier in response to: Mathias Pannier
Mathias wrote:

The request thread ask the pool. If no COM object is available a
new COM object is created and added to the IGlobalInterfaceTable.

This is where you can run into problems.

A COM object operates in the apartment that it is created in, and dies if
that apartment goes away while the object is still alive. Which apartment
a thread is associated with is dictated by its call to CoInitialize/Ex().
Which apartment the COM object belongs to is dictated by the COM object's
threading model and the creating thread's threading model.

If a thread calls CoInitialize() or CoInitializeEx(COINIT_APARTMENTTHREADED),
it creates a new single-threaded apartment (STA) for itself, which exists
for the lifetime of that thread. If a thread calls CoInitializeEx(COINIT_MULTITHREADED)
instead, it gets associated with a single shared multi-threaded apartment
(MTA) that is global for the calling process.

If a COM object is marked as apartment-threaded, it can only be created in
an STA. If an STA thread creates the object, it is created within that thread's
local STA, and that thread does not use marshaling to access it. If an MTA
thread creates the object, a new STA is created to host the object, and the
thread uses marshaling to access it. Any other thread has to use marshaling
to access the object.

If a COM object is marked as multi/free-threaded, it will be created in the
MTA. If an MTA thread creates the object, it does not use marshaling to
access it. In fact, any MTA thread can directly access the object without
having to use marshaling, as they are all in the same MTA apartment. If
an STA creates the object, it uses marshaling to access the object.

If a COM object is marked as "both", it will adopt whether threading model
the creating thread is using, whether that be STA or MTA, and act accordingly.

If the COM object is marked as neutral-threaded (COM+ only), things get even
more comlicated, which I won't go into here.

In short, if a COM object and an accessing thread are associated with the
same apartment, the thread does not need to use marshalling to access the
COM object. Marshaling is only needed when the thread is associated with
a different apartment than the COM object. IGlobalInterfaceTable handles
that detail for you, as it knows the threading models of the object and thread.
When marshaling is needed, it returns a suitable proxy. When marshaling
is not needed, it returns the object directly.

MSDN used to have a table showing which apartment a COM object is created
in based on the threading model of the its creating thread (the table is
in COM+ documentatio, but it applies to COM as well, just ignore the neutral
portions):

Threading Model Attribute
https://web.archive.org/web/20120713071919/http://msdn.microsoft.com/en-us/library/ms681753.aspx

There is also an MSDN blog that provides some details:

What are these "Threading Models" and why do I care?
http://blogs.msdn.com/b/larryosterman/archive/2004/04/28/122240.aspx

This is why I suggest you move the creation of the COM object to a dedicated
thread that manages the objects, and then delegates/marshals the objects
via IGlobalInterfaceTable to any threads that need to access the objects.

How can my "request thread" tell the pool to create a new COM
object within it's own thread?

The pool thread has to manage that on its own. The requesting thread would
have to signal the pool thread that a COM object is needed. The pool thread
would have to detect that signal, create a new object, and make it available.
The requesting thread would then wait for that object. Now, how you do
that signal is up to you. You can use kernel events (CreateEvent()), thread
messages (PostThreadMessage()), etc.

--
Remy Lebeau (TeamB)
Mathias Pannier

Posts: 38
Registered: 4/12/01
Re: creation and usage of a ComServer within several threads in anISAPI[Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 21, 2015 2:35 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Am 18.09.2015 20:04, schrieb Remy Lebeau (TeamB):
A COM object operates in the apartment that it is created in, and dies if
that apartment goes away while the object is still alive.

This is why I suggest you move the creation of the COM object to a dedicated
thread that manages the objects, and then delegates/marshals the objects
via IGlobalInterfaceTable to any threads that need to access the objects.

How can my "request thread" tell the pool to create a new COM
object within it's own thread?

The pool thread has to manage that on its own. The requesting thread would
have to signal the pool thread that a COM object is needed. The pool thread
would have to detect that signal, create a new object, and make it available.
The requesting thread would then wait for that object. Now, how you do
that signal is up to you. You can use kernel events (CreateEvent()), thread
messages (PostThreadMessage()), etc.

Thanks a lot for Your explanation. The lines above are the solution for
my problem. (I think I have understood it, now.)
Mathias Pannier

Posts: 38
Registered: 4/12/01
Re: creation and usage of a ComServer within several threads in anISAPI [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 17, 2015 12:33 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Am 10.09.2015 22:29, schrieb Remy Lebeau (TeamB):
I'm not sure if I understand Your question correctly. My Pool is a
global var. It is created in the initialization section of the unit
(in the main thread?).
I probably would not start the pool thread there. Bu whatever, you have
a thread running by the time the ISAPI threads start running.

Where is the place in my Delphi WebBroker ISAPI Pproject to start/create
my pool?

- There is the dpr file with begin/end
- There is my WebModul class (which is create/freed by each request)
- There is the WebModul Unit (outside of the class) where I can declare
a var and use the initialization section
Mathias Pannier

Posts: 38
Registered: 4/12/01
Re: creation and usage of a ComServer within several threads in an ISAPI
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 10, 2015 2:22 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
I have found an example of how to use IGlobalInterfaceTable: https://stackoverflow.com/questions/5595152/using-twordapplication-with-threads
After reading this post I have only the "cookie" in my pool. Two problems/questions:

1. The answer from David Heffernan confuses me: You can't do this. All the calls to the Word COM interfaces have be made from the thread that created the object.-> Which prerequisites needs the ComServer to be used within my scenario? (I did not use Word)

2. I get an AV if I call ConnectTo(MyInterface) within the thread. MyCallStack:

System.Win.ComObj.InterfaceConnect(nil,(10734424, 0, 0, (200, 247, 122, 5, 152, 247, 122, 5)),TServerEventDispatch($3960BE4) as IInterface,60452116)
Vcl.OleServer.TOleServer.ConnectEvents(???)
:008962f2 TOleServer.ConnectEvents + $1A

MyComServer has some events. Did I have to use the events interface in IGlobalInterfaceTable? If yes, how can I connect my Object to this events?

Regards
M. Pannier
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: creation and usage of a ComServer within several threads in anISAPI
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 10, 2015 1:54 PM   in response to: Mathias Pannier in response to: Mathias Pannier
Mathias wrote:

1. The answer from David Heffernan confuses me: You can't do this.

He is wrong, and I have commented as such on his answer.

All the calls to the Word COM interfaces have be made from the thread
that created the object.

That part is correct, for apartment-threaded COM objects. What David did
not take into account is that when the main thread creates the Word object
and puts it into IGlobalInterfaceTable, and then the worker thread uses IGlobalInterfaceTable
to retreive the Word object, IGlobalInterfaceTable will automatically marshal
the Word Object appropriately, and that marshaling will use a proxy that
delegates back to the main thread. In that regard, all method calls made
on the the marshaled Word object by the worker thread will actually execute
in the main thread, thus satisfying the requirement that only the thread
that creates the object can make calls into the object.

Which prerequisites needs the ComServer to be used within my scenario?
(I did not use Word)

When an apartment-threaded COM object is marshaled to another apartment,
that apartment receives a proxy, not the original object. Calls into the
proxy are delegated back to the original thread that created the COM object.
IGlobalInterfaceTable handles these details for you. All you have to worry
about is:

1. creating the object, and storing it into IGlobalInterfaceTable.

2. retreiving the object from IGlobalInterfaceTable when needed.

3. revoking the object from IGlobalInterfaceTable when you are done using
the object.

2. I get an AV if I call ConnectTo(MyInterface) within the thread.

Please show your actual code.

MyComServer has some events. Did I have to use the events interface in
IGlobalInterfaceTable? If yes, how can I connect my Object to this
events?

Since your COM object is apartment-threaded, your events interface needs
to be connected to the COM object in the same thread that creates the COM
object. You can then add the COM object into IGlobalInterfaceTable for use
in other threads. However, the events will be triggered in the context of
the thread that created the COM object. If you need to use the event data
in your other thread, you will have to pass the data around yourself as needed.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02