Class_terminate not fired for module level objects 
Author Message
 Class_terminate not fired for module level objects

Hi,

I have an apartment threaded activeX DLL which contains
three Classes.

The DLL has a sub Main() entry point within which it
creates an instance of two of the
components private classes.

I would expect the Class_Terminate events for these
private objects to fire when the DLL closes.
The Class_Terminate events are fired ONLY when running
through the IDE.

The Main module (modMain.bas) is as follows

' modMain -------------------------------------------------
--------------------
Option Explicit

Private Declare Sub OutputDebugString Lib "kernel32"
Alias "OutputDebugStringA" (ByVal lpOutputString As String)
Private Declare Sub GetLocalTime Lib "kernel32"
(lpSystemTime As SYSTEMTIME)

Public Type SYSTEMTIME
        wYear As Integer
        wMonth As Integer
        wDayOfWeek As Integer
        wDay As Integer
        wHour As Integer
        wMinute As Integer
        wSecond As Integer
        wMilliseconds As Integer
End Type

Private m_oPrivate  As PPrivate
Private m_oChild    As PChild

Public Sub Main()
  ShowDebug "DLL Apartment Sub Main() -> " & App.ThreadID
  Set m_oPrivate = New PUKDLLTHREADREF.PPrivate
  Set m_oChild = New PUKDLLTHREADREF.PChild
End Sub

'+---------------------------------------------------------
--------------------
'| Public Function ShowDebug()
'+---------------------------------------------------------
--------------------
'| Purpose   : Display debug information
'| Arguments : <psInfo> - Debug string to display
'+---------------------------------------------------------
--------------------
Public Sub ShowDebug(psInfo As String)
  Dim lpStruct As SYSTEMTIME
  GetLocalTime lpStruct
  psInfo = Format$(Time, "hh:mm:ss") & ":" &
Format$(lpStruct.wMilliseconds, "000") & " " & psInfo
  Call OutputDebugString(psInfo & vbCrLf)
  Debug.Print psInfo
End Sub
'+---------------------------------------------------------
--------------------
'| EOF Public Function ShowDebug()
'+---------------------------------------------------------
--------------------

' EOF modMain ---------------------------------------------
------------------------

The class modules are as follows

' PChild class --------------------------------------------
--------------------
Option Explicit

Private Sub Class_Initialize()
  ShowDebug "PChild Class_Initialize() -> " & App.ThreadID
End Sub

Private Sub Class_Terminate()
  ShowDebug "PChild Class_Terminate() -> " & App.ThreadID
End Sub
' EOF PChild ----------------------------------------------
--------------------

' PPrivate class ------------------------------------------
--------------------
Option Explicit

Private Sub Class_Initialize()
  ShowDebug "PPrivate Class_Initialize() -> " &
App.ThreadID
End Sub

Private Sub Class_Terminate()
  MsgBox "PPrivate terminated"
  ShowDebug "PPrivate Class_Terminate() -> " & App.ThreadID
End Sub
' EOF PPrivate class --------------------------------------
--------------------

' PPublic class -------------------------------------------
--------------------
Option Explicit

Private Sub Class_Initialize()
  ShowDebug "PPublic Class_Initialize() -> " & App.ThreadID
End Sub

Private Sub Class_Terminate()
  ShowDebug "PPublic Class_Terminate() -> " & App.ThreadID
End Sub
' EOF PPublic class ---------------------------------------
---------------------

The client just creates an instance of PPublic and then
destroys it.
Running through the IDE I see the message box from the
terminate event of PPrivate and
in the immediate window the likes of..

17:10:45:375 DLL Apartment Sub Main() -> 348
17:10:45:375 PPrivate Class_Initialize() -> 348
17:10:45:375 PChild Class_Initialize() -> 348
17:10:45:375 PPublic Class_Initialize() -> 348
17:10:45:375 PPublic Class_Terminate() -> 348
17:10:49:191 PPrivate Class_Terminate() -> 348
17:10:49:191 PChild Class_Terminate() -> 348

As I would expect, however running compiled and viewing
the debug output I get

17:12:38:297 DLL Apartment Sub Main() -> 376
17:12:38:297 PPrivate Class_Initialize() -> 376
17:12:38:297 PChild Class_Initialize() -> 376
17:12:38:297 PPublic Class_Initialize() -> 376
17:12:38:297 PPublic Class_Terminate() -> 376

