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.
"Paul Blackburn" <pblack...@hotmail.com> wrote in message
news:634b01c1b969$1d1785b0$9ae62ecf@tkmsftngxa02...
> 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!
Many thanks for your feedback.
Cheers
Paul
> 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?
--
Steffen Ramlow: s.ra...@gmx.de
Enterprise Development: http://www.dev-purgatory.org/
> 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/>
>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
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
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/>