Author Topic: Layer Man crashes AutoCAD after running VB.NET code  (Read 3368 times)

0 Members and 1 Guest are viewing this topic.

slappy

  • Guest
Layer Man crashes AutoCAD after running VB.NET code
« on: February 03, 2011, 02:32:53 PM »
ACA 2010
VS Express 2010
VB.NET

The long and short is I have project which goes out and finds all the Viewports compiles their scales and produces a dialog listing available viewports with buttons to switch to desired viewport.  The code runs perfectly, except it seems to leave something behind that when you open the Layer Manager (palette or classic) Autocad throws an Unhandled Exception.

Two different ones depending on how the dialog is closed.
Dialog closed by user: faae8eh
Closed by code(user selects viewport button):fdafbeh

See attached zip file for project.  Be aware this is in VS express 2010.

All comments are appreciated.

Thanks

Slap


n.yuan

  • Bull Frog
  • Posts: 348
Re: Layer Man crashes AutoCAD after running VB.NET code
« Reply #1 on: February 03, 2011, 06:49:44 PM »
Well, I looked at your code and saw quite some issues (so I did not run it). here are some of them:

1. There is no need to use COM API objects in it, especially the "GetAcadApplication()" method does not make any sense at all: your program only runs when AutoCAD is running and you loaded the DLL with NETLOAD. There is no chance for your code to "Fail to communicate with AutoCAD" when the code start running in AutoCAD.

2. In the command method, you show the form with this

vpSelectdialog.Show()

which is not recommended. Instead, you'd better use Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog()/ShowModelessDailog(). In your case, since the program works against current active drawing, not multiple drawings, using ShowModalDialog() would be preferred.

3. In the class VPFucntions' ListCurrVports() method (BTW, the method being Shared is preferred, in your case), you unnecessaarily placed current document (MdiActiveDocument), WorkingDatabase and current Layout in a Using block and call Dispose() method on them. Your code does not create/instantiate those objects, thus your code should not dispose them. Sometimes, calling Dispose() on live AutoCAD objects may be harmless, but you never know. It depends on how Autodesk implemented the Dispose() method, harm or harmless is a wild guess. The point is you SHOULD not dispose these objects.

4. In the same method, you start a Transaction, but never finish it by either calling Commit() or Abort(), your code simply go straightly to Transaction.Dispose(). This may leave some opened objects in an unstable status. Again, it depends on how Transaction.Dispose() is implemented, we as outsider can only guess that without Commit()/Abort() a transaction would be very bad thing to do. This could be the reason of the crash you described.

