Re: owner of class

From: Ralph (msnews.20.nt_consulting32_at_spamgourmet.com)
Date: 12/30/04


Date: Thu, 30 Dec 2004 17:31:59 -0600


"Mike D Sutton" <EDais@mvps.org> wrote in message
news:ebsYRfq7EHA.3944@TK2MSFTNGP12.phx.gbl...
> > I have an application where I use a lot of class modules. At least a
> > hundred, and some of these
> >
> > Something that I think would help me during debugging is to know what
class
> > objects are currently initialized, what intanciated them (i.e. myclass1
> > created an instance of myclassX and myclass2 created an instance of
myclassX.)
> >
> > One way I was thinking of doing it was to include a variable or two in
each
> > class module that gets set when the class is created that contains the
> > specific name of the class (i.e. cAcct might be set goodAcct = new cAcct
and
> > set baddAcct = new cAcct). Then, add a set of
AddToStack/RemoveFromStack
> > functions to each class that would maintain an inventory of existing
class
> > objects.
> >
> > What I want to accomplish is to identify possible situations where I
> > terminate a given object but because I've created circular reference,
the
> > class doesn't terminate until after I terminate one or more other
classes.
> >
> > What I would like to be able to do is have a hidden interface 'feature'
that
> > would display a list of all of the active classes and who called them.
Then,
> > I could easily tell of a class was still active after I thought it
should be
> > closed.
> >
> > Example:
> >
> > Module/Class/Form Instance Name Class
> > ===========================
> > frmMain goodAcct cAcct
> > frmMain badAcct cAcct
> > frmMain strClass cString
> > myClass strClass cString
> >
> > I don't suppose there is anyway to ask the application itself (i.e. is
there
> > some sort of reachable collection of active classes already) without
needing
> > me to create and manage my own?
>
> One way of doing this is to have an Attach and Detach method of each class
that allows it to store it's parent object, probably the
> easiest way of doing this is you have multiple classes in your project is
to use an interface which exposes the functionality:
>
> '*** IParentTrack
> Public Property Get ParentObj() As Object
> End Property
>
> Public Sub AttachParent(ByRef inParent As Object)
> End Sub
>
> Public Function DetachParent() As Boolean
> End Function
> '***
>
> Then each class you need to know it's parent object can simply implement
the interface:
>
> '***
> Implements IParentTrack
>
> Dim m_ParentObj As Object
>
> Private Property Get IParentTrack_ParentObj() As Object
> Set IParentTrack_ParentObj = m_ParentObj
> End Property
>
> Private Sub IParentTrack_AttachParent(ByRef inParent As Object)
> m_ParentObj = inParent
> End Sub
>
> Private Function IParentTrack_DetachParent() As Boolean
> IParentTrack_DetachParent = Not (m_ParentObj Is Nothing)
> m_ParentObj = Nothing
> End Function
> '***
>
> You must remember to call DetachParent() on each before you want to
destroy it though otherwise you're left with a circular
> reference. If you don't like this solution then you could store a 'soft
pointer' to the parent by using the ObjPtr() function
> instead:
>
> '*** IParentTrack
> Public Property Get ParentPtr() As Long
> End Property
>
> Public Sub AttachParent(ByRef inParent As Object)
> End Sub
>
> Public Function DetachParent() As Boolean
> End Function
> '***
>
> '***
> Implements IParentTrack
>
> Dim m_ParentPtr As Long
>
> Private Property Get IParentTrack_ParentPtr() As Long
> IParentTrack_ParentPtr = m_ParentPtr
> End Property
>
> Private Sub IParentTrack_AttachParent(ByRef inParent As Object)
> m_ParentObj = ObjPtr(inParent)
> End Sub
>
> Private Function IParentTrack_DetachParent() As Boolean
> IParentTrack_DetachParent = (m_ParentPtr <> 0)
> m_ParentObj = 0
> End Function
> '***
>
> This doesn't quite give you the flexibility of the first solution, but
doesn't create any circular references. If you need to get
> back to the parent object from the pointer then you can use this function:
>
> '***
> Private Declare Sub PutDWord Lib "MSVBVM60.dll" Alias _
> "PutMem4" (ByRef inDst As Any, ByVal inSrc As Long)
>
> ...
>
> Private Function ResolveObjPtr(ByVal inPtr As Long) As Object
> Dim TempObj As Object
>
> If (inPtr) Then
> Call PutDWord(TempObj, inPtr)
> Set ResolveObjPtr = TempObj
> Call PutDWord(TempObj, 0&)
> End If
> End Function
> '***
>
> If the parent object has already been released though, this will give you
back an invalid object which will more thank likely kill
> the IDE if you attempt to use it, so be careful when using it..
> If you're in a position to use a class factory for your object creating
then it's possible to add parent/child relationships
> directly into it including communication between the parent and child
classes for when either gets destroyed - Have a look at the
> class factory and subject/observer design patterns if you want to know
more on this approach.
> Hope this helps,
>
> Mike
>
>
> - Microsoft Visual Basic MVP -
> E-Mail: EDais@mvps.org
> WWW: http://EDais.mvps.org/
>

In addition to Mike's sage advice and suggestions I would like to add one
'Gotcha'. When dealing with VB 'references' be aware that there are
situations where the actual 'COM' reference count and the count you are
maintaining are not exactly the same. (Objects are available for destruction
when the 'COM' Reference count goes to zero.) While you weren't necessary
attempting to maintain COM reference counts - I though I would warn you,
just in case in the heat of the moment, you ever accidently attempt to make
that comparison.

Also you might want to take a look at the mechanism employed by the VB Class
Builder Wizard for displaying a count when classes are created and
destroyed. [View::Options "Include Debug code in Initialize and Terminate
events"].

It is a very simple, and definitely less elegant process, but can be very
useful for isolating problems during development. Also note how you can use
compiler directives to instrument your code only during debugging sessions.
Something you might like to apply in conjuctions with Mike's suggestions.

[You can replace the Debug.Print statements with the Win32 API
OutputDebugString() call and then use a debug viewer to view and track
messages/counts. (www.sysinternals.com)]

hth
-ralph



Relevant Pages

  • Re: owner of class
    ... One way of doing this is to have an Attach and Detach method of each class that allows it to store it's parent object, ... Public Sub AttachParent ... Private Sub IParentTrack_AttachParent ... Private Function IParentTrack_DetachParentAs Boolean ...
    (microsoft.public.vb.general.discussion)
  • Re: Wo IDisposable implementieren
    ... Private Parent As Object ... Sub New ... End Sub ...
    (microsoft.public.de.german.entwickler.dotnet.vb)
  • Re: Determine if an item in a collection has changed
    ... notify parent, "messenger" and/or client's code about changes in their ... > Private m_Name As String ... > 'Friend Property Let IsDirty ... > Public Sub MarkAsTrash() ...
    (microsoft.public.vb.general.discussion)
  • Re: Wo IDisposable implementieren
    ... Private Parent As Object ... Sub New(ByVal c As cls1, ByVal n As Integer) ...
    (microsoft.public.de.german.entwickler.dotnet.vb)
  • Re: ReferenceAdded and ReferenceRemoved events fired multiple times
    ... Private WithEvents m_objReferencesEvents As VSLangProj.ReferencesEvents ... Public Sub OnConnection(ByVal application As Object, ... MessageBox.Show("Removed reference " & pReference.Name) ... My code is being run multiple times ...
    (microsoft.public.vsnet.ide)