Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event 
Author Message
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event

I have an ActiveX EXE project that I use to communicate between
applications.
Each application in turn creates an object, checks for previously registered
instances of itself before registering to receive messages.
The application can then recieve string messages via an event.

The problem is that when I set the object to Nothing in my application the
Class_Terminate event does not fire.
I unregister the application within the Terminate event.
This means that if the application is restarted it finds there is already an
application of that name registered.
It is OK if all objects are closed (i.e. by closing all applications that
use it) as the EXE is then unloaded from memory.

As a workaround I have ceated a new UnRegisterApplication method that my
application call before setting the object to Nothing.

The ActiveX EXE has the following methods/events:

Event MessageReceived(Message As String)
IsAppRegistered(AppName As String) As Boolean
RegisterApp(AppName As String) As Boolean
PostMessage(AppName As String, Message As String) As Boolean
ListAllRegisteredApps() As String {This is only used for diagnostics,
returned a comma-delimited string of AppNames}
... and the new ...
UnRegisterApp(AppName As String)

The instancing is set to 5 - MultiUse, is not persistable and has a thread
pool of one.

Does anybody have any clue as to why the Class_Terminate event does not fire
when the object is set to Nothing?

Thanks in advance,

Norris



Tue, 21 Sep 2004 18:15:27 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event
The ActiveX EXE object has one reference in the exe that is creating
it - this is the reference that you are setting to nothing. But there
must also be a reference to it held inside the ActiveX EXE (or
somewhere, it's difficult to tell without the actual code) in order
for it to receive the messages from other applications, this reference
also needs to be set to nothing in your UnRegister... method.

On Fri, 5 Apr 2002 11:15:27 +0100, "Norris"

Quote:

>I have an ActiveX EXE project that I use to communicate between
>applications.
>Each application in turn creates an object, checks for previously registered
>instances of itself before registering to receive messages.
>The application can then recieve string messages via an event.

>The problem is that when I set the object to Nothing in my application the
>Class_Terminate event does not fire.
>I unregister the application within the Terminate event.
>This means that if the application is restarted it finds there is already an
>application of that name registered.
>It is OK if all objects are closed (i.e. by closing all applications that
>use it) as the EXE is then unloaded from memory.

>As a workaround I have ceated a new UnRegisterApplication method that my
>application call before setting the object to Nothing.

>The ActiveX EXE has the following methods/events:

>Event MessageReceived(Message As String)
>IsAppRegistered(AppName As String) As Boolean
>RegisterApp(AppName As String) As Boolean
>PostMessage(AppName As String, Message As String) As Boolean
>ListAllRegisteredApps() As String {This is only used for diagnostics,
>returned a comma-delimited string of AppNames}
>... and the new ...
>UnRegisterApp(AppName As String)

>The instancing is set to 5 - MultiUse, is not persistable and has a thread
>pool of one.

>Does anybody have any clue as to why the Class_Terminate event does not fire
>when the object is set to Nothing?

>Thanks in advance,

>Norris

Stephen Martin
EMSoft Solutions Inc.
Toronto, ON


Tue, 21 Sep 2004 21:12:01 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event
Sorry, let me clarify

I have an ActiveX EXE project (CBSMsg) that exposes a single object
(CBSMessenger). It contains a standard module that shares variables between
CBSMessenger objects, keeps track of the registered application names (as an
array of strings) and provides the functionality that is exposed by the
CBSMessenger object (i.e. registerapp, isapploaded, etc).

I then have a couple of standard EXE projects and an Access 2000 database
that reference the CBSMsg.CBSMessenger object to pass messages between each
other. Each of these applications creates a CBSMessenger object and disposes
of it before quitting.

Within the Class_Terminate event of the CBSMessenger object I unregister the
application (i.e. remove it from the array). However, when any of the
applications set the object to Nothing, the Class_Terminate event does not
fire. The Class_Initialise event fires whenever an object is created though.

It's a little time since I did my VB6 exams, but I'm sure I should get an
Initialse/Terminate event for each object I create/dispose?
If I set the CBSMsg project instancing to be single-use the objects will not
be able to talk to each other.

Norris


Quote:
> The ActiveX EXE object has one reference in the exe that is creating
> it - this is the reference that you are setting to nothing. But there
> must also be a reference to it held inside the ActiveX EXE (or
> somewhere, it's difficult to tell without the actual code) in order
> for it to receive the messages from other applications, this reference
> also needs to be set to nothing in your UnRegister... method.

> On Fri, 5 Apr 2002 11:15:27 +0100, "Norris"

> >I have an ActiveX EXE project that I use to communicate between
> >applications.
> >Each application in turn creates an object, checks for previously
registered
> >instances of itself before registering to receive messages.
> >The application can then recieve string messages via an event.

> >The problem is that when I set the object to Nothing in my application
the
> >Class_Terminate event does not fire.
> >I unregister the application within the Terminate event.
> >This means that if the application is restarted it finds there is already
an
> >application of that name registered.
> >It is OK if all objects are closed (i.e. by closing all applications that
> >use it) as the EXE is then unloaded from memory.

> >As a workaround I have ceated a new UnRegisterApplication method that my
> >application call before setting the object to Nothing.

> >The ActiveX EXE has the following methods/events:

> >Event MessageReceived(Message As String)
> >IsAppRegistered(AppName As String) As Boolean
> >RegisterApp(AppName As String) As Boolean
> >PostMessage(AppName As String, Message As String) As Boolean
> >ListAllRegisteredApps() As String {This is only used for diagnostics,
> >returned a comma-delimited string of AppNames}
> >... and the new ...
> >UnRegisterApp(AppName As String)

> >The instancing is set to 5 - MultiUse, is not persistable and has a
thread
> >pool of one.

> >Does anybody have any clue as to why the Class_Terminate event does not
fire
> >when the object is set to Nothing?

> >Thanks in advance,

> >Norris

> Stephen Martin
> EMSoft Solutions Inc.
> Toronto, ON



Tue, 21 Sep 2004 21:37:21 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event
Hi,

When one of your standard exe projects creates a CBSMessenger object
it creates one reference to it and that reference is removed when you
set it to nothing. But in the CBSMsg ActiveX exe program you are
probably keeping a reference to the object also in order to
communicate with it, this is probably the reference that is never
being removed. The Class_Terminate will not fire until all references
are removed.

For instance, if app A calls its CBSMessenger's PostMessage method I
assume app B receives the MessageReceived event from its CBSMessenger
object. But how does one CBSMessenger inform the other that it has a
message - there is probably a reference being held there somewhere.

On Fri, 5 Apr 2002 14:37:21 +0100, "Norris"

Quote:

>Sorry, let me clarify

>I have an ActiveX EXE project (CBSMsg) that exposes a single object
>(CBSMessenger). It contains a standard module that shares variables between
>CBSMessenger objects, keeps track of the registered application names (as an
>array of strings) and provides the functionality that is exposed by the
>CBSMessenger object (i.e. registerapp, isapploaded, etc).

>I then have a couple of standard EXE projects and an Access 2000 database
>that reference the CBSMsg.CBSMessenger object to pass messages between each
>other. Each of these applications creates a CBSMessenger object and disposes
>of it before quitting.

>Within the Class_Terminate event of the CBSMessenger object I unregister the
>application (i.e. remove it from the array). However, when any of the
>applications set the object to Nothing, the Class_Terminate event does not
>fire. The Class_Initialise event fires whenever an object is created though.

>It's a little time since I did my VB6 exams, but I'm sure I should get an
>Initialse/Terminate event for each object I create/dispose?
>If I set the CBSMsg project instancing to be single-use the objects will not
>be able to talk to each other.

>Norris

Stephen Martin
EMSoft Solutions Inc.
Toronto, ON


Wed, 22 Sep 2004 02:31:37 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event

Quote:

> I have an ActiveX EXE project that I use to communicate between
> applications.
> Each application in turn creates an object, checks for previously registered
> instances of itself before registering to receive messages.
> The application can then recieve string messages via an event.

> The problem is that when I set the object to Nothing in my application the
> Class_Terminate event does not fire.
> I unregister the application within the Terminate event.
> This means that if the application is restarted it finds there is already an
> application of that name registered.
> It is OK if all objects are closed (i.e. by closing all applications that
> use it) as the EXE is then unloaded from memory.

Does it still happen if you compile the ActiveX EXE to p-code?
(This seems to matter in the case of ActiveX /DLL/ instances!)
What else holds a reference to this object, perhaps a collection
or array of registered applications?  Class_Terminate fires only
when the /last/ reference to an object, internal or external, is
released.  The ActiveX EXE as a whole will shut down when the
last external reference is released, despite any /internal/ refs
to public objects that might still exist.

--
Joe Foster <mailto:jlfoster%40znet.com>  Sign the Check! <http://www.xenu.net/>
WARNING: I cannot be held responsible for the above        They're   coming  to
because  my cats have  apparently  learned to type.        take me away, ha ha!



Wed, 22 Sep 2004 04:20:35 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event


Quote:
> I have an ActiveX EXE project that I use to communicate between
> applications.
> Each application in turn creates an object, checks for previously
registered
> instances of itself before registering to receive messages.
> The application can then recieve string messages via an event.

> The problem is that when I set the object to Nothing in my application the
> Class_Terminate event does not fire.
> I unregister the application within the Terminate event.
> This means that if the application is restarted it finds there is already
an
> application of that name registered.
> It is OK if all objects are closed (i.e. by closing all applications that
> use it) as the EXE is then unloaded from memory.

> As a workaround I have ceated a new UnRegisterApplication method that my
> application call before setting the object to Nothing.

> The ActiveX EXE has the following methods/events:

> Event MessageReceived(Message As String)
> IsAppRegistered(AppName As String) As Boolean
> RegisterApp(AppName As String) As Boolean
> PostMessage(AppName As String, Message As String) As Boolean
> ListAllRegisteredApps() As String {This is only used for diagnostics,
> returned a comma-delimited string of AppNames}
> ... and the new ...
> UnRegisterApp(AppName As String)

> The instancing is set to 5 - MultiUse, is not persistable and has a thread
> pool of one.

> Does anybody have any clue as to why the Class_Terminate event does not
fire
> when the object is set to Nothing?

It sounds like you may want to set Instancing to either SingleUser or
GlobalSingleUse.  This will cause each application to create a new instance
of the class.  When Instancing is MultiUse, only one instance gets
created....ever.  When your other applications instantiate the class, they
simply get a reference to the instance that already exists (and Windows
incrememts an internal reference count).  When you set the object variable
to Nothing, Windows decrements this reference count and only destroys the
instance when that count reaches 0.  Therefore, Class_Terminate won't fire
until the very last reference held to it by all your apps has been set to
Nothing.  IOW, you're seeing it work exactly as it should.

If you need to keep Instancing at MultiUse, then you must do what you've
done with the UnregisterApplication method you wrote.  It's not a
work-around.  That's the way it's designed to work.

Mike



Wed, 22 Sep 2004 20:59:20 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event

Quote:

> It sounds like you may want to set Instancing to either SingleUser or
> GlobalSingleUse.  This will cause each application to create a new instance
> of the class.

YM "server"

Quote:
>  When Instancing is MultiUse, only one instance gets
> created....ever.  When your other applications instantiate the class, they
> simply get a reference to the instance that already exists (and Windows

ITYM "server instance"

Quote:
> incrememts an internal reference count).  When you set the object variable
> to Nothing, Windows decrements this reference count and only destroys the
> instance when that count reaches 0.  Therefore, Class_Terminate won't fire
> until the very last reference held to it by all your apps has been set to
> Nothing.  IOW, you're seeing it work exactly as it should.

> If you need to keep Instancing at MultiUse, then you must do what you've
> done with the UnregisterApplication method you wrote.  It's not a
> work-around.  That's the way it's designed to work.

I think he's keeping a master list within the component that's
keeping each object instance alive, whether the creating task
is still alive or not.  Instead, how about using events to
communicate within the component?  Pseudo-code:

class towncrier ' private
  public event announce(byref from as string, byref collision as boolean)

  public event broadcast(byref from as string, byref value as variant, _
    byref replies as variant)

  public event soundoff(byref list() as string, byref count as long)

  public function register(byref name as string) as boolean
    dim collision as boolean
    raiseevent announce(name, collision)
    register = collision = false
  end function

  public function shout(byref name as string, byref value as variant) as variant
    raiseevent broadcast(name, value, shout)
  end function

  public function who() as variant
    dim list() as string, count as long
    redim list(0 to 15)

    raiseevent soundoff(list, count)

    if count = 0 then
      who = vba.array()
    elseif count <= ubound(list) then
      redim preserve list(0 to count - 1)
      who = list
    else
      who = list
    end if
  end function
end class

module globals
  globals crier as towncrier ' GoF mediator pattern

  private sub main()
    if crier is nothing then set crier = new towncrier
  end sub
end module

class client ' multiuse
  private withevents crier as towncrier
  private myname as string

  public event broadcast(byval from as string, byval value as variant, _
    byref replies as variant) ' byval to minimize cross-process marshaling

  public property get name() as string
    name = myname
  end property

  public property let name(byval newname as string)
    'if myname = newname then myname = newname : exit property
    if crier.register(newname) then
      myname = newname
    else
      err.raise vbobjecterror + 1000, , "name already in use"
    end if
  end property

  public function shout(byval value as variant) as variant
    shout = crier.shout(myname, value)
  end function

  public function who() as variant
    who = crier.who
  end function

  private sub class_initialize()
    set crier = globals.crier
    myname = "anonymous coward"
  end sub

  private sub crier_announce(byref from as string, byref collision as boolean)
    if from = myname then collision = true
  end sub

  private sub crier_broadcast(byref from as string, byref value as variant, _
    byref replies as variant)

    if from <> myname then raiseevent broadcast(from, value, replies)
  end sub

  private sub crier_soundoff(byref list() as string, byref count as long)
    if count >= ubound(list) then redim preserve list(0 to count + count \ 2)
    list(count) = myname
    count = count + 1
  end sub
end class

...and I haven't even had any coffee yet...

--
Joe Foster <mailto:jlfoster%40znet.com>     Got Thetans? <http://www.xenu.net/>
WARNING: I cannot be held responsible for the above        They're   coming  to
because  my cats have  apparently  learned to type.        take me away, ha ha!



Wed, 22 Sep 2004 23:59:18 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event
Hi Stephen,

You are correct. Thanks. It looks like I need my UnRegisterApp after all
then.
The objects are referenced and held in an array within my common module as
follows:

Private Type Register
    AppName As String
    Instance As CBSMessenger
End Type
Dim Registers() As Register

Strangely enough I have been using my CBSMessenger object for several months
and never had any problems reported by users. I have ended up breaking
compatibility and re-issuing all applications that use it including a call
to UnregisterApp.

As a matter of academical interest, is there any way you can think of
detecting when an object is set to nothing by an application while it is
still referenced internally, without calling a pre-disposal method like
UnRegisterApp? Is there a way of knowing what objects have been created
without storing a reference in a collection i.e. is there an inherent
collection of object instances?

Thanks again,

Norris


Quote:
> Hi,

> When one of your standard exe projects creates a CBSMessenger object
> it creates one reference to it and that reference is removed when you
> set it to nothing. But in the CBSMsg ActiveX exe program you are
> probably keeping a reference to the object also in order to
> communicate with it, this is probably the reference that is never
> being removed. The Class_Terminate will not fire until all references
> are removed.

> For instance, if app A calls its CBSMessenger's PostMessage method I
> assume app B receives the MessageReceived event from its CBSMessenger
> object. But how does one CBSMessenger inform the other that it has a
> message - there is probably a reference being held there somewhere.

> On Fri, 5 Apr 2002 14:37:21 +0100, "Norris"

> >Sorry, let me clarify

> >I have an ActiveX EXE project (CBSMsg) that exposes a single object
> >(CBSMessenger). It contains a standard module that shares variables
between
> >CBSMessenger objects, keeps track of the registered application names (as
an
> >array of strings) and provides the functionality that is exposed by the
> >CBSMessenger object (i.e. registerapp, isapploaded, etc).

> >I then have a couple of standard EXE projects and an Access 2000 database
> >that reference the CBSMsg.CBSMessenger object to pass messages between
each
> >other. Each of these applications creates a CBSMessenger object and
disposes
> >of it before quitting.

> >Within the Class_Terminate event of the CBSMessenger object I unregister
the
> >application (i.e. remove it from the array). However, when any of the
> >applications set the object to Nothing, the Class_Terminate event does
not
> >fire. The Class_Initialise event fires whenever an object is created
though.

> >It's a little time since I did my VB6 exams, but I'm sure I should get an
> >Initialse/Terminate event for each object I create/dispose?
> >If I set the CBSMsg project instancing to be single-use the objects will
not
> >be able to talk to each other.

> >Norris

> Stephen Martin
> EMSoft Solutions Inc.
> Toronto, ON



Fri, 24 Sep 2004 16:28:31 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event
FYI, a common module within the project does hold a collection of objects as
follows:
Private Type Register
    AppName As String
    Instance As CBSMessenger
End Type
Dim Registers() As Register

The instancing must be set to MultiUse, as when set to SingleUse an instance
of the executable runs for each created object. Each executable will have
it's own common module with a collection of one. I would not then be able to
pass messages between objects (which is the whole point of the project) as
each would be a discrete process.

Many thanks for your replies.

As I asked Stephen Martin, for purely academical reasons, can anybody think
of a way of iterating through object instances without maintaining a
collection? Are there any inherent collections of objects that have been
created?

Norris




Quote:

> > It sounds like you may want to set Instancing to either SingleUser or
> > GlobalSingleUse.  This will cause each application to create a new
instance
> > of the class.

> YM "server"

> >  When Instancing is MultiUse, only one instance gets
> > created....ever.  When your other applications instantiate the class,
they
> > simply get a reference to the instance that already exists (and Windows

> ITYM "server instance"

> > incrememts an internal reference count).  When you set the object
variable
> > to Nothing, Windows decrements this reference count and only destroys
the
> > instance when that count reaches 0.  Therefore, Class_Terminate won't
fire
> > until the very last reference held to it by all your apps has been set
to
> > Nothing.  IOW, you're seeing it work exactly as it should.

> > If you need to keep Instancing at MultiUse, then you must do what you've
> > done with the UnregisterApplication method you wrote.  It's not a
> > work-around.  That's the way it's designed to work.

> I think he's keeping a master list within the component that's
> keeping each object instance alive, whether the creating task
> is still alive or not.  Instead, how about using events to
> communicate within the component?  Pseudo-code:

> class towncrier ' private
>   public event announce(byref from as string, byref collision as boolean)

>   public event broadcast(byref from as string, byref value as variant, _
>     byref replies as variant)

>   public event soundoff(byref list() as string, byref count as long)

>   public function register(byref name as string) as boolean
>     dim collision as boolean
>     raiseevent announce(name, collision)
>     register = collision = false
>   end function

>   public function shout(byref name as string, byref value as variant) as
variant
>     raiseevent broadcast(name, value, shout)
>   end function

>   public function who() as variant
>     dim list() as string, count as long
>     redim list(0 to 15)

>     raiseevent soundoff(list, count)

>     if count = 0 then
>       who = vba.array()
>     elseif count <= ubound(list) then
>       redim preserve list(0 to count - 1)
>       who = list
>     else
>       who = list
>     end if
>   end function
> end class

> module globals
>   globals crier as towncrier ' GoF mediator pattern

>   private sub main()
>     if crier is nothing then set crier = new towncrier
>   end sub
> end module

> class client ' multiuse
>   private withevents crier as towncrier
>   private myname as string

>   public event broadcast(byval from as string, byval value as variant, _
>     byref replies as variant) ' byval to minimize cross-process marshaling

>   public property get name() as string
>     name = myname
>   end property

>   public property let name(byval newname as string)
>     'if myname = newname then myname = newname : exit property
>     if crier.register(newname) then
>       myname = newname
>     else
>       err.raise vbobjecterror + 1000, , "name already in use"
>     end if
>   end property

>   public function shout(byval value as variant) as variant
>     shout = crier.shout(myname, value)
>   end function

>   public function who() as variant
>     who = crier.who
>   end function

>   private sub class_initialize()
>     set crier = globals.crier
>     myname = "anonymous coward"
>   end sub

>   private sub crier_announce(byref from as string, byref collision as
boolean)
>     if from = myname then collision = true
>   end sub

>   private sub crier_broadcast(byref from as string, byref value as
variant, _
>     byref replies as variant)

>     if from <> myname then raiseevent broadcast(from, value, replies)
>   end sub

>   private sub crier_soundoff(byref list() as string, byref count as long)
>     if count >= ubound(list) then redim preserve list(0 to count + count \
2)
>     list(count) = myname
>     count = count + 1
>   end sub
> end class

> ...and I haven't even had any coffee yet...

> --
> Joe Foster <mailto:jlfoster%40znet.com>     Got Thetans?

<http://www.xenu.net/>

- Show quoted text -

Quote:
> WARNING: I cannot be held responsible for the above        They're
coming  to
> because  my cats have  apparently  learned to type.        take me away,
ha ha!



Fri, 24 Sep 2004 16:39:25 GMT  
 Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event

Quote:

> You are correct. Thanks. It looks like I need my UnRegisterApp after all
> then.

No, you don't.

Quote:
> The objects are referenced and held in an array within my common module as
> follows:

> Private Type Register
>     AppName As String
>     Instance As CBSMessenger
> End Type
> Dim Registers() As Register

> Strangely enough I have been using my CBSMessenger object for several months
> and never had any problems reported by users. I have ended up breaking
> compatibility and re-issuing all applications that use it including a call
> to UnregisterApp.

This wasn't really necessary.

Quote:
> As a matter of academical interest, is there any way you can think of
> detecting when an object is set to nothing by an application while it is
> still referenced internally, without calling a pre-disposal method like
> UnRegisterApp? Is there a way of knowing what objects have been created
> without storing a reference in a collection i.e. is there an inherent
> collection of object instances?

The code I posted uses events to simulate this.  The additional
overhead of using events shouldn't be a deal-breaker, at least
as compared to the overhead inherent in using an ActiveX EXE in
the first place!

--
Joe Foster <mailto:jlfoster%40znet.com>     On the cans? <http://www.xenu.net/>
WARNING: I cannot be held responsible for the above        They're   coming  to
because  my cats have  apparently  learned to type.        take me away, ha ha!



Fri, 24 Sep 2004 23:37:03 GMT  
 
 [ 10 post ] 

 Relevant Pages 

1. Class_terminate not fired for module level objects

2. Class_Terminate Not Firing

3. Firing events from GlobalMultiUse ActiveX EXE

4. ActiveX control causing subform's load event not to fire

5. ActiveX custom event not firing HELP -- URGENT!

6. Terminate Event on ActiveX user control not firing

7. ActiveX run mode terminate event does not fire

8. ActiveX custom event not firing HELP -- URGENT!

9. VB6 ActiveX Control -- Change Event Not Firing

10. ActiveX Param not firing event

11. Is there an event fired inside a script component when it is destroyed, like VB's Class_Terminate?

12. Object in ASP page not firing events

 

 
Powered by phpBB® Forum Software