Board index » cppbuilder » Firing COM/DCOM events in automation object

Firing COM/DCOM events in automation object

I am writing an automation server with events enabled so that I can notify the client when certain things happen.  The Fire_<EventName>() method exists in several classes in the file <ProjectName>_TLB.h.  One of these classes is for the client to sink the event.  What I can't figure out is how to call the Fire method from the automation server.  I have not found any examples of this in the Borland docs or anywhere else.  Anybody know how to do this and/or where to find a good example?  Thanks!!

Dave

 

Re:Firing COM/DCOM events in automation object


I'd also like to the sample. I have the same problems.

Qim

Quote
"Dave Sampson" <dsamp...@qwest.net> wrote in message

news:3b2fccae$1_2@dnews...
Quote

> I am writing an automation server with events enabled so that I can notify

the client when certain things happen.  The Fire_<EventName>() method exists
in several classes in the file <ProjectName>_TLB.h.  One of these classes is
for the client to sink the event.  What I can't figure out is how to call
the Fire method from the automation server.  I have not found any examples
of this in the Borland docs or anywhere else.  Anybody know how to do this
and/or where to find a good example?  Thanks!!
Quote

> Dave

Re:Firing COM/DCOM events in automation object


Quote
Dave Sampson wrote:

> I am writing an automation server with events enabled so that I can notify the
> client when certain things happen.  The Fire_<EventName>() method exists in several
> classes in the file <ProjectName>_TLB.h.  One of these classes is for the client to
> sink the event.  What I can't figure out is how to call the Fire method from the
> automation server.  I have not found any examples of this in the Borland docs or
> anywhere else.  Anybody know how to do this and/or where to find a good example?  

I have the same problem. I don't know anything about COM or OLE automation,
but I was forced to add COM support to the application that I make at
my job, and I was given a very short time to do that. So I've found a
*very bad and stupid* method to solve this problem - don't follow me if you
feel you have a chance to find a normal way to solve it.

So here's my solution:

I've added 2 methods to my server: "SetupEvents" and "CleanupEvents". Every client
*must* call "SetupEvents" to start receiving events (for example, right after
Connect() call) and "CleanupEvents" before disconnecting.

In "SetupEvents" you should add the instance of T<your_server_name>Impl (this)
to some global array (or simply store it in global variable if your server can
have only one client), and in "CleanupEvents" you should delete that entry
from the global array. Then, when you need to fire the event, you need to call
"Fire_<EventName>()" for every entry in that array - all clients will receive it.

I've tested it under Win98 and it worked well, but I don't know how many bugs
it will introduce in future, so - I repeat - use it only if it's your last
hope :)

If anybody knows how to do it normally - please let me know too...

Here is the sample of my code:

// ========= in T<your_server_name>Impl.cpp ===============
STDMETHODIMP T<your_server_name>Impl::SetupEvents()
{
  ClientList.AddElement(this);
  return S_OK;

Quote
}

STDMETHODIMP T<your_server_name>Impl::CleanupEvents()
{
  ClientList.DelElement(this);
  return S_OK;

Quote
}

// ========= somewhere else ===============
class TComObjectList
{ private:
    T<your_server_name>Impl **List;
    int Count;

    int FindElement(T<your_server_name>Impl *object);
  public:
    void AddElement(T<your_server_name>Impl *object);
    void DelElement(T<your_server_name>Impl *object);

    void Fire_Test_Event(void);

    TComObjectList(void){ List=NULL; Count=0;};
    ~TComObjectList(){ if(List) delete[] List; };

Quote
};

//---------------------------------------------------------------------------
TComObjectList ClientList;

int TComObjectList::FindElement(T<your_server_name>Impl *object)
{
  if(!List) return -1;
  for(int i=0;i<Count;i++)
    if(List[i]==object)
      return i;
  return -1;

Quote
}

//---------------------------------------------------------------------------

void TComObjectList::AddElement(T<your_server_name>Impl *object)
{
  if(FindElement(object)>=0) return;

  // .....
  // add new element to array
  // .....

Quote
}

//---------------------------------------------------------------------------

void TComObjectList::DelElement(T<your_server_name>Impl *object)
{
  int index=FindElement(object);

  if(index<0) return;

  // .....
  // delete element "index" from array
  // .....

Quote
}

//---------------------------------------------------------------------------

