Board index » cppbuilder » Event Sinks

Event Sinks


2003-12-05 07:47:56 PM
cppbuilder85
Hi All,
I have a Remote Server COM object embedded into and application that runs on
a server computer (WIN2K Server), I run a client application on this server
and on a remote machine, I have many methods within the COM obect that run
fine and up until now have had few problems doing all that is necessary, now
I have the following scenario:
Client 1 tells the server that it has an alarm, this alarm is then sent
after 10 seconds to Client 2, both clients now display the the alarm, if
Client 1 selects the alarm I want to tell Client 2 to disable the ability
for its user to select that alarm, no problem, I use an event sink, I have
implemented the sink and when Client 1 selects the alarm it tells the server
that it has done so and in that method in the server I fire the
Fire_OnCallAccepted funtion, this should broadcast that the alarm is
accepted to all clients, or so I thought, the COM documentation I have read
suggests that this should happen but the Fire_OnCallAccepted funtion only
fires the event, in this case, in Client 1 and not Client 2. If Client 2
accepts the alarm Client 2 gets theFire_OnCallAccepted event.
Have I misread the documentation or am I doing something wrong, any advice
would be appreciated.
TIA
Andrew
 
 

Re:Event Sinks

"Andrew" < XXXX@XXXXX.COM >wrote in message
Quote
in that method in the server I fire the Fire_OnCallAccepted funtion,
this should broadcast that the alarm is accepted to all clients, or so
I thought, the COM documentation I have read suggests that this
should happen but the Fire_OnCallAccepted funtion only fires the
event, in this case, in Client 1 and not Client 2. If Client 2 accepts
the alarm Client 2 gets theFire_OnCallAccepted event.
In theory, yes it is supposed to broadcast to all clients.
In practice, I find that is rarely the case. The implementation for
handling sink events does not keep track of multiple clients correctly, so
that when you call a Fire_...() function, it only knows about the calling
client.
To get around the problem in my own COM client objects, I maintain a global
list of all the client instances that have been created, and then when I
need to trigger an event I iterate through my list calling the event for
each client. For example:
--- TMyClientImp.h ---
class TMyClientImp :
...
{
public:
static TThreadList* AvailableClients;
TMyClientImp();
~TMyClientImp();
STDMETHOD(DoSomething());
};
--- TMyClientImp.cpp ---
TThreadList* TMyClientImp::AvailableClients = NULL;
TMyClientImp::TMyClientImp()
{
if( !AvailableClients )
AvailableClients = new TThreadList;
AvailableClients->Add(this);
}
TMyClientImp::~TMyClientImp()
{
if( AvailableClients )
{
TList *pList = AvailableClients->LockList();
pList->Remove(this);
int count = pList->Count;
AvailableClients->UnlockList();
if( count < 1 )
{
delete AvailableClients;
AvailableClients = NULL;
}
}
}
STDMETHODIMP TMyClientImp::DoSomething()
{
TMyClientImp *pIntf;
try
{
if( AvailableClients )
{
TList *pList = AvailableClients->LockList();
try
{
for(int x = 0; x < pList->Count; ++x)
{
pIntf = static_cast<TMyClientImp*>(pList->Items);
pIntf->Fire_DidSomething();
}
}
__finally {
AvailableClients->UnlockList();
}
}
}
catch(Exception &e)
{
return Error(e.Message, IID_IMyClient);
}
return S_OK;
}
Gambit
 

Re:Event Sinks

This depends on the threading model also ... the contect is lost in Fire xxx
event.
The call appearantly goes to COM land somewhere<g>..
You could also add your own CConnectionpoint Impl that uses a Global
interface to hold the event sinks.
There is an article on MSDN and one of the VC sites. search for
'CComDynamicUnkArray_GIT' <** IIRC **>
 

{smallsort}

Re:Event Sinks

"Michael Harris" < XXXX@XXXXX.COM >wrote in message
Quote
This depends on the threading model also ...
True. In my case, my COM servers are always free-threaded, so the
marshalling is handled automatically, although I do minimize the number of
threads accessing the interfaces when triggering events from one thread to
another.
Quote
You could also add your own CConnectionpoint Impl that
uses a Global interface to hold the event sinks.
I do use connection points, actually. And the Fire_... functions do access
the connection point implementations, just not across multiple clients
correctly.
Quote
There is an article on MSDN and one of the VC sites. search for
'CComDynamicUnkArray_GIT' <** IIRC **>
That is for accessing the IGlobalInterfaceTable interface. That will handle
the marshalling of the eventsink/connectionpoint interfaces across threads
in the server, but that does not fix the original problem that the
interfaces are not being tracked correctly in the first place so that events
can be broadcasted.
Gambit
 

Re:Event Sinks

Maybe the error is somewhere else....
for example. In the previous code sample, each TMyClientImp enters a
single TThreadList (AvailableClients).
So, the obvious question is. when multiple Clients Connect to the mentioned
server, are the connectionpoints piped into a single instance of the object
responsible for event notifiction ?
didin't think so.
 

Re:Event Sinks

"Michael Harris" < XXXX@XXXXX.COM >wrote in message
Quote
In the previous code sample, each TMyClientImp enters
a single TThreadList (AvailableClients).
Correct.
Quote
So, the obvious question is. when multiple Clients Connect
to the mentioned server, are the connectionpoints piped into
a single instance of the object responsible for event notifiction ?
Each client instance has its own connection point implementation, which is
supposed to be registered into a single list inside the ATL. But it doesn't
register correctly or something, because when the Fire_...() functions are
called, although they try to loop through a list somewhere, they only
execute the loop code once - for the calling client. So it would appear
that support for broadcasting events is not implemented correctly in the ATL
itself. Thus the workaround I use to keep my own list of client instances
so that I can then broadcast manually.
Gambit
 

Re:Event Sinks

Assuming the server exe is multi-use / multi-threaded ant the server app has
an automation server or just some com class called , say 'TServerImpl'.
each client will likely start its own object of type 'TServerImpl' inside
the server for each call to CocreateInstance or TComxx::Create even though
you may be using muti-use model.(many to one).
so, only the consectutive connections called from within a single clent
process would register with any particular instance of 'TServerImpl'.
Try a little experiment when you have time. in the server exe. in
'TServerImpl' , add
DECLARE_CLASSFACTORY_SINGLETON(TServerImpl)
in the impl code you want to share across client thread.
The connectionpoints will fail (because you are using a free threaded model)
But now you can fix them by using the GIT technique previously mentioned.
Remy Lebeau (TeamB)" wrote in message
Quote

Each client instance has its own connection point implementation, which is
supposed to be registered into a single list inside the ATL. But it
doesn't
register correctly or something, because when the Fire_...() functions are
called, although they try to loop through a list somewhere, they only
execute the loop code once - for the calling client. So it would appear
that support for broadcasting events is not implemented correctly in the
ATL
itself. Thus the workaround I use to keep my own list of client instances
so that I can then broadcast manually.


Gambit


 

Re:Event Sinks

Hi,
Thanks for the replies, I'll look at those implementations.
Andrew