Can't get code to work: Finding total Areas

Can't get code to work: Finding total Areas

Anonymous
Not applicable
1,584 Views
10 Replies
Message 1 of 11

Can't get code to work: Finding total Areas

Anonymous
Not applicable

I've been trying to find helpful documentation to start writing applications for my CAD team, but with the move to Cad 2011, Windows 7 64Bit, ect...there's nothing I can find that will show me even basic stuff like attaching to AutoCAD and grabbing the area of all entities.  Here's what I have writen down.  I know it doesn't work, because it comes up with an error message and crashed before it gets into the Sub.

 

 

Option Explicit On

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Geometry
Imports System.Text
Imports System

Public Class Form1

    Friend Sub DoStuff() Handles Button1.Click

        MsgBox("Neat")

        ' Get active AutoCAD Application
        Dim app As Application = GetObject(, "AutoCAD.Application")

        ' Get active document
        Dim doc As Document = app.DocumentManager.MdiActiveDocument

        ' Create the database object
        Dim dwg As Database = doc.Editor.Document.Database

        ' Populate the selection set with objects that match the filter criteria
        Dim sel As SelectionSet = doc.Editor.SelectAll().Value

        ' Start a transaction
        Dim trans As Transaction = dwg.TransactionManager.StartTransaction

        ' Loop through each selected object in the selection set
        Dim entSet As New DBObjectCollection

        Try
            For Each selEnt As SelectedObject In sel
                Dim ent As Entity = trans.GetObject(selEnt.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite)
                Dim entType As String = ent.GetType.ToString

                Dim entArea

                Select Case ent.GetType.ToString
                    Case "Autodesk.AutoCad.DatabaseServices.DBArc"
                        Dim arcEnt As Arc = ent
                        entArea = arcEnt.Area
                    Case "Autodesk.AutoCad.DatabaseServices.DBCircle"
                        Dim cirEnt As Circle = ent
                        entArea = cirEnt.Area
                    Case "Autodesk.AutoCad.DatabaseServices.DBEllipse"
                        Dim eliEnt As Ellipse = ent
                        entArea = eliEnt.Area
                    Case "Autodesk.AutoCad.DatabaseServices.DBPolyline"
                        Dim plyEnt As Polyline = ent
                        entArea = plyEnt.Area
                    Case "Autodesk.AutoCad.DatabaseServices.DBRegion"
                        Dim regEnt As Region = ent
                        entArea = regEnt.Area
                    Case "Autodesk.AutoCad.DatabaseServices.DBSpline"
                        Dim splEnt As Spline = ent
                        entArea = splEnt.Area
                    Case Else
                End Select
            Next
        Catch ex As Autodesk.AutoCAD.Runtime.Exception

        End Try
    End Sub
End Class

 

 

It's simple.  All I want to do is go to each entitiy in the drawing and find their area.  I plan on combining the areas to show total area.  But the program crashes before I can even get it to work.  I have two warnings right now displaying, so those may be it.

 

 

Warning	1	Possible problem detected while building assembly 'TestTestTestTest': Referenced assembly 'mscorlib.dll' targets a different processor	TestTestTestTest

Warning	2	Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated.	U:\settings\My Documents\Visual Studio 2010\Projects\TestTestTestTest\TestTestTestTest\Form1.vb	21	31	TestTestTestTest

 

 

0 Likes
Accepted solutions (1)
1,585 Views
10 Replies
Replies (10)
Message 2 of 11

norman.yuan
Mentor
Mentor

In regard of the warning you get, since you use 64-bit OS (and 64-bit Acad, I suppose), make sure your project is built to target "Any CPU", or "x64" (if you only expect your app is only used with 64-bit Box).

 

Is your app an stand-alone exe that automate AutoCAD, or it is Acad managed dll, running inside AutoCAD (loaded into AutoCAD with "NETLOAD")?

 

I raid this question because of this line of code:

 

       ' Get active AutoCAD Application
        Dim app As Application = GetObject(, "AutoCAD.Application")

If it is former, the all the code after this line will not run, because these lines of code uses acad managed APIs, which can ONLY run inside AutoCAd, not an external EXE.

 