void TComObjectList::Fire_Test_Event(void)
{
  for(int i=0;i<Count;i++)
    List[i]->Fire_TestEvent();

Quote
}

//---------------------------------------------------------------------------

Yours faithfully,
Alex Roschin
E-mail: rochi...@yahoo.com

Re:Firing COM/DCOM events in automation object


Alex,

I think your solution will work fine.

You don't need to add functions for starting to recieve events, though.  You
can create a constructor for your implementation class, and add your
instance to the global list at that time.

If you add a destructor, you can remove yourself from your list at that
time.

I used a static TList in my Implementation class to add the instances to, so
I did not need a global variable.  Of course if you need to fire events from
outside your implementation class, you may need a global variable, or some
other mechanism.

Quote
"Alex Roschin" <rochi...@yahoo.com> wrote in message

news:3B378F08.ECBCB43F@yahoo.com...
Quote
> Dave Sampson wrote:

> > I am writing an automation server with events enabled so that I can
notify the
> > client when certain things happen.  The Fire_<EventName>() method exists
in several
> > classes in the file <ProjectName>_TLB.h.  One of these classes is for
the client to
> > sink the event.  What I can't figure out is how to call the Fire method
from the
> > automation server.  I have not found any examples of this in the Borland
docs or
> > anywhere else.  Anybody know how to do this and/or where to find a good
example?

> I have the same problem. I don't know anything about COM or OLE
automation,
> but I was forced to add COM support to the application that I make at
> my job, and I was given a very short time to do that. So I've found a
> *very bad and stupid* method to solve this problem - don't follow me if
you
> feel you have a chance to find a normal way to solve it.

> So here's my solution:

> I've added 2 methods to my server: "SetupEvents" and "CleanupEvents".
Every client
> *must* call "SetupEvents" to start receiving events (for example, right
after
> Connect() call) and "CleanupEvents" before disconnecting.

> In "SetupEvents" you should add the instance of T<your_server_name>Impl
(this)
> to some global array (or simply store it in global variable if your server
can
> have only one client), and in "CleanupEvents" you should delete that entry
> from the global array. Then, when you need to fire the event, you need to
call
> "Fire_<EventName>()" for every entry in that array - all clients will
receive it.

> I've tested it under Win98 and it worked well, but I don't know how many
bugs
> it will introduce in future, so - I repeat - use it only if it's your last
> hope :)

> If anybody knows how to do it normally - please let me know too...

> Here is the sample of my code:

> // ========= in T<your_server_name>Impl.cpp ===============
> STDMETHODIMP T<your_server_name>Impl::SetupEvents()
> {
>   ClientList.AddElement(this);
>   return S_OK;
> }

> STDMETHODIMP T<your_server_name>Impl::CleanupEvents()
> {
>   ClientList.DelElement(this);
>   return S_OK;
> }

> // ========= somewhere else ===============
> class TComObjectList
> { private:
>     T<your_server_name>Impl **List;
>     int Count;

>     int FindElement(T<your_server_name>Impl *object);
>   public:
>     void AddElement(T<your_server_name>Impl *object);
>     void DelElement(T<your_server_name>Impl *object);

>     void Fire_Test_Event(void);

>     TComObjectList(void){ List=NULL; Count=0;};
>     ~TComObjectList(){ if(List) delete[] List; };
> };

//--------------------------------------------------------------------------
-
Quote
> TComObjectList ClientList;

> int TComObjectList::FindElement(T<your_server_name>Impl *object)
> {
>   if(!List) return -1;
>   for(int i=0;i<Count;i++)
>     if(List[i]==object)
>       return i;
>   return -1;
> }

//--------------------------------------------------------------------------
-
Quote

> void TComObjectList::AddElement(T<your_server_name>Impl *object)
> {
>   if(FindElement(object)>=0) return;

>   // .....
>   // add new element to array
>   // .....
> }

//--------------------------------------------------------------------------
-
Quote

> void TComObjectList::DelElement(T<your_server_name>Impl *object)
> {
>   int index=FindElement(object);

>   if(index<0) return;

>   // .....
>   // delete element "index" from array
>   // .....
> }

//--------------------------------------------------------------------------
-
Quote

> void TComObjectList::Fire_Test_Event(void)
> {
>   for(int i=0;i<Count;i++)
>     List[i]->Fire_TestEvent();
> }

