Message 1 of 2
Class destructor doesn't run
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
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