If it is latter, then you get things wrong: GetObject(,"AutoCAD.Application") returns an COM object (AcadApplication), which you do not needed in your managed Acad API app and it DOES NOT have property "DocumentManager" as your following code needs.

 

My wild guess is your app is the latter and the code of above line causes crash (since you do not have exception handling there). In this case, you need to replace the above line of code with

 

Dim app As Application=Autodesk.AutoCAD.ApplicationServices.Application

 

After this change, you code might be OK with still one possiblity of exception: lack of locking the document. It seems you want user clicks a button on the form and let AutoCAD select all entities. If the form is shown as modeless form, you must lock the document before start a transaction.

 

Another small issue: for reading area of an entity, you dod not change the entity in any way, thus, do not open entity with OpenMode.ForWrite, OpenMode.ForRead is sufficient.

 

Even you open entity only for read, you do not leave a transaction not commited or not rolled back. That is, alway commit the transaction (or roll back the transaction if necessary).

 

You'd also better wrap the transaction with Using...End Using statement, so that when the transaction is done, it would be disposed properly.

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 11

Anonymous
Not applicable

I appolosige if it's not that clean.  This was me trying to run an upgrade of a VB6 program and then cleaning up the parts that didn't import well.

 

I've made managed DLL's before for AutoCAD, but this was one another co-worker made that was a stand alone program that hooked into the program from COM.  I didn't know if I should rewrite it or not.  I guess that answers my question.

 

But here's a new problem.  I created a small DLL and built it, then went to load it to test it out and I was told that the framework (4.0) was newer then what AutoCAD was using.  So, I turned my framework down to 3.5, and now I'm getting an error when I build...

 

 

Warning	1	The primary reference "acmgd" could not be resolved because it has an indirect dependency on the framework assembly "System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" which could not be resolved in the currently targeted framework. ".NETFramework,Version=v3.5,Profile=Client". To resolve this problem, either remove the reference "acmgd" or retarget your application to a framework version which contains "System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".	AreaCalcs

 

Else, is there a stand-alone way of doing this application?  I wanted to make it easy enough for my CAD team so they just had to push a button and they would get all of the layers on a drawing and what the total area of all objects was.

 

0 Likes
Message 4 of 11

norman.yuan
Mentor
Mentor
Accepted solution

OK, if it is a stand-alone app, the code you showed in original post simply cannot work: you cannot use acad managed API in external app.

 

With minor change to that code (as I showed in previous reply), of course, you can make a managed API app running inside Acad. With the combination of Acad2011 and VS2010, you simply set the target .NET framework to .NET3.x in the VS2010 project.

 

if you still want to do this in external exe app, you have to stick with COM API, with minor changes to the VB6 code, not the managed API.

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 11

Anonymous
Not applicable

I think I've got it now.  I have converted it to a managed dll inside of AutoCAD.  Problem is that the CAD guys I was testing it for say that it takes too long.  Do internal managed dlls process slower then external EXE files?  Or am I doing something wrong?

0 Likes
Message 6 of 11

chiefbraincloud
Collaborator
Collaborator

Do internal managed dlls process slower then external EXE files?

 

Should be very much the opposite.  The simpler the program, the less noticeable it would be, but all of my Managed Code runs noticeably faster than it's old COM couterpart.

Dave O.                                                                  Sig-Logos32.png
0 Likes
Message 7 of 11

chiefbraincloud
Collaborator
Collaborator

I could suggest a couple of things that may improve the performance somewhat.

 

One, you could use a selection filter 'OR'-ing all of the entity types you care about, like this:

 

Dim 

tvs As TypedValue() = New TypedValue() {New TypedValue(DxfCode.Operator, "<or"), _

New TypedValue(DxfCode.Start, "ARC"), _

New TypedValue(DxfCode.Start, "CIRCLE"), _

New TypedValue(DxfCode.Start, "ELLIPSE"), _

New TypedValue(DxfCode.Start, "POLYLINE"), _

New TypedValue(DxfCode.Start, "REGION"), _

New TypedValue(DxfCode.Start, "SPLINE"), _

New TypedValue(DxfCode.Operator, "or>")}

Dim sel As SelectionSet = doc.Editor.SelectAll(New SelectionFilter(tvs)).Value

 

