Class destructor doesn't run

Class destructor doesn't run

DonStauffer99
Advocate Advocate
260 Views
1 Reply
Message 1 of 2

Class destructor doesn't run

DonStauffer99
Advocate
Advocate

 

Inventor 2018 under Windows 10.
The destructor of this class usually doesn't run, for some reason.
I'm trying to figure out why. I've tried just letting the variable
go out of scope, and I've tried setting it to Nothing.
This class is in a .NET Class Library DLL.
Typically the object is contained in another class, for a specific part,
and it is that containing class which is instantiated in the rule.
The constructor works fine, and loads the part if necessary. The class
is reference-counted, but usually there's only one instance representing
a particular part.

Any thoughts are appreciated.
Imports System.Runtime.InteropServices
Imports Inventor
Imports Autodesk.iLogic.Interfaces

Namespace Inventor
    '===========================================================================
    '   Part
    '   
    '   Base class, in the OOP sense, for all Inventor parts. That is, it is
    '   intended to supply low-level control of Inventor parts to other classes.
    '   Those Inventor parts can be "derived" parts, "base" parts, or neither.
    '   This class is where Inventor API access happens. Each specific Inventor
    '   part class object contains an instance of the Part class which it uses
    '   to do the "real" work.
    '===========================================================================
    Public Class Part
        '-----------------------------------------------------------------------
        Private Shared ILowLevelSupport_ As ILowLevelSupport
        '-----------------------------------------------------------------------
        Private Shared InstanceCount_ As New Dictionary(Of String, Integer)
        Private Shared WasFileOpen_ As New Dictionary(Of String, Boolean)
        '-----------------------------------------------------------------------
        Private DisplayName_ As String
        Private Doc_ As PartDocument
        '-----------------------------------------------------------------------
        '   Constructor
        '   
        '   Loads document into memory if necessary, and saves a reference to
        '   it. Increments instance count, and, for first instance, stores
        '   whether the file was in memory already.
        '-----------------------------------------------------------------------
        Public Sub New(ByRef DisplayName As String)

            DisplayName_ = DisplayName

            '   Add to instance count

            Dim n As Integer

            With InstanceCount_
                .TryGetValue(DisplayName_, n)
                .Item(DisplayName_) = n + 1
            End With

            '   Get reference to part document if it's in memory already

            For Each d As Document In Application.Documents

                If d.DisplayName() = DisplayName_ Then
                    Doc_ = d
                    If n = 0 Then
                        WasFileOpen_.Item(DisplayName_) = True
                    End If
                    Exit Sub
                End If

            Next

            '   Document was not in memory. Load it.

            If n = 0 Then WasFileOpen_.Item(DisplayName_) = False
            Doc_ = OpenDoc()

        End Sub '   New
        '-----------------------------------------------------------------------
        '   Destructor
        '   
        '   Decrements the instance count and, for last instance, saves the
        '   file, then closes it if it had to be opened on construction of first
        '   instance.
        '   
        '   March 2018
        '   
        '   I have been unable to get destructors to fire reliably in iLogic,
        '   whether in an external file or a DLL. I've had to use a workaround
        '   of saving and closing from outside the class before the last object
        '   goes out of scope.
        '-----------------------------------------------------------------------
        Protected Overrides Sub Finalize()

            '*******************************************************************
            '   This usually doesn't run when objects go out of scope or are
            '   explicitly set to Nothing. I don't know why.
            '*******************************************************************

            MsgBox("Destructor for " & DisplayName_)

            '   Reduce instance count

            Dim n As Integer

            With InstanceCount_
                .TryGetValue(DisplayName_, n)
                If n > 0 Then .Item(DisplayName_) = n - 1
            End With

            If n > 1 Then Exit Sub
            If Doc_ Is Nothing Then Exit Sub

            '   Save document if possible

            Save()

            '   Close document if it had to be opened and this is the last
            '   instance

            CloseIfYouOpened()

        End Sub '   Finalize
        '-----------------------------------------------------------------------
        '   OpenFile
        '   
        '   Attempts to open the part file, suppressing errors. Returns Document
        '   reference, or Nothing for failure.
        '-----------------------------------------------------------------------
        Private Function OpenFile(ByRef dirSpec As String) As Document

            Dim filespec As String = dirSpec & "\" & DisplayName_ & ".ipt"

            Application.SilentOperation = True

            Try
                OpenFile = Application.Documents.Open(filespec)
            Catch ex As Exception
                OpenFile = Nothing
            End Try

            Application.SilentOperation = False

        End Function    '   OpenFile
        '-----------------------------------------------------------------------
        '   OpenDoc
        '   
        '   Finds and opens the document file. Returns Document reference, or
        '   Nothing for failure.
        '-----------------------------------------------------------------------
        Private Function OpenDoc() As Document

            '   Get Workspace path

            Dim dpm As DesignProjectManager
            Dim dp As DesignProject

            dpm = Application.DesignProjectManager
            dp = dpm.ActiveDesignProject

            '   Try to open document there

            Dim doc As Document = OpenFile(dp.WorkspacePath)

            If Not doc Is Nothing Then
                OpenDoc = doc
                Exit Function
            End If

            '   Try to open file in each, testing for not found error

            For Each p As ProjectPath In dp.WorkgroupPaths

                doc = OpenFile(p.Path())

                If Not doc Is Nothing Then
                    OpenDoc = doc
                    Exit Function
                End If

            Next

            OpenDoc = Nothing

        End Function    '   OpenDoc
        '-----------------------------------------------------------------------
        '   Save
        '   
        '   Saves the file to disk. Fails quietly if this is the rule doc.
        '-----------------------------------------------------------------------
        Public Sub Save()

            Try

                Doc_.Save2(False)

            Catch ex As COMException

                If ex.HResult <> &H80030103 Then Throw ex
                If Not IsRuleDoc Then Throw ex

            End Try

        End Sub '   Save
        '-----------------------------------------------------------------------
        '   Close
        '   
        '   Closes the document.
        '-----------------------------------------------------------------------
        Public Sub Close()
            Doc_.Close(True)
        End Sub
        '-----------------------------------------------------------------------
        '   CloseIfYouOpened
        '   
        '   Closes the document if the first instance of this class for that
        '   document opened it.
        '   
        '   Making this public is a workaround for unreliable destructor firing.
        '-----------------------------------------------------------------------
        Public Sub CloseIfYouOpened()

            Dim WasFileOpen As Boolean
            WasFileOpen_.TryGetValue(DisplayName_, WasFileOpen)

            If Not WasFileOpen Then Close()

        End Sub    '   CloseIfYouOpened
        '-----------------------------------------------------------------------
        '   StoreParams
        '   
        '   Updates the Inventor part with new parameter values from the
        '   Dictionary (a fancy collection object). Only parameters named in the
        '   Dictionary will be updated. This only works for User Parameters.
        '-----------------------------------------------------------------------
        Public Sub StoreParams(ByRef params As Dictionary(Of String, Object))

            Dim pcd As PartComponentDefinition = Doc_.ComponentDefinition ' The problem; why?

            Dim p As Parameters = pcd.Parameters
            Dim ups As UserParameters = p.UserParameters

            Dim up As UserParameter
            For Each key As String In params.Keys

                up = ups.Item(key)
                up.Expression = params(key)

            Next

        End Sub '   StoreParams
        '-----------------------------------------------------------------------
        '   RetrieveParams
        '   
        '   Gets the values from the Inventor part of all parameters named in
        '   the Dictionary. Values already in the Dictionary for those parameter
        '   names will be overwritten.
        '-----------------------------------------------------------------------
        Public Sub RetrieveParams(ByRef params As Dictionary(Of String, Object))

            Dim pcd As PartComponentDefinition = Doc_.ComponentDefinition
            Dim ps As Parameters = pcd.Parameters
            Dim p As Parameter

            For Each key As String In New Dictionary(Of String, Object)(params).Keys

                p = ps.Item(key)
                params(key) = p.Value

            Next

        End Sub '   RetrieveParams
        '-----------------------------------------------------------------------
        '   Update
        '   
        '   Affects the Inventor part like clicking the Update button on the
        '   ribbon.
        '-----------------------------------------------------------------------
        Public Sub Update()
            Doc_.Update()
        End Sub '   Update
        '-----------------------------------------------------------------------
        '   UpdateSuppressedDerivation
        '   
        '   Assumes this class represents an Inventor derived part. This
        '   function unsuppresses this Inventor part's derivation link to the
        '   base part just long enough to update the part.
        '-----------------------------------------------------------------------
        Public Sub UpdateSuppressedDerivation _
                    (ByRef BasePartDisplayName As String)

            Dim dpc As DerivedPartComponent

            dpc = GetDerivedPartComponent(BasePartDisplayName)
            If dpc Is Nothing Then Exit Sub

            dpc.SuppressLinkToFile = False
            Update()
            dpc.SuppressLinkToFile = True

        End Sub '   UpdateSuppressedDerivation
        '-----------------------------------------------------------------------
        '   UpdateSuppressedDerivation
        '   
        '   Assumes this class represents an Inventor base part. This function
        '   instructs the Inventor derived part to unsuppress the derivation
        '   link to the base part this class represents, just long enough to
        '   update the derived part.
        '-----------------------------------------------------------------------
        Public Sub UpdateSuppressedDerivation(ByRef DerivedPart As Part)
            DerivedPart.UpdateSuppressedDerivation(DisplayName_)
        End Sub
        '-----------------------------------------------------------------------
        '   GetDerivedPartComponent
        '   
        '   This function's name is confusing, because it echoes the name of the
        '   Inventor API property it reads. Despite the name
        '   "DerivedPartComponent", the returned object represents functionality
        '   with regard to an Inventor base Part. Assumes this class represents
        '   an Inventor derived part.
        '   
        '   Returns the Inventor DerivedPartComponent object reference for a
        '   base part used to derive the part this class represents. The object
        '   can be used to suppress/unsuppress or delete the derivation link.
        '-----------------------------------------------------------------------
        Private Function GetDerivedPartComponent _
                            (BasePartDisplayName As String) _
                                                        As DerivedPartComponent

            Dim pcd As PartComponentDefinition = Doc_.ComponentDefinition
            Dim rcs As ReferenceComponents = pcd.ReferenceComponents
            Dim dpcs As DerivedPartComponents = rcs.DerivedPartComponents

            For Each dpc As DerivedPartComponent In dpcs

                If dpc.Name = BasePartDisplayName Then
                    GetDerivedPartComponent = dpc
                    Exit Function
                End If
            Next

            '   That is not a derivation base of this part

            GetDerivedPartComponent = Nothing

        End Function    '   GetDerivedPartComponent
        '-----------------------------------------------------------------------
        '   iLogicVb Property
        '
        '   Sets or gets the (static) low-level support object.
        '-----------------------------------------------------------------------
        Public Shared Property iLogicVb As ILowLevelSupport

            Set(value As ILowLevelSupport)
                ILowLevelSupport_ = value
            End Set

            Get
                Return ILowLevelSupport_
            End Get

        End Property '   iLogicVb
        '-----------------------------------------------------------------------
        '   Application Property
        '   
        '   Returns a reference to the Application object.
        '-----------------------------------------------------------------------
        Public Shared ReadOnly Property Application As Application

            Get
                Return iLogicVb.Application
            End Get

        End Property    '   Application
        '-----------------------------------------------------------------------
        '   Automation Property
        '   
        '   Returns a reference to the Automation object.
        '-----------------------------------------------------------------------
        Public Shared ReadOnly Property Automation As Application

            Get
                Return iLogicVb.Automation
            End Get

        End Property    '   Automation
        '-----------------------------------------------------------------------
        '   RuleDocument Property
        '   
        '   Returns a reference to the Document object which ran the rule.
        '-----------------------------------------------------------------------
        Public Shared ReadOnly Property RuleDocument As Application

            Get
                Return iLogicVb.RuleDocument
            End Get

        End Property    '   RuleDocument
        '-----------------------------------------------------------------------
        '   IsRuleDoc Property
        '
        '   Retuarns a boolean telling if this is the document running the rule.
        '-----------------------------------------------------------------------
        Public ReadOnly Property IsRuleDoc As Boolean

            Get
                Return RuleDocument.DisplayName = DisplayName_
            End Get

        End Property '   IsRuleDoc
        '-----------------------------------------------------------------------
        '   IsDirty Property
        '
        '   Gets or sets the Document.Dirty property.
        '-----------------------------------------------------------------------
        Public Property IsDirty As Boolean

            Get
                Return Doc_.Dirty
            End Get

            Set(value As Boolean)
                Doc_.Dirty = value
            End Set

        End Property        '   IsDirty
        '-----------------------------------------------------------------------
        '   IsDerivationActive Property
        '   
        '   Assumes this class represents an Inventor derived part. This
        '   property sets or gets the status of the Inventor part derivation
        '   from the Inventor part whose display name is supplied, using True
        '   for active and False For suppressed.
        '-----------------------------------------------------------------------
        Public Property IsDerivationActive _
                        (ByVal BasePartDisplayName As String) As Boolean

            Get

                Dim dpc As DerivedPartComponent
                dpc = GetDerivedPartComponent(BasePartDisplayName)
                If dpc Is Nothing Then Return False
                If dpc.SuppressLinkToFile Then Return False
                Return True

            End Get

            Set(ByVal shouldMakeActive As Boolean)

                Dim dpc As DerivedPartComponent
                dpc = GetDerivedPartComponent(BasePartDisplayName)
                If dpc Is Nothing Then Return
                If shouldMakeActive Then
                    dpc.SuppressLinkToFile = False
                Else
                    dpc.SuppressLinkToFile = True
                End If

            End Set

        End Property    '   IsDerivationActive  
        '--------------------------- --------------------------------------------
        '   IsDerivationActive Property
        '   
        '   Assumes this class represents an Inventor base part. Setting this
        '   property instructs the Inventor derived part whether the derivation
        '   link should be active, using True for active and False for
        '   suppressed. Getting this property reports the same information
        '   without changing it.
        '-----------------------------------------------------------------------
        Public Property IsDerivationActive(DerivedPart As Part) As Boolean

            Get
                Return DerivedPart.IsDerivationActive(DisplayName_)
            End Get

            Set(ByVal ShouldMakeActive As Boolean)
                DerivedPart.IsDerivationActive(DisplayName_) = ShouldMakeActive
            End Set

        End Property    '   IsDerivationActive  
        '-----------------------------------------------------------------------
    End Class   '   Part
    '===========================================================================
End Namespace   '   Inventor
0 Likes
261 Views
1 Reply
Reply (1)
Message 2 of 2

DonStauffer99
Advocate
Advocate

I did some reading, and of all the **** stupid things Microsoft ever did, especially regarding object-oriented programming, this has to be the stupidest ever. Some moron had to have thought this up.

They took away class destructors in VB.

That's right, Finalize doesn't work as a destructor, and neither does Dispose, and they took away Class_Terminate. So VB HAS NO DESTRUCTORS!

Finalize will (eventually) run when garbage collection decides to reclaim the resources, but you never know when that will be. For me, it seems to be depending on where the object got created. If it's in an Inventor rule, Finalize runs when the rule finishes. But if it's in a class library DLL, it's - wait for it - when Inventor CLOSES.

Dispose is worthless because it doesn't automatically run when the object goes out of scope, which is the whole point of destructors. You have to call Dispose explicitly, so it's really just like any other method. I could have written an "ImDoneWithThisObject()" method. That's not a destructor, and neither is Finalize.

0 Likes