//--------------------------------------------------------------------------
-

- Show quoted text -

Quote

> Yours faithfully,
> Alex Roschin
> E-mail: rochi...@yahoo.com

Re:Firing COM/DCOM events in automation object


Hi Alex & Steve,

Thanks for your ideas.  I had given up hope that anyone was actually going to respond!

Since my original post, I found a lot of useful information in the book C++ Builder5 Developer's Guide by Sam's Publishing.

My understanding of the event processing is that the macros in the server code actually implement the connection point list (of connected clients) for you, so you don't need to keep it yourself.  You should be able to call the Fire_<event> method one time, regardless of how many clients are connected, and they all will get the notification.  But since I can't get to the Fire_<event> method from the server object, maybe you do have to call it for each instance...

I'm going to play with your idea and see where it takes me.

Thanks!

Dave

Re:Firing COM/DCOM events in automation object


Quote
Steve Cox wrote:

> Alex,

> I think your solution will work fine.

> You don't need to add functions for starting to recieve events, though.  You
> can create a constructor for your implementation class, and add your
> instance to the global list at that time.

> If you add a destructor, you can remove yourself from your list at that
> time.

Thanks Steve!

The idea sounds good, but I can't make it work. It looks like the implementation
class doesn't call the destructor. Here's the code I've added to the class:

// ...
public:
  TExpRemoteControlImpl()
  { MessageBox(NULL,"AddElement","",MB_OK);
    ClientList.AddElement(this);
  }

  ~TExpRemoteControlImpl()
  { MessageBox(NULL,"DelElement","",MB_OK);
    ClientList.DelElement(this);
  }
// ...

I can see the "AddElement" message when the client connects to the server, but
I can't see "DelElement" message at all, even when the server closes.

I use single-use server model so I actually don't need to manage multiple clients,
but I still want to understand why it isn't working :) Do you know what am I
doing wrong?

Yours faithfully,
Alex Roschin
E-mail: rochi...@yahoo.com

Re:Firing COM/DCOM events in automation object


Alex,

The ~TExpRemoteControlImpl() destructor should be called when the client
terminates.  Do you know if the class that creates the Server object deletes
it?  Or if it does, does that class get deleted?

Quote
"Alex Roschin" <rochi...@yahoo.com> wrote in message

news:3B3B279C.D58D32C9@yahoo.com...

Quote
> Thanks Steve!

> The idea sounds good, but I can't make it work. It looks like the
implementation
> class doesn't call the destructor. Here's the code I've added to the
class:

> // ...
> public:
>   TExpRemoteControlImpl()
>   { MessageBox(NULL,"AddElement","",MB_OK);
>     ClientList.AddElement(this);
>   }

>   ~TExpRemoteControlImpl()
>   { MessageBox(NULL,"DelElement","",MB_OK);
>     ClientList.DelElement(this);
>   }
> // ...

> I can see the "AddElement" message when the client connects to the server,
but
> I can't see "DelElement" message at all, even when the server closes.

> I use single-use server model so I actually don't need to manage multiple
clients,
> but I still want to understand why it isn't working :) Do you know what am
I
> doing wrong?

> Yours faithfully,
> Alex Roschin
> E-mail: rochi...@yahoo.com

Re:Firing COM/DCOM events in automation object


Quote
> Alex,

> The ~TExpRemoteControlImpl() destructor should be called when the client
> terminates.  Do you know if the class that creates the Server object deletes
> it?  Or if it does, does that class get deleted?

Steve,

Client program deletes the object it created to interact with the server -
it was that place where I was calling "CleanupEvents" before. I don't know
how to check if the class *really* gets deleted - the only method I know is
to "flag" the destructor like I did. CodeGuard doesn't report any memory leaks
so I believe it is deleted properly.

BTW I've experimented with the Borland's examples "AutoCon" and "AutoSrv" -
their behaviour is the same. When you start several clients and then close
them one after another, the last destroyed object doesn't cause the server's
implementation class destructor to call. In those examples every client
EXE creates 2 server objects, so if you start 2 clients and then close
them both you'll get 4 constructor call messages and 3 destructor call
ones. The last destroyed object dies without destructor. Since the server
terminates right after the last object is destroyed it should not cause any
errors, so I don't think it's a big problem :)

Yours faithfully,
Alex Roschin
E-mail: rochi...@yahoo.com

Other Threads