The two private class instances are NOT terminated?  
Even stranger is that if I comment out one of the private
class creations I get the main
entry point called each time so the private instance is
created and destroyed with PPublic.

Any help/ideas greatly appreciated.



Sun, 08 Aug 2004 01:16:05 GMT  
 Class_terminate not fired for module level objects
Try set this objects to Nothing manually


Quote:
> Hi,

> I have an apartment threaded activeX DLL which contains
> three Classes.

> The DLL has a sub Main() entry point within which it
> creates an instance of two of the
> components private classes.

> I would expect the Class_Terminate events for these
> private objects to fire when the DLL closes.
> The Class_Terminate events are fired ONLY when running
> through the IDE.

> The Main module (modMain.bas) is as follows

> ' modMain -------------------------------------------------
> --------------------
> Option Explicit

> Private Declare Sub OutputDebugString Lib "kernel32"
> Alias "OutputDebugStringA" (ByVal lpOutputString As String)
> Private Declare Sub GetLocalTime Lib "kernel32"
> (lpSystemTime As SYSTEMTIME)

> Public Type SYSTEMTIME
>         wYear As Integer
>         wMonth As Integer
>         wDayOfWeek As Integer
>         wDay As Integer
>         wHour As Integer
>         wMinute As Integer
>         wSecond As Integer
>         wMilliseconds As Integer
> End Type

> Private m_oPrivate  As PPrivate
> Private m_oChild    As PChild

> Public Sub Main()
>   ShowDebug "DLL Apartment Sub Main() -> " & App.ThreadID
>   Set m_oPrivate = New PUKDLLTHREADREF.PPrivate
>   Set m_oChild = New PUKDLLTHREADREF.PChild
> End Sub

> '+---------------------------------------------------------
> --------------------
> '| Public Function ShowDebug()
> '+---------------------------------------------------------
> --------------------
> '| Purpose   : Display debug information
> '| Arguments : <psInfo> - Debug string to display
> '+---------------------------------------------------------
> --------------------
> Public Sub ShowDebug(psInfo As String)
>   Dim lpStruct As SYSTEMTIME
>   GetLocalTime lpStruct
>   psInfo = Format$(Time, "hh:mm:ss") & ":" &
> Format$(lpStruct.wMilliseconds, "000") & " " & psInfo
>   Call OutputDebugString(psInfo & vbCrLf)
>   Debug.Print psInfo
> End Sub
> '+---------------------------------------------------------
> --------------------
> '| EOF Public Function ShowDebug()
> '+---------------------------------------------------------
> --------------------

> ' EOF modMain ---------------------------------------------
> ------------------------

> The class modules are as follows

> ' PChild class --------------------------------------------
> --------------------
> Option Explicit

> Private Sub Class_Initialize()
>   ShowDebug "PChild Class_Initialize() -> " & App.ThreadID
> End Sub

> Private Sub Class_Terminate()
>   ShowDebug "PChild Class_Terminate() -> " & App.ThreadID
> End Sub
> ' EOF PChild ----------------------------------------------
> --------------------

> ' PPrivate class ------------------------------------------
> --------------------
> Option Explicit

> Private Sub Class_Initialize()
>   ShowDebug "PPrivate Class_Initialize() -> " &
> App.ThreadID
> End Sub

> Private Sub Class_Terminate()
>   MsgBox "PPrivate terminated"
>   ShowDebug "PPrivate Class_Terminate() -> " & App.ThreadID
> End Sub
> ' EOF PPrivate class --------------------------------------
> --------------------

> ' PPublic class -------------------------------------------
> --------------------
> Option Explicit

> Private Sub Class_Initialize()
>   ShowDebug "PPublic Class_Initialize() -> " & App.ThreadID
> End Sub

> Private Sub Class_Terminate()
>   ShowDebug "PPublic Class_Terminate() -> " & App.ThreadID
> End Sub
> ' EOF PPublic class ---------------------------------------
> ---------------------

> The client just creates an instance of PPublic and then
> destroys it.
> Running through the IDE I see the message box from the
> terminate event of PPrivate and
> in the immediate window the likes of..

> 17:10:45:375 DLL Apartment Sub Main() -> 348
> 17:10:45:375 PPrivate Class_Initialize() -> 348
> 17:10:45:375 PChild Class_Initialize() -> 348
> 17:10:45:375 PPublic Class_Initialize() -> 348
> 17:10:45:375 PPublic Class_Terminate() -> 348
> 17:10:49:191 PPrivate Class_Terminate() -> 348
> 17:10:49:191 PChild Class_Terminate() -> 348

