8 Replies Latest reply on Apr 19, 2018 10:18 AM by Jacob Corder

    Is there any way to clear temporary bodies from model when a script is prematurely terminated?

    J. R.

      Hello,

       

      Having switched from VBA to VB.NET (on Visual Studio), I'm encoutering a bizarre behavior of termination of temporary bodies. When I run a VB.NET script that creates temporary bodies, and at the end, set the pointers to Nothing and let the script complete itself, it disposes of all temporary bodies in the model. However, if at any point of debugging my script, I terminate it prematurely (Shift+F5, or if it encounter any error and stops by itself), the temporary bodies remain in the model, and I am unable to dispose of them. The script is terminated, all pointers should be gone, but the temporary bodies are still visible. Rebuilding, redrawing model doesn't help. Even closing Visual Studio doesn't clear these bodies. The only thing that helps is closing and re-opening the model itself. It would appear that SolidWorks latches onto these temporary bodies, and they become completely independent of Visual Studio.

      I tried calling GC.Collect(), GraphicsRedraw2() and other functions at the start of my script, so it would clear out the temporary bodies of the previous debug, but it doesn't help either.

       

      I'm sure the solution is dead simple here, but I am unable to find it. Is there some API function to call at the beginning of my script to clear all temporary bodies that remain from previous debugs?

        • Re: Is there any way to clear temporary bodies from model when a script is prematurely terminated?
          Artem Taturevych

          Hi,

           

          As I understand you are using VSTA macros, right? Can you try to change this option: Stop VSTA debugger on macro exit (located on the Tools > Options > System Options dialog)

           

          Thanks,

          Artem

            • Re: Is there any way to clear temporary bodies from model when a script is prematurely terminated?
              J. R.

              No, sir. I am using VB.NET on Visual Studio 2012, not VSTA. I was unable to find such option in the Options menu...

                • Re: Is there any way to clear temporary bodies from model when a script is prematurely terminated?
                  Jacob Corder

                  First off if you are doing this in .net, you need to properly handle temporary bodies.  these SOBs will self destruct if they dont have a reference to them.  and collections do not work.

                   

                   

                  wrap your code in a

                   

                  Try
                       Do stuff, make bodies

                   

                   

                  Catch ex as exception

                  Finally 'This will ALWAYS EXECUTE

                       For i = 0 to ubound(TempBodies)

                            ReleaseBody(ModDoc,TempBodies,True)

                       next

                  End Try

                   

                  Public Shared Function ReleaseBody(ByVal ModDoc As ModelDoc2, ByRef COMObject As Body2, ByVal DisableDisplayFirst As Boolean, Optional ByVal FinalRelease As Boolean = False) As Integer

                          ReleaseBody = 1        

                              Try

                                  If DisableDisplayFirst = True AndAlso IsNothing(ModDoc) = False Then

                                      COMObject.Hide(ModDoc)

                                      If COMObject.DisableDisplay = False Then

                                          COMObject.DisableDisplay = True

                                      End If

                                  End If

                                  If IsNothing(COMObject) = False Then

                                      ReleaseBody = Marshal.ReleaseComObject(COMObject)

                                  Else

                                      ReleaseBody = 0

                                  End If

                              Catch coe As COMException

                                  ReleaseBody = 1

                              Catch ex As Exception

                                  ReleaseBody = 1

                              End Try      

                          COMObject = Nothing

                      End Function

                    • Re: Is there any way to clear temporary bodies from model when a script is prematurely terminated?
                      J. R.

                      Thank you for your answer. As I understand it, temporary bodies should indeed destroy themselves when the reference to them is deleted. For some reason, they don't in VB.NET, even if I close Visual Studio.

                       

                      When you said to wrap my code in that Try statement, did you mean wrap everything, or each function and sub individually? I fail to understand how this will help if, for example, I set a breakpoint in any point of my code, and then stop the script from running - then the exception won't happen, isn't that right?

                        • Re: Is there any way to clear temporary bodies from model when a script is prematurely terminated?
                          Jacob Corder

                          well if you are making temp bodies in .net, you need to make sure all references to these bodies are released.

                           

                          They add GDI handles.

                           

                          huge memory leaks can occur.

                           

                          try

                           

                          catch EX as exception

                           

                          Finally

                           

                          end try

                           

                          this is so important to use because if an exception occurs then the release of the bodies always happens in the finally block.

                           

                          you can even call return  in the try catch statement,and the finally will always get hit.

                           

                          it is a guarantee that the release of the bodies  happens.

                           

                          without the try catch finally, you cannot guarantee that they will be released.

                           

                          and seriously you should be try catch finally pretty much anything you retrieve from sldworks.

                           

                          Like

                           

                          Dim SwSurface as surface=nothing

                          try

                               SwSurface = SwFace.GetSurface

                               Do Something with the surface     Return Something 'Still Finally will get called releasing the reference to the SwSurface

                           

                          catch ex as exception 'if error occurs then Finally is executed

                           

                          finally 'if No error occurs then Finally is executed

                               marshal.releasecomobject(SwSurface)

                          end try

                          • Re: Is there any way to clear temporary bodies from model when a script is prematurely terminated?
                            Jacob Corder

                            also they aren't destroying themselves due to Garbage collection has not finalized those items.

                             

                            you can actually create a weakreference to a temporary body and when it finalizes you will know. i use ConditionalWeakTable(of object, Of A custom class that notifies)

                             

                            basically this binds the 2 together so when the Object (Of Object) is finalized then so is your custom class that is built to be destroyed)

                             

                            here is the class that i use. feel free to use it or not.

                             

                            Imports System.Runtime.CompilerServices
                            
                            
                            Public Class GCNotifier
                                Implements IDisposable
                                Private map As New ConditionalWeakTable(Of Object, Notifier)
                                Sub New()
                            
                            
                                End Sub
                                Function IsSafe(ThisObj As Object) As Boolean
                                    Dim ThisNot As Notifier = Nothing
                                    If IsNothing(map) = False And IsNothing(ThisObj) = False Then
                                        Try
                                            map.TryGetValue(ThisObj, ThisNot)
                                            If IsNothing(ThisNot) = False Then
                                                Return ThisNot.IsSafe
                                            End If
                                        Catch ex As Exception
                                        End Try
                                    End If
                                    Return True
                                End Function
                                Function AddDisplayDimension(ByVal ThisObj As SolidWorks.Interop.sldworks.DisplayDimension, ByVal Description As String) As Notifier
                                    'Description is used for callback purposes. use anything you want. it is not required
                                    If IsNothing(ThisObj) = False Then
                                        If IsNothing(map) Then map = New ConditionalWeakTable(Of Object, Notifier)
                                        Dim NewNot As Notifier = map.GetOrCreateValue(ThisObj)
                                        If IsNothing(NewNot.m_ObjectToWatch) And IsNothing(NewNot.M_DispDimension) And IsNothing(NewNot.M_Annotation) Then
                                            NewNot.Description = Description
                                            NewNot.M_DispDimension = ThisObj
                                            'Below, only finalized gets called as IDisplayDimensions dont implement IDisposable
                                            AddHandler NewNot.ObjectDisposed, AddressOf Me.ObjectDisposed
                                            AddHandler NewNot.ObjectFinalized, AddressOf Me.ObjectFinalized
                                            Return NewNot
                                        End If
                                    End If
                                    Return Nothing
                                End Function
                                Function AddAnnotation(ByVal ThisAnn As SolidWorks.Interop.sldworks.Annotation, ByVal Description As String) As Notifier
                                    'Description is used for callback purposes. use anything you want. it is not required
                                    If IsNothing(ThisAnn) = False Then
                                        If IsNothing(map) Then map = New ConditionalWeakTable(Of Object, Notifier)
                                        Dim NewNot As Notifier = map.GetOrCreateValue(ThisAnn)
                                        If IsNothing(NewNot.m_ObjectToWatch) And IsNothing(NewNot.M_DispDimension) And IsNothing(NewNot.M_Annotation) Then
                                            NewNot.Description = Description
                                            NewNot.M_Annotation = ThisAnn
                                            'Below, only finalized gets called as IDisplayDimensions dont implement IDisposable
                                            AddHandler NewNot.ObjectDisposed, AddressOf Me.ObjectDisposed
                                            AddHandler NewNot.ObjectFinalized, AddressOf Me.ObjectFinalized
                                            Return NewNot
                                        End If
                                    End If
                                    Return Nothing
                                End Function
                                Function AddObject(ByVal ThisObj As Object, ByVal Description As String) As Notifier
                                    'Description is used for callback purposes. use anything you want. it is not required
                                    If IsNothing(ThisObj) = False Then
                                        If IsNothing(map) Then map = New ConditionalWeakTable(Of Object, Notifier)
                                        Dim NewNot As Notifier = map.GetOrCreateValue(ThisObj)
                                        If IsNothing(NewNot.m_ObjectToWatch) And IsNothing(NewNot.M_DispDimension) And IsNothing(NewNot.M_Annotation) Then
                                            NewNot.Description = Description
                                            NewNot.m_ObjectToWatch = ThisObj
                                            'Below, only finalized gets called normally unless the object implements IDisposable
                                            AddHandler NewNot.ObjectDisposed, AddressOf Me.ObjectDisposed
                                            AddHandler NewNot.ObjectFinalized, AddressOf Me.ObjectFinalized
                                            Return NewNot
                                        End If
                                    End If
                                    Return Nothing
                                End Function
                            
                            
                                Sub RemoveObject(ByVal ThisObj As Object)
                                    Dim ThisNot As Notifier = Nothing
                                    If IsNothing(ThisObj) = False And IsNothing(map) = False Then
                                        Try
                                            map.TryGetValue(ThisObj, ThisNot)
                                            If IsNothing(ThisNot) = False Then
                                                'Remove Handlers
                                                RemoveHandler ThisNot.ObjectDisposed, AddressOf Me.ObjectDisposed
                                                RemoveHandler ThisNot.ObjectFinalized, AddressOf Me.ObjectFinalized
                                                map.Remove(ThisObj)
                                                ThisNot.Dispose()
                                            End If
                                        Catch ex As Exception
                                        End Try
                                    End If
                                End Sub
                                Sub ObjectDisposed(ByVal Sender As Object, ByVal E As EventArgs)
                                    '   Debug.Print("Object Disposed")
                                End Sub
                            
                            
                                Sub ObjectFinalized(ByVal Sender As Object, ByVal E As EventArgs)
                                    ' Debug.Print("Object Finalized")
                                    If IsNothing(Sender) = False AndAlso TypeOf Sender Is Notifier Then
                                        Dim ThisNot As Notifier = Sender
                                        Try
                                            'Remove all objects being watched. the type of object can be modified where you can watch bodies, displayDimensions, faces,edges ect
                                            If IsNothing(ThisNot.m_ObjectToWatch) = False Then
                                                RemoveObject(ThisNot.m_ObjectToWatch)
                                            End If
                                            If IsNothing(ThisNot.M_DispDimension) = False Then
                                                RemoveObject(ThisNot.M_DispDimension)
                                            End If
                                            If IsNothing(ThisNot.M_Annotation) = False Then
                                                RemoveObject(ThisNot.M_Annotation)
                                            End If
                                        Catch ex As Exception
                                        End Try
                                    End If
                                End Sub
                                Sub RemoveAllConnections()
                                    map = Nothing
                                End Sub
                                Class Notifier
                                    Implements IDisposable
                            
                            
                                    Public Description As String = ""
                                    'Add as many specific object types you want here
                                    Public m_ObjectToWatch As Object
                                    Public M_DispDimension As SolidWorks.Interop.sldworks.DisplayDimension
                                    Public M_Annotation As SolidWorks.Interop.sldworks.Annotation
                                    Public Event ObjectDisposed As EventHandler
                                    Public Event ObjectFinalized As EventHandler
                            
                            #Region "IDisposable Support"
                                    Sub ReRegisterForFinalize()
                                        If IsNothing(M_DispDimension) = False Then
                                            GC.ReRegisterForFinalize(M_DispDimension)
                                        End If
                                        If IsNothing(m_ObjectToWatch) = False Then
                                            GC.ReRegisterForFinalize(m_ObjectToWatch)
                                        End If
                                        If IsNothing(M_Annotation) = False Then
                                            GC.ReRegisterForFinalize(M_Annotation)
                                        End If
                                    End Sub
                                    Private disposedValue As Boolean ' To detect redundant calls
                                    Public Function Issafe() As Boolean
                                        Return Not disposedValue
                                    End Function
                            
                            
                                    ' IDisposable
                                    Protected Overridable Sub Dispose(disposing As Boolean)
                            
                            
                                        If Not Me.disposedValue Then
                                            If disposing Then
                                                RaiseEvent ObjectDisposed(Me, New EventArgs)
                                                ' TODO: dispose managed state (managed objects).
                                            End If
                            
                            
                                            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                                            ' TODO: set large fields to null.
                                        End If
                                        If disposing = False Then RaiseEvent ObjectFinalized(Me, New EventArgs)
                                        m_ObjectToWatch = Nothing
                                        M_DispDimension = Nothing
                                        M_Annotation = Nothing
                                        Me.disposedValue = True
                                    End Sub
                            
                            
                                    ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
                                    Protected Overrides Sub Finalize()
                                        
                                        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
                                        Dispose(False)
                                        MyBase.Finalize()
                                    End Sub
                            
                            
                                    ' This code added by Visual Basic to correctly implement the disposable pattern.
                                    Public Sub Dispose() Implements IDisposable.Dispose
                                        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
                                        Dispose(True)
                                        GC.SuppressFinalize(Me)
                                    End Sub
                            #End Region
                            
                            
                                End Class
                            
                            
                            
                            
                            
                            
                            #Region "IDisposable Support"
                                Public Function DestroyAll() As Boolean
                                    'Probably a better way to handle this.
                                    Finalize()
                                End Function
                                Private disposedValue As Boolean ' To detect redundant calls
                            
                            
                                ' IDisposable
                                Protected Overridable Sub Dispose(disposing As Boolean)
                                    If Not Me.disposedValue Then
                                        If disposing Then
                                            ' TODO: dispose managed state (managed objects).
                                        End If
                                        If IsNothing(map) = False Then
                                            map = Nothing
                                        End If
                                        ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                                        ' TODO: set large fields to null.
                                    End If
                                    Me.disposedValue = True
                                End Sub
                            
                            
                                ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
                                Protected Overrides Sub Finalize()
                                    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
                                    Dispose(False)
                                 
                                    MyBase.Finalize()
                                End Sub
                            
                            
                                ' This code added by Visual Basic to correctly implement the disposable pattern.
                                Public Sub Dispose() Implements IDisposable.Dispose
                                    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
                                    Dispose(True)
                                    GC.SuppressFinalize(Me)
                                End Sub
                            #End Region
                            
                            
                            End Class
                            
                        • Re: Is there any way to clear temporary bodies from model when a script is prematurely terminated?
                          Jacob Corder

                          this is the code i use to Release Temporary Bodies.

                           

                          I have a whole class of disposals to handle releases properly.

                          • Re: Is there any way to clear temporary bodies from model when a script is prematurely terminated?
                            Jacob Corder

                            it probably has something to do with GC.Finalize not being called on those items.