That would make sure you were only looping through and opening objects you care about.

 

The other way to accomplish a similar benefit is to check the ObjectId.ObjectClass.Name property prior to opening the objects

 

So Instead of this:

Dim trans As Transaction = dwg.TransactionManager.StartTransaction
        Try
            For Each selEnt As SelectedObject In sel
                Dim ent As Entity = trans.GetObject(selEnt.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead)
                               Select Case ent.GetType.ToString

                                            Case ...

 

Do this:

Dim trans As Transaction = dwg.TransactionManager.StartTransaction
        Try
            For Each selEnt As SelectedObject In sel
                               Select Case selEnt.ObjectId.ObjectClass.Name

                                            Case "AcDbArc"

                                                    Dim crv As Curve = trans.GetObject(selEnt.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead)

 

The thing to note about that is that the return value of ObjectClass.Name is the ObjectARX name, so, like I did in the example case the names (for all entity types) should be "AcDb" followed by the .Net Typename, like Arc or Circle... etc.

 

The only exceptions to that rule that I am aware of are classes which have '3d' or '2d' in the name, such as the .NET class Solid3d = the Arx class AcDb3dSolid.  So the '3d' part moved from the end to the middle.  The others like that follow the same pattern, I believe.

 

Basically either of these methods will prevent you from having to open every object to test it's type, and I didn't point it out, but the Area property is a member of the Curve Class, so you only have to open everything as Curve, instead of having to open as Entity, and then Cast to Arc, Circle, Spline, etc.

 

Also note, since the function (as it is posted) does not make any changes, so you should open the objects for Read, not for Write.

 

Dave O.                                                                  Sig-Logos32.png
0 Likes
Message 8 of 11

Anonymous
Not applicable

That worked, but it's still kinda slow.  Maybe it's because the drawing still has a ton of entities that it tries to select.  I want to limit the layers it's going to select by giving the user the ability to choose layers they want to select.  I have something building a sorted list of layers, now I just need to know how to append that filter criteria to the one you helped me build.  Basically, I want it to:

 

Select entities Where type in (list of types) And LayerName In (list of selected layer names)

0 Likes
Message 9 of 11

chiefbraincloud
Collaborator
Collaborator

I wasn't sure it was going to work, but I tested this out and it does work.

Obviously if you are collecting a list of layers at runtime, you will need to build your TypedValue array dynamically, but it just shows that you are -ANDing two -ORs, one OR full of Layer names, the other OR full of Object Types.

 

Dim tvs As TypedValue() = New TypedValue() {New TypedValue(DxfCode.Operator, "<and"), _

New TypedValue(DxfCode.Operator, "<or"), _

New TypedValue(DxfCode.LayerName, "0"), _

New TypedValue(DxfCode.LayerName, "DEFPOINTS"), _

New TypedValue(DxfCode.Operator, "or>"), _

New TypedValue(DxfCode.Operator, "<or"), _

New TypedValue(DxfCode.Start, "CIRCLE"), _

New TypedValue(DxfCode.Start, "ARC"), _

New TypedValue(DxfCode.Start, "LINE"), _

New TypedValue(DxfCode.Operator, "or>"), _

New TypedValue(DxfCode.Operator, "and>")}

 

Dave O.                                                                  Sig-Logos32.png
0 Likes
Message 10 of 11

chiefbraincloud
Collaborator
Collaborator

Turns out that the enclosing AND is unnecessary.  Apparently it is implicit.

Dave O.                                                                  Sig-Logos32.png
0 Likes
Message 11 of 11

Paulio
Advocate
Advocate

This may not be relevent but I thought I'd post it anyway.

You mentioned that it's a bit slow and I just wanted to say that I experienced a similar thing when moving to 2011.

A process that took 4 seconds in 2010 suddely took almost 20 in 2011

 

It turns out that the NavCube and NavBar were causing the delay (I guess it's something to do with them being redrawn at various times during the process).

I set the NAVBARDISPLAY, NAVCUBEDISPLAY and UCSICON system variables to 0 before running my process then set them back to what they were when it's finished. It got the processing time in 2011 down to 3 seconds.

 

HTH.

 

Paul

 

0 Likes