cadalyst
AutoCAD

CAD Clinic: AutoCAD Commands in VB .NET

14 Jun, 2005 By: Mike Tuersley Cadalyst

Use .NET Editors to Create Simple AutoCAD commands


Welcome back! This month we're going to create two simple AutoCAD commands in Visual Basic .Net. If you have not already read last month's column discussing .NET editors, please do so before continuing with this article. With the commands that I will create here, you can use any editor -- including Windows' NotePad -- because no graphics are involved. I will use the free .NET editor SharpDevelop by SharpDevelop.

For this article, we will recreate the classic "Hello World" example and throw the text into AutoCAD's command line and into an MText object. The code is derived from the Autodesk sample available in the AutoCAD 2006 install, in the AutoCAD 2006 ObjectARX toolkit and from the Autodesk Web site.

To begin, open SharpDevelop and create a new combine -- a combine is equivalent to a new project in Microsoft Visual Basic. In the next dialog box, select a Class Library combine (figure 1).

figure
Figure 1. Creating a new project in SharpDevelop.

Now add the references to the project just like with Visual Basic 6 or VBA. An important difference exists between Microsoft's Visual Studio products and SharpDevelop here. After selecting to add a reference in VS, you must browse to AutoCAD's installed directory and select the managed libraries: ACDBMGD.DLL and ACMGD.DLL. Don't select the AutoCAD 2006 type library in the COM tab! That would create a COM InterOp project similar to VB6-based programming. This project will be a .NET managed project, so you need to hook into the MGD files (please refer to last month's column for information regarding managed code). This step is easier in SharpDevelop because the two files are selected through the GAC tab (figure 2).

figure
Figure 2. Adding references in SharpDevelop

Now you should have the code window open for SharpDevelop. Its environment is similar to Visual Studio's. For more information, please refer to SharpDevelop's Help file. Okay, let's look at the code to accomplish the first "Hello World!" example in VB.NET. The easiest way is for me to show the code then describe it:

Imports System
Imports System.Runtime.InteropServices
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices

Imports acadApp = Autodesk.AutoCAD.ApplicationServices.Application

Public Class HelloWorld
   _
  Public Sub HelloCommand()
    acadApp.DocumentManager.MdiActiveDocument.Editor.WriteMessage( _
    vbNewLine & "Hello World!" & vbNewLine)
    acadApp.UpdateScreen()
  End Sub

End ClassM

Starting from the top, the first block of code is the Imports statements. Besides adding references to the assemblies (references) in .NET, you specifically state which assemblies' namespaces are to be used. Think of a namespace as an object within the reference that you want to access. This step also saves typing within your code. For example, in the Autodesk examples, you'll see them call the MdiActiveDocument.Editor like this:

Autodesk.AutoCAD.ApplicationServices.DocumentManager.
  MdiActiveDocument.Editor

That's a lot of code to type in if you don't need to.

Also notice that within the Imports block, I also create a global variable AND set it in the same statement. This is a great feature of .NET where you can declare and set variables all in one line such as:

• In VB6-based code:

Dim sText As String
sText = "Hello World"

• In .NET code:

Dim sText As String = "Hello World"

The next block of code is our class object. Just as in VB6-based programming, our DLL needs a class so it can be called from AutoCAD. Then I have the actual subroutine, HelloCommand, where I write the message to AutoCAD's command line. Notice the subroutine has a descriptor attached to it:

<Autodesk.AutoCAD.Runtime.CommandMethod("HELLO")>

This is all we need to make our subroutine accessible from within AutoCAD! No more calling it through VBA or writing a LISP wrapper to access it. That functionality alone is worth the learning curve of .NET.

Imports System
Imports Microsoft.VisualBasic
Imports acadApp = Autodesk.AutoCAD.ApplicationServices.Application

Public Class HelloWorld

<Autodesk.AutoCAD.Runtime.CommandMethod("HELLO")> _
  Public Sub HelloCommand()
    acadApp.DocumentManager.MdiActiveDocument.Editor.
	  WriteMessage(vbNewLine & _
    "Hello World!" & vbNewLine)
    acadApp.UpdateScreen()
  End Sub

End Class

To test this program, compile it. Then fire up AutoCAD 2006, type in NETLOAD and load the DLL you just created. Once it loads, type HELLO into AutoCAD's command prompt and "Hello World!" should echo back after you press the Enter key.

figure
Figure 3. Using NETLOAD in AutoCAD and the end result of Hello World

No unloading option exists with .NET assemblies, so AutoCAD needs to be shutdown if you want to load the DLL a second time. This is important to note in case, for example, the DLL did not work and you need to change it and re-test it.

Assuming that worked, let's change the program to insert the "Hello World!" text into the current drawing as MText. The code to accomplish this is going to look like:

