Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
DE205-4
Wondering what all the hype is around .NET? We'll give you the basic information you need to get up to speed on writing your own .NET applications based on the AutoCAD .NET application programming interface (API). This foundation class includes: an overview of the .NET Framework and what is means for programmers; how to write your first AutoCAD .NET add-in; simple user interaction; a discussion of the structure of the DWG database; how to add CAD entities to the DWG database; and how to traverse the entities already in the DWG. Make sure you also sign up for our "AutoCAD .NET: A Tour" class if you attend this.
About the Speaker: Stephen has been a member of the Autodesk Developer Technical Services Team since 2000, first as a support engineer and then as manager of the EMEA (Europe, Middle East, and Africa) Team. In those roles, his responsibilities included support for the AutoCAD APIs, including ObjectARX and AutoCAD .NET, as well as AutoCAD OEM and RealDWG technologies. Currently, he manages the Developer Technical Services Team in the Americas and serves as Workgroup Lead, working closely with the AutoCAD engineering team on future improvements in the AutoCAD APIs. Stephen started his career as a scientist, and has a Ph.D in Atomic and Laser Physics from the University of Oxford.
A note to beginners API means Application Programming Interface. Thats the set of functions AutoCAD exposes for you to use in your application to make AutoCAD do what you want.
The AutoCAD .NET API is a wrapper on top of the (unmanaged) C++ ObjectARX API. ObjectARX programmers have a big advantage over VB or LISP programmers, because the AutoCAD .NET Object Model is based on ObjectARX.
missing from the 2005 release), and adding lots of Editor functionality (such as InputPointMonitors, SelectionSets, advanced command line interaction, etc.) and a comprehensive Event model. Since the AutoCAD 2006 release, the .NET API architecture has remained unchanged. Changes since then have been in adding new APIs corresponding to new product features. In fact, some AutoCAD APIs (the CUI API, for example) are only available as .NET APIs. However, the 2007 API did take advantage of AutoCAD 2007 being a binary incompatible release to move from .NET Framework 1.1 to .NET Framework 2. As the API now uses features introduced in Framework 2, it is no longer possible to develop AutoCAD .NET applications using an IDE that relies on older .NET Frameworks3.
If youve ever been subjected to a learn to program tutorial before, youll know that every beginners tutorial in the world starts with a Hello World project. So who are we to break with decades of tradition? Once Visual Studio has started, you need to create a new project. Go to the File menu and select New->Project.
Unsurprisingly, this displays the New Project dialog. Were going to create a small VB.NET project. The steps are pretty much the same for C#; its just the C# syntax around the AutoCAD API calls that differs. Well show a C# project in the class.
3
4
You dont have to buy Visual Studio to write your AutoCAD .NET add-ins. There are a number of free development environments available. For example, you can use Visual Studio Express, but you have to tweak it a little to get it to work see Kean Walmsleys blog posting for details (http://through-theinterface.typepad.com/through_the_interface/2006/07/debugging_using.html).
Click on the Visual Basic project type and select the Class Library Template. Then give the project a name. You can call it anything you like, but (being a slave to tradition) Im going to call it HelloWorld.
When youve done that, click OK. Now open the Solution Explorer window (the View->Solution Explorer menu item) if its not already open; double click on My Project; and select the References tab.
Click the Add button, select the Browse tab; navigate to C:\Program Files\AutoCAD 2009 (or wherever your AutoCAD is installed), and search for *mgd.dll (i.e. type *mgd.dll in the File name textbox and click OK).
This will display all the files in that folder ending with mgd.dll. Now select acmgd.dll and acdbmgd.dll by Ctrl-clicking on them both; then click OK. These are the two DLLs that expose the AutoCAD .NET API. acmgd.dll exposes all the Editor functionality (SelectionSets, Commands and Keywords, Prompts and Dialogs, etc.); acdbmgd.dll exposes all the Database access functionality (the DWG Database itself, Lines, Circles, MText, etc.).
Back on the My Project screen References tab, double-click on acdbmgd.dll to display its Properties, and set Copy Local to False. (This is to ensure your .NET add-in loads the copy of acdbmgd.dll stored in the AutoCAD folder and doesnt try to make and load a local copy. You probably guessed that from the property name). Then do the same for acmgd.dll.
Now we want to write our code. Double-click on Class1.vb in the Solution Explorer window. This is where you write your add-in code. Edit the template code as follows:
Public Class Class1 <CommandMethod("HelloWorld")> _ Public Sub MyMethod() Application.ShowAlertDialog("Hello World from VB.NET!") End Sub End Class
The Imports statements allow you to use classes from the namespaces you import without having to type out the full namespace. You dont strictly need them, but the code becomes hard to read otherwise. For example,
Application.ShowAlertDialog("Hello World from VB.NET!")
becomes
There are several other Autodesk.AutoCAD namespaces, but these are the three youll need in just about every AutoCAD add-in you write. (In this example, DatabaseServices is not strictly necessary).
<CommandMethod("HelloWorld")> is an attribute. This is telling AutoCAD to run the
subroutine MyMethod when the user types HELLOWORLD on the command line. The rest of the code should be self-explanatory. Thats all the code we need. But before we can test our first AutoCAD add-in we have to set one more project setting. Double-click again on My Project in the Solution Explorer; select the Debug tab; set the Start Action to Start external program; and enter the location of the AutoCAD executable the default location is C:\Program Files\AutoCAD 2009\acad.exe. This is telling Visual Studio that youre going to load your DLL into the AutoCAD process space to debug it5.
Now were ready to run our add-in. Hit F5 to start debugging, and AutoCAD will launch. Type NETLOAD on the command line and hit ENTER. In the NETLOAD file select dialog, navigate to the location of your compiled .NET assembly DLL. This should be in this location, unless you changed your default project settings:
The AutoCAD .NET API can only be used from inside the AutoCAD process space. You cant use admgd.dll and acdbmgd.dll in a standalone executable. (But see the section on RealDWG at the end of this document).
The assembly DLL is called HelloWorld.dll. Double-click on it to load it. Now type HELLOWORLD at the command line, and AutoCAD will display this dialog.
Congratulations! Youve just created your first custom command using AutoCAD .NET.
But lets not stop just yet. Switch back to Visual Studio and hit Shift+F5 to stop the debugging session, open up Class1.vb by double-clicking on it in the Solution Explorer, and replace all the code in that document with this:
Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Public Class Class1 <CommandMethod("HelloWorld")> _ Public Sub MyMethod() ' Some variable definitions Dim activeDoc As Document Dim BTR As BlockTableRecord ' Store a reference to the currently active document (to make the next two lines of code shorter) activeDoc = Application.DocumentManager.MdiActiveDocument ' Start a transaction to edit the DWG database Using trans As Transaction = activeDoc.TransactionManager.StartTransaction() ' Open BlockTableRecord of currently active space ready to add entity. ' (This will be ModelSpace if you've not switched to a layout, otherwise PaperSpace). BTR = trans.GetObject(activeDoc.Database.CurrentSpaceId, OpenMode.ForWrite) ' Create a new MText entity and set its text Dim txt As MText = New MText txt.Contents = "Hello World from VB.NET!" ' Add the MText to ModelSpace BTR.AppendEntity(txt) txt.SetDatabaseDefaults()
' Add the Mtext to the transaction trans.AddNewlyCreatedDBObject(txt, True) ' Commit transaction so changes persist in the DWG Database. trans.Commit() End Using End Sub End Class
Now hit F5 again; NETLOAD the HelloWorld.dll; and invoke the HELLOWORLD command again. ZOOM EXTENTS and you should see this:
Give yourself a big hug youve just created your first drawing using VB.NET!
Like any database, the DWG Database consists of a number of tables in which are stored records. For example, an MS Access database might contain a table of employees (lets call it the EmployeeTable), which might contain a record for each Employee (an EmployeeTableRecord). Like this -
In the same way, most of the information in a DWG Database is stored in SymbolTables as SymbolTableRecords. We have several types of Symbol in AutoCAD, such as Blocks, Dimension Styles, Layers, LineTypes, UCSs, Views, Viewports, and Registered Applications (used for storing Xdata). Each of these SymbolTables stores a different type of SymbolTableRecord. The most important aspect of a CAD application is to be able to draw something, so lets concentrate on how AutoCAD stores its graphical Entities6 (Lines, Circles, Polylines, etc) you draw on-screen. Graphical Entities are always stored in (or are owned by) a BlockTableRecord. Think of a BlockTableRecord as containing all the Entities that make up the definition of a Block. But just to complicate things, ModelSpace and PaperSpace are also represented in the DWG Database by BlockTableRecords. This makes sense if you think about it - Block definitions and ModelSpace both just store a bunch of graphical Entities. The only difference is that you have a window to look into the ModelSpace and PaperSpace BlockTableRecords the drawing document. To view other BlockTableRecords, you have to insert a BlockReference into ModelSpace or PaperSpace. And, of course, all the BlockTableRecords are stored in the BlockTable. Below is a screenshot from the Inspector DWG Database viewing utility7, with the tree expanded to show some BlockTableRecords and LinetypeTableRecords. Note the names of the BlockTableRecords in the BlockTable shown on the left screenshot they include Model_Space and Paper_Space. The right-hand screenshot expands the ModelSpace BlockTableRecord to show the Entities it contains (two Lines, a Circle and a Polyline in this case). Dont worry about the AcDb prefix on the class names for now.
Entity has a special meaning in AutoCAD. This is the name given to anything in the DWG Database that is represented graphically. More generally, we use the word Object (or the DBObject class) to describe anything in the database that includes both Entities and non-graphical objects.
7
11
Theres a little more to the DWG Database than just Tables. Notice the Named Objects Dictionary and Extension Dictionary shown in the Inspector tree. These are used for storing data in the DWG, but are outside the scope of this introductory class.
Transactions
Continuing with our database theme, it shouldnt come as a surprise that we have a similar mechanism for modifying the DWG Database as for modifying a relational database we use Transactions. The concept of a transaction is simple. Any changes you make within a transaction are only committed to the database when the transaction is committed. If anything goes wrong while youre modifying the database within a transaction, then you simply abort the transaction and the database is returned to the state it was in before the transaction started. This is shown in the simple flow diagram below.
12
Unique Identifiers
As for any database, its very important to be able to identify each object stored in the DWG database. Without some kind of unique identifier, there would be no way to distinguish (for example) one red circle from the other red circles in the drawing. When you add an object to the DWG Database, it is assigned two unique identifiers a Handle and an ObjectID.
Handles
A Handle is unique within a drawing, but is not unique across all the drawings open in a session. This means that two objects in two different drawings can have the same Handle. (Although combining the Handle with the drawing pathname would be unique). Handles are saved with the drawing. When you save and reopen a drawing, each object will have the same handle as it did last time. The only time an objects handle will change is if you INSERT the object into a new drawing then the Handles have to change to avoid clashes with the Handles in the drawing youre INSERTing into. The .NET DBObject class the parent class for all objects that can be stored in the DWG database exposes a Handle property, which is inherited by all the classes that derive from it.
ObjectIds
Like a Handle, an ObjectId is unique within a drawing. But an ObjectId is also unique within an AutoCAD session. No matter how many drawings you open in one session, youll never see the same ObjectId. Unlike a Handle, the ObjectId isnt saved with the drawing, So when you close and reopen a drawing, the objects in it will most likely have different ObjectIds. AutoCAD uses ObjectIds to track relationships between objects in a drawing. But when AutoCAD saves these relationships in the DWG file, it translates them to handles. The DBObject class exposes an ObjectId property. You will mostly be using ObjectIds (rather than handles) in your .NET applications. The following table summarizes the similarities differences between Handles and ObjectIds. Handle Unique in DWG Unique in session Saved with DWG Yes No Yes ObjectId Yes Yes No
To find the ObjectId of an object you have the handle for, you can call the Database.GetObjectId() function. 13
To find the Handle of an object you have an ObjectId for, you can open the object using its ObjectId and then call the DBObject.GetHandle() function.
Before we can access the Database, we have to start a new transaction. We start a transaction from the TransactionManager, which is accessed through a property of the Document.
Using trans As Transaction = activeDoc.TransactionManager.StartTransaction()
The Using keyword tells the .NET runtime to Dispose of the Transaction (trans) once the program flow has moved beyond the End Using statement. This just helps us to clean up after ourselves. (Otherwise, wed have to explicitly call the Transactions Dispose function when wed finished with it). To add an Entity to the currently active space (ModelSpace or PaperSpace), we ask the transaction to open the BlockTableRecord for the current space. We access the ObjectID of the current space using the CurrentSpaceId property of the Database, and pass it to the GetObject() method of the Transaction. Like in any database, we have a choice of opening the table for Write if were planning on adding or removing any records, or for Read if were only interested in querying the contents. In this case, were planning to add some Mtext to the BlockTableRecord, so we open it for Write.
BTR = trans.GetObject(activeDoc.Database.CurrentSpaceId, OpenMode.ForWrite)
There is an alternative to using the CurrentSpaceId property. Well mention it here for completeness, and because it ties in with our earlier discussion of the database structure To add an Entity to ModelSpace, we can work down the structure of the Database. First we need to access the Database for the currently active AutoCAD DWG document. Thats this line of code
DB = Application.DocumentManager.MdiActiveDocument.Database
14
Next we open the BlockTable. We open it inside the transaction using the Transaction.GetObject() method. (The ObjectId for the BlockTable is returned by the BlockTableId property).
BT = trans.GetObject(DB.BlockTableId, OpenMode.ForRead)
Then we open the ModelSpace BlockTable Record (again using the transaction)
MS = trans.GetObject(BT(BlockTableRecord.ModelSpace),OpenMode.ForWrite)
This looks a bit funny, because the BlockTable class is a collection of BlockTableRecords. BlockTableRecord.ModelSpace is a pre-defined constant that returns the name of the ModelSpace BlockTableRecord. If wed wanted the BlockTableRecord defining Block_A, wed have called BT(Block_A). And notice that were opening the ModelSpace BlockTableRecord for Write thats because were planning on adding some MText to ModelSpace. Next we create a new instance of an MText Entity, set its contents to the text we want to display, and add it to the current space. The commented code is pretty obvious here. The SetDatabaseDefaults simply sets the textstyle etc. of the text to the default for this drawing.
' Create a new MText entity and set its text Dim txt As MText = New MText txt.Contents = "Hello World from VB.NET!" ' Add the MText to ModelSpace BTR.AppendEntity(txt) txt.SetDatabaseDefaults()
Weve created a new MText instance and added it to ModelSpace, so we need to let the Transaction know about that change. Thats how we can make sure the MText is removed from the Database if the Transaction is aborted, or permanently added if the Transaction is committed. You have to notify the Transaction like this every time you add something new to the DWG Database. Finally, we commit the Transaction. If we get to this line of code, then everything worked fine and there were no errors. Committing the Transaction makes our changes permanent and tidies up after us (closing everything that we opened).
' Commit transaction so changes persist in the DWG Database. trans.Commit()
By the way, Ive not put any error handling in this simple code. Error handling is a very important part of .NET, and we will cover that briefly in the seminar.
15
If you open the Object Browser for the Hello World application we created in our first handout (or any AutoCAD .NET add-in project), then you can expand the acdbmgd assembly to see the namespaces it contains.
Now expand the DatabaseServices namespace in the Object Browser, scroll down to the MText class and expand the tree below it as far as DBObject. Do the same with Line.
16
Expanding the tree in this way exposes the inheritance hierarchy of that class9. We can see that MText is derived from (or is a type of) Entity, which in turn is derived from (or is a type of) DBObject. For a Line, we see that theres a further layer of abstraction. A Line is a type of Curve, which is a type of Entity, which is a type of DBObject. Arcs, Circles and Polylines (to name but a few) are also types of Curve. For our purposes, we can consider the base class for every object that can reside in a DWG Database to be DBObject. This means that anything you can access inside the DWG is derived from DBObject. There are other classes further up the hierarchy, but we dont normally work with those. Every DBObject in the Database that displays graphics is also an Entity10. How do you view the properties and events exposed by these classes? Click on MText, and youll see these listed to the right. Here weve scrolled down to show the Contents property we used in the sample code. Clicking on a method or property displays a function signature (as shown in the screenshot). If you now click on Entity in the expanded MText class node, youll see all the methods, properties and events that MText inherits from its parent Entity class (and that are common to every other class that is also a type of Entity).
Take some time to browse through and expand the different class nodes in the browser to see whats there. Here are the inheritance hierarchies for BlockTable and BlockTableRecord. You can see that BlockTable is a type of SymbolTable, and BlockTableRecord is a type of SymbolTableRecord as discussed earlier. Notice that these are not graphical entities, so theyre not a type of Entity. Look at the other Autodesk.AutoCAD namespaces as well and notice the different class hierarchies in those.
9
Object Browser shows a child to parent inheritance hierarchy. A parent to child diagram is better for studying which classes share common ancestors. The ObjectARX SDK includes such a diagram in the <ObjectARX SDK>\classmap folder. You could also define an Entity as anything that is owned by (or stored in) a BlockTableRecord.
10
17
And a final note about the class names displayed by Inspector we showed earlier - The language of AutoCAD is ObjectARX. The .NET API is a thin wrapper on top of this. The classes in the DatabaseServices namespace are the same as those prefixed by AcDb in ObjectARX. So when you see AcDbLine in ObjectARX, this translates to Autodesk.AutoCAD.DatabaseServices.Line in .NET.
Further reading
Here are a few places you can go to for more information on the AutoCAD .NET API: x www.autodesk.com/developautocad - for a general overview of the AutoCAD APIs and publicly available resources. This includes the excellent Introduction to AutoCAD .NET Programming DevTV recording by Fenton Webb. www.autodesk.com/apitraining - if youre interested in attending classroom training delivered by the Autodesk Developer Network team. www.autodesk.com/joinadn - to find out how becoming a member of the Autodesk Developer Network can help your application development efforts, including our members-only knowledgebase and unlimited technical support. www.objectarx.com to download the ObjectARX SDK, which includes AutoCAD .NET API documentation and samples.
x x
18
blogs.autodesk.com/through-the-interface Kean Walmsleys blog focusing primarily on AutoCAD APIs. Kean is senior manager of the global Autodesk Developer Technical Services team (the team who support our ADN partners). discussion.autodesk.com/forum.jspa?forumID=152 the Autodesk public discussion group for the AutoCAD .NET API.
Acknowledgements
Thank you to everyone in the Autodesk Developer Technical Services team (past and present) for their contributions to the training material and SDK samples on which Ive based AutoCAD .NET Basics. And thank you to you for signing up for my class.
19