5. In the form's code behind, the VPSelect_Close(...) method, you instantiate a new VPDialogControl class, which is the command class. This is really not necessary. The correct way would be: the form shows as modal dialog; the form's close button does nothing but hide the form (that is, its DislogResult property being set to either OK or Cancel; then in the command method (opendialog()), after call ShowModalDialog(), you cal the DialogClosing(0 method (and remove the "GetAcadApplication()" part, you can use MdiDocument.Editor.SwitchToModel[Paper]Space(), instead of AcadDocument.MSpace=True).

Hope this help a little bit.

kaefer

  • Guest
Re: Layer Man crashes AutoCAD after running VB.NET code
« Reply #2 on: February 04, 2011, 03:02:57 AM »
4. In the same method, you start a Transaction, but never finish it by either calling Commit() or Abort(), your code simply go straightly to Transaction.Dispose(). This may leave some opened objects in an unstable status. Again, it depends on how Transaction.Dispose() is implemented, we as outsider can only guess that without Commit()/Abort() a transaction would be very bad thing to do. This could be the reason of the crash you described.

All very well what you have observed but with Transaction.Dispose() I don't agree that it's that bad.

Remember that according to http://msdn.microsoft.com/en-us/library/yh598w02%28v=VS.100%29.aspx the using statement around the Transaction is equivalent to
Code: [Select]
    {
        Transaction tr = db.TransactionManager.StartTransaction();
        try {
            ...
            // Exception occurs here
            ...
            // and here should be tr.Commit(); or tr.Abort();
        } finally {
            if (tr != null)
                ((IDisposable)tr).Dispose();
        }
    }

Luckily it's not the case that everytime when we receive an error at the position indicated that AutoCAD will crash and burn. So there's a certain guarantee that the roll-back performed by Transaction.Abort() will be performed by Transaction.Dispose() too. Otherwise we would be obliged to wrap each transaction on our own with another try/catch/finally which calls Abort() in the catch clause. As I see it, this is almost never done.

Regards, Thorsten

Glenn R

  • Guest
Re: Layer Man crashes AutoCAD after running VB.NET code
« Reply #3 on: February 04, 2011, 08:55:06 AM »
If memory serves, just disposing of a transaction and NOT calling Commit OR Abort will call Abort implicitly, however the cost of Aborting (rollback) in relation to Commiting (even if only reading) is not really worth it. Only Abort if you absolutely have to.

Something like this perhaps:
Code: [Select]
Transaction tr = null;
try {
  tr = db.TransactionManager.StartTransaction();
  // Some code that might cause an exception
  // Get to here, all's good, so commit
  tr.Commit();
} catch (Exception ex) {
  // do something with exception if need be
  tr.Abort();
} finally {
  if (tr != null)
    tr.Dispose();
}

Cheers
Glenn

slappy

  • Guest
Re: Layer Man crashes AutoCAD after running VB.NET code
« Reply #4 on: February 04, 2011, 10:09:09 AM »
Thanks n.yuan, you have given me alot to think about...

FYI - I did do a slight overhaul which removed all Autocad Interop calls, thus removing GetApplication.  However, the crash is still prevelent.  I set the dialog as modeless to test a theory. Originally it was modal.

I will look into each area you pointed out and see what I can come up with.  In your assesment, do you think the transaction is appropriate?  If it's only a retrieval of information and not and exchange would something else less invasive work?

Thanks again.  As you can tell I'm new at this and still have alot to learn.

slappy

  • Guest
Re: Layer Man crashes AutoCAD after running VB.NET code
« Reply #5 on: February 04, 2011, 12:45:09 PM »
Awesome!

I revamp per your suggestions and the issue has gone away.  I believe it was in the "Using" of the database and document.  However, there still remains some, um..., annoyances that bother me.  If I could bother someone again to take a look.

First, on clicking a created button, a background program window will flash in front of AutoCAD.  It seems what ever program had focus last.

Second, I've provided a snap shot of my Immediate Window from VS express that shows a number of errors that I can't track down.  Please offer thoughts on these.

Please see attached project.

Thanks again.

slappy

  • Guest
Re: Layer Man crashes AutoCAD after running VB.NET code
« Reply #6 on: February 04, 2011, 12:47:22 PM »
oops, heres the project.

n.yuan

  • Bull Frog
  • Posts: 348
Re: Layer Man crashes AutoCAD after running VB.NET code
« Reply #7 on: February 07, 2011, 01:23:59 PM »
I ran your code and did see the other window (whatever the previous active Windows app before AutoCAD window is activated) flash on top of Acad window when cliking the button on your form.

I am not sure why you have to add controls (buttons) to the form at runtime, which make things quite complicated, and could explain why you got those errors shown in the immediate window (you attached in the ImmediateWindow.txt).

I did a little bit quick fix that eliminated the flashing issue:


1. In the class vpSelect.vb (the form's code behind):

Added a public variable _SelectedVPortNumber. (note: all the public member field you declared in the form should better be private member, and only expose those that need to be accessed by other classes out side the form as public properties. But here, fo rthe simpilcity, I just use an public member)

Public _SelectedVportNumber As Integer


In the HandleSetViewport() button click event handling method, the changes are shown below, with comments

Code: [Select]
   Private Sub HandleSetViewport(ByVal sender As System.Object, ByVal e As System.EventArgs)
        If TypeOf sender Is Button Then
            Dim sendername As String = sender.text
            Dim vpNum As Integer = Nothing
            Dim vpInfo As VPInfo
            For Each vpInfo In _scaleList
                If vpInfo.scaleName = sendername Then
                    vpNum = vpInfo.VPNum
                    Exit For
                End If
            Next

            ''Get the input from the form
            ''and make it available to code outside the form
            _SelectedVportNumber = vpNum
            ''hide the form and indicate the form's Dialogresult statu
            Me.DialogResult = DialogResult.OK

            '' leave this task done outside the form
            '' and leave the code the calls the form to decide
            ''if the form needs to be disposed or not

            ''vpfunctions.SetViewport(vpNum)
            ''Me.Dispose()
        End If
    End Sub

Also, the VPSelect_Disposing() and calling GC.Collect() in it is completely unnecessary.

2. In vpMain class, the change is to wrap the dialog form in a "Using...End Using" block:

Code: [Select]
<CommandMethod("VPselect")> _
    Public Sub opendialog()
        If AutoCAD.ApplicationServices.Application.GetSystemVariable("tilemode") = 0 Then
            _drawingUnit = AutoCAD.ApplicationServices.Application.GetSystemVariable("measurement")
            nTilemode = AutoCAD.ApplicationServices.Application.GetSystemVariable("tilemode")
            nCVPort = AutoCAD.ApplicationServices.Application.GetSystemVariable("cvport")
            If nCVPort = 1 Then
                Try
                    Dim acadDoc As Document = AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
                    acadDoc.Editor.SwitchToModelSpace()
                Catch ex As System.Exception
                    MsgBox("Failed to find a Viewport.")
                    Exit Sub
                End Try
            End If

            ''Wrap the form in Using...block
            Using vpSelectdialog As New vpSelect
                ''Only do "SetViewPort()" when the form is OKed. If the user click "x" at form's corner
                ''or the Cancel/Close button (you should have this button on the form), then do nothing
                If AutoCAD.ApplicationServices.Application.ShowModalDialog(vpSelectdialog) = DialogResult.OK Then
                    vpfunctions.SetViewport(vpSelectdialog._SelectedVportNumber)
                End If
            End Using
        Else
            MsgBox("Cannot switch Viewports in Model Space!")
        End If

    End Sub

HTH

slappy

  • Guest
Re: Layer Man crashes AutoCAD after running VB.NET code
« Reply #8 on: February 07, 2011, 02:35:11 PM »
Again, AWESOME!  It makes so much sense! :lol:

I create buttons at runtime because I want the button list to be dynamic, right or wrong.  The buttons need to reflect all available viewports and respective scales.  This conveys to the user whether or not a viewport exists that is either unnecessary or scaled inaccurately. Thus the scalelist hashtable, duplicate tracking and non-standard scale formating.

Anyway, I am open to suggestions if my approach is unconventional or even palettable  :ugly:

I also forewent the cancel button.  The consequences of picking a scale/button is a non-issue, I didn't want them to be confused with yet another button...  Yes, I have very confused users.  Architects!  :lmao:

Anyway, I look forward to more enlightenment.  For now it works far better than before and I REALLY appreciate the help.