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