Imports System
Imports System.Type
Imports System.CLSCompliantAttribute
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput

Imports acadApp = Autodesk.AutoCAD.ApplicationServices.Application

Public Class HelloWorld

<Autodesk.AutoCAD.Runtime.CommandMethod("HELLOTEXT")> _
  Public Function HelloTextCommand()
    Dim acadMText As MText
    Dim acadBT As BlockTable
    Dim acadBTR As BlockTableRecord
    Dim acadDB As Database = HostApplicationServices.WorkingDatabase
    Dim acadTrans As Transaction
    acadTrans = acadDB.TransactionManager.StartTransaction()
    Try
      acadBT = trans.GetObject(acadDB.BlockTableId, _
        Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead)
      acadBTR = trans.GetObject(acadBT(acadBTR.ModelSpace), _
        Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite)
      acadMText = New MText()
      acadMText.Contents = "Hello World!!"
      acadBTR.AppendEntity(acadMText)
      acadTrans.AddNewlyCreatedDBObject(acadMText, True)
      acadTrans.Commit()
    Catch
      acadTrans.Abort()
    Finally
      If Not isnothing(acadTrans) Then acadTrans.Dispose()
      If Not isnothing(acadMText) Then acadMText.dispose()
      If Not isnothing(acadBT) Then acadBT.dispose()
      If Not isnothing(acadDB) Then acadDB.dispose()
    End Try
  End Function
End Class

Starting at the top, again, I added some more calls to the Imports section. Because this required drawing the text, I had to go into AutoCAD instead of just using its command line. After that, I declared my class object and my subroutine. Here, I changed the sub name and the AutoCAD command so this code can co-exist in a project alongside the first example.

Communicate with AutoCAD
Next, is the actual communication with AutoCAD, which is extremely important! You'll use this same structure again and again as you do more managed projects, so it is worth delving into in more detail.

With unmanaged, COM-based code, I could access the appropriate drawing space (model or layout) and add the MText object. With managed code however, I am inserting an MText object directly into the drawing's database. In order to complete this task, I must go through the AutoCAD Transaction object just as if this were a program I was writing that talked to a Microsoft Access or SQL database. This process can be stepped out like so:

  1. Start a Transaction
  2. Connect to the drawing's database
  3. Connect to the drawing database's Block table
  4. Create a record in the Block table
  5. Save or Cancel the transaction
After the transaction is initiated, I talk to the Block table to retrieve an instance of the space I am looking to draw in -- model space in this example. Remember that model and layouts are seen as blocks to a drawing. Next I talk to the block table and retrieve a new entry for the MText object. Think of this step as adding a new row to a database table. I create my MText entity and set up its properties -- text string, color, etc. -- add it to the block table and commit the transaction to the drawing's database. Sounds fairly complicated, but once you get used to it, it becomes second nature.

Try-Catch-Finally Loop
The other new concept in this block of code is the Try-Catch-Finally loop in .NET. The Try statement is a hook to .NET's garbage collection and handles error trapping for us. In .NET, we don't need the old "On Error Resume" statements, and you should not use them because they bypass the automatic garbage collector in .NET. The way this structure works is if any error occurs within the code following the Try command, it is sent to the Catch statement. Multiple Catch statements can handle different errors just like the COM approach of testing for error codes in an error handler. For example, a typical Catch statement looks like: CATCH ex As Exception. The Finally command is optional. If included, the program goes there regardless of whether an error occurs or not. In this example, I want to clear out my object variables no matter what happens. As you can see, I call the individual object's Dispose method to terminate the object reference instead of setting the object to Nothing as in VB6. If I were to set an object to Nothing in .NET, the reference to the object would still exist -- it would just be nothing.

Source Code Notes
If you are just starting with .NET, the source code for this month is complete, but you may need to remove and reattach the references to the MGD files. If you are using another editor instead of SharpDevelop, the only file needed in the source code is the NEW CLASS.VB file. From Microsoft's Visual Studio, create a new class library project and then add the file to it. For other editors, just use this file as you need. The nice feature with .NET is that the VB file is ASCII text until the time it is compiled, so it can be opened with a program as simple as NotePad.

Kick It Up A Notch!
Using this code as an example, explore what else you can create in the managed world. Remember to use the Object Browser to walk yourself through the API as you do more complicated tasks. Next month, I will take both these examples and do them again in C#. See you then!


About the Author: Mike Tuersley


More News and Resources from Cadalyst Partners

For Mold Designers! Cadalyst has an area of our site focused on technologies and resources specific to the mold design professional. Sponsored by Siemens NX.  Visit the Equipped Mold Designer here!


For Architects! Cadalyst has an area of our site focused on technologies and resources specific to the building design professional. Sponsored by HP.  Visit the Equipped Architect here!