> As I would expect, however running compiled and viewing
> the debug output I get

> 17:12:38:297 DLL Apartment Sub Main() -> 376
> 17:12:38:297 PPrivate Class_Initialize() -> 376
> 17:12:38:297 PChild Class_Initialize() -> 376
> 17:12:38:297 PPublic Class_Initialize() -> 376
> 17:12:38:297 PPublic Class_Terminate() -> 376

> The two private class instances are NOT terminated?
> Even stranger is that if I comment out one of the private
> class creations I get the main
> entry point called each time so the private instance is
> created and destroyed with PPublic.

> Any help/ideas greatly appreciated.



Sun, 08 Aug 2004 02:44:45 GMT  
 Class_terminate not fired for module level objects

Quote:

> The DLL has a sub Main() entry point within which it
> creates an instance of two of the
> components private classes.

> I would expect the Class_Terminate events for these
> private objects to fire when the DLL closes.
> The Class_Terminate events are fired ONLY when running
> through the IDE.

You're right -- only external references to public objects "count".
Perhaps your public classes can manage the lifetimes of your internal
objects.  Air-code:

----module globals----
global pi as double

global i1 as internal_1
global i2 as internal_2

private refcount as long

' reserve sub main for initializing things that won't need shutting down
sub main()
  pi = 4 * atn(1)
end sub

public sub acquire()
  debug.assert refcount >= 0
  debug.assert (i1 is nothing) = (refcount = 0)
  debug.assert (i2 is nothing) = (refcount = 0)

  if refcount = 0 then
    set i1 = new internal_1
    set i2 = new internal_2
    refcount = 1

  else
    refcount = refcount + 1
  end if
end sub

public sub release()
  debug.assert refcount > 0
  debug.assert (i1 is nothing) = false
  debug.assert (i2 is nothing) = false

  if refcount = 1 then
    set i1 = nothing
    set i2 = nothing
    refcount = 0

  else
    refcount = refcount - 1
  end if
end sub
----end module----

----class multiuse_1----
private sub class_initialize()
  globals.acquire
end sub

private sub class_terminate()
  globals.release
end sub
----end class----

----class public_not_creatable_1----
private sub class_initialize()
  globals.acquire
end sub

private sub class_terminate()
  globals.release
end sub
----end class----

--
Joe Foster <mailto:jlfoster%40znet.com>   Space Cooties! <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!



Sun, 08 Aug 2004 07:50:50 GMT  
 Class_terminate not fired for module level objects

Quote:
>-----Original Message-----




Quote:

>> The DLL has a sub Main() entry point within which it
>> creates an instance of two of the
>> components private classes.

>> I would expect the Class_Terminate events for these
>> private objects to fire when the DLL closes.
>> The Class_Terminate events are fired ONLY when running
>> through the IDE.

>You're right -- only external references to public
objects "count".
>Perhaps your public classes can manage the lifetimes of
your internal
>objects.  Air-code:

<Snip>
In my real world problem I wanted an object to live the
duration of the apartment/thread.
In the scenario you suggested the thread specific object
would be initialised and terminated
many times over.
With VB it is not possible to determine when to explicitly
destroy global objects (ie we have no
way of knowing when a thread detaches as you do in C++)
and I HAD made the assumption that when
the DLL was unloaded the global objects would be
destroyed, reference count reduced to zero,
and the terminate event called.
The bizarrre thing I find is that the behaviour between
one and two global objects is so different
(this seems to have been corrupted and left out from my
original post)
A single global object results in the Sub Main() called
every time a new public object is created
and destroyed, which sort of makes sense, ie DLL loaded
for new public object, pubic object terminated,
dll unloaded.
I have found a workaround which involves using C++ to
manage the object lifetime but this means I'm
going have to review all VB components when it comes to
module level objects.

Many thanks for your feedback.

Cheers
Paul



Sun, 08 Aug 2004 22:12:05 GMT  
 Class_terminate not fired for module level objects

Quote:
> I have found a workaround which involves using C++ to
> manage the object lifetime but this means I'm
> going have to review all VB components when it comes to
> module level objects.

how?

--

Enterprise Development: http://www.dev-purgatory.org/



Mon, 09 Aug 2004 01:17:03 GMT  
 Class_terminate not fired for module level objects

Quote:

> In my real world problem I wanted an object to live the
> duration of the apartment/thread.
> In the scenario you suggested the thread specific object
> would be initialised and terminated
> many times over.

Perhaps you can use a factory class to help control these resources.
A GlobalMultiUse factory class instance will stick around as long as
the apartment exists (or until the instance is released explicitly),
and I haven't been able to get its Class_Terminate event to not fire,
at least not without using Task Manager to kill the task outright.  As
an added bonus, a GMU class can simulate parameterized constructors,
right now, without making you migrate to VB.NOT:

----class factory (globalmultiuse)----
public function a(x, y, z) as a
  set a = new a
  a.setup x, y, z
end function

public function b(p, q) as b
  set b = new b
  b.setup p, q
end function

private sub class_initialize()
  globals.acquire
end sub

private sub class_terminate()
  globals.release
end sub
----end class----

----class a (publicnotcreatable)----
friend sub setup(x, y, z)
  ' etc etc
end sub

private sub class_initialize()
  globals.acquire
end sub

private sub class_terminate()
  globals.release
end sub
----end class----

----class b (publicnotcreatable)----
friend sub setup(p, q)
  ' etc etc
end sub

private sub class_initialize()
  globals.acquire
end sub

private sub class_terminate()
  globals.release
end sub
----end class----

--
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!



Mon, 09 Aug 2004 09:50:22 GMT  
 Class_terminate not fired for module level objects

Quote:
>> In my real world problem I wanted an object to live the
>> duration of the apartment/thread.
>Perhaps you can use a factory class to help control these
>resources. A GlobalMultiUse factory class instance will
> stick around as long as the apartment exists (or until >

the instance is released explicitly),
<Snip>
A new instance of a GMU object is created (and existing
instance terminated) when referenced in another ActiveX
component.  ie call a method from say your client exe,
a support dll and a client hosted OCX and you will see the
object created and destroyed three times.  The thread data
I wish to keep would be lost.  

Many thanks for the suggestions



Mon, 09 Aug 2004 23:33:58 GMT  
 Class_terminate not fired for module level objects

Quote:
> I have found a workaround which involves using C++ to
> manage the object lifetime but this means I'm
> going have to review all VB components when it comes to
> module level objects.

I've got an ATL class with a static CMapWordToPtr array
containing the objects I wish to store mapped by the
thread id. These do not have AddRef() called.

I have a public property (GET) which will either
a) if there is an object for the current thread id then
return it or
b) create a new instance of the object I'm storing, store
it in the mapped array and return it

The FinalRelease() (Class_Terminate equivalent) is called
just fine and my objects are thread specific and last for
the the threads lifetime, so I can now have an apartment
threaded DLL with a thread wide data store.

e-mail me if you'd like the C++ code.

Cheers
Paul



Mon, 09 Aug 2004 23:51:44 GMT  
 Class_terminate not fired for module level objects

Quote:

> >> In my real world problem I wanted an object to live the
> >> duration of the apartment/thread.

> >Perhaps you can use a factory class to help control these
> >resources. A GlobalMultiUse factory class instance will
> > stick around as long as the apartment exists (or until >
> the instance is released explicitly),
> <Snip>
> A new instance of a GMU object is created (and existing
> instance terminated) when referenced in another ActiveX
> component.  ie call a method from say your client exe,
> a support dll and a client hosted OCX and you will see the
> object created and destroyed three times.  The thread data
> I wish to keep would be lost.

Those bastards...  (I seem to be saying that about Microsoft
quite a bit lately!)

Hey, Class_Terminate seems to be more reliable in ActiveX DLLs
compiled to P-Code instead of to Native Code, but as before, I
haven't really tested this very thoroughly...

--
Joe Foster <mailto:jlfoster%40znet.com>  Sacrament R2-45 <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!



Tue, 10 Aug 2004 03:49:24 GMT  
 
 [ 9 post ] 

 Relevant Pages 

1. Destroying ActiveX EXE Object Does Not Fire The Class_Terminate Event

2. Class_Terminate Not Firing

3. Class_Terminate won't fire

4. Nested usercontrol events not firing in second level of nesting

5. Works at Module level but not at Form

6. Module Level Versus Subroutine Level

7. dim at procedure level or module level

8. ON ERROR GOTO does not fire using ADO connection object

9. Object in ASP page not firing events

10. click event not firing if lost_focus is firing first

11. Remote Data Objects in combination with level 1 compliance level

12. VB6 - Capture KeyUp at Form Level - Not FileListBox Control Level

 

 
Powered by phpBB® Forum Software