Invoking Unmanaged Methods from AutoCAD's .NET API
One of the greatest strength's of Autodesk's AutoCAD software is the extensive API
access provided by the product. For years now, it has been very common to
use Lisp or VBA to extend the functionality of the various AutoCAD products in numerous
ways. However, recent years have seen the introduction of the Common Language
Runtime (CLR) from Microsoft, and the introduction of a variety of programming
languages that access this CLR via Microsoft's .NET Frameworks. The most-popular
of these languages include Visual Basic and C-Sharp.
The ability to program in a high-level language like Visual Basic or C-Sharp, along
with the extensive support provided by the runtime system through the .NET
frameworks, provides significant benefits to application developers.
One of these benefits is the ability to create "managed code", which
reduces the complexities involved in managing computer resources. As a result,
Autodesk has provided a "managed API" that can be used to customize AutoCAD
from a .NET environment.
Unfortunately, the managed API fails to expose all of the functionality available
in the ObjectARX libraries. From time to time, it may be necessary for you
to access this "hidden" functionality from a C#.NET or VB.NET program. This
article illustrates one way to accomplish this task. All examples are provided
in C-Sharp, but it should be relatively easy to translate them to any other .NET
language.
Preparing the Environment
In order to access the various entry points exposed to ObjectARX from a .NET language,
we must first identify which methods or properties we wish to access, by reading
the ObjectARX documentation. Once we have identified a useful access point,
we must identify something called the "mangled" names for the access point.
In order to do this, we will use a tool called dumpbin that should be installed
by default when you install your IDE. For example, dumpbin is installed
along with Visual Studio. If you are using the Express Editions of Visual
Studio, you may have to download and install VS Express C++ Edition to get
dumbin.
In order to use dumpbin easily, we want to add the location of the dumpbin.exe
file to our Environment Path, so we may run it from any
directory. This may have happened during installation, or you may have to
do it yourself. You can test this to see if it is already setup by starting a Command
Prompt window and typing "dumpbin" at the command line. If you get
an error message stating that the command could not be found, then we need to find
dumpbin and add its directory to our Environment Path.
To do that, we'll open up our hard drive in Windows Explorer, then right-click
on the "Program Files" directory and select "Search...".
We will then look for "dumpbin". A sample of the possible results
is shown below.
It looks like there are three versions of dumpbin on this system. Two
of them look like they're for 64-bit systems, but I'm using a 32-bit system,
so I'll go with the first one in the list. We now need to add this directory
to the Environment Path.
To do this, right-click on My Computer and select "Properties", then go
to the "Advanced" tab and hit the "Environment Variables" button.
In the lower section of the dialog box ("System variables"), find the
"PATH" system variable and hit the "Edit" button. Now,
add the above path to the end of the PATH variable, separated from the path before
it by a semi-colon. Make sure you do not erase or overtype any of the directories
already in this path, or you may cause portions of your system to cease to function
properly.
The image below illustrates this process:
Now, if you go back to the Command Prompt window and type "dumpbin", you
should see a list of command options in the window.
Note that, when you attempt to run dumpbin, you may get a warning message that other
files could not be found. For example, you may get a warning that "mspdb80.dll"
could not be found. If so, follow the same procedure as above. Locate
mspdb80.dll by searching Program Files, then add its directory to the Environment
Path (it is probably at "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE").
Identifying the "Mangled Name"
Now that we have configured our environment so that we can run dumpbin, we can use
it to identify the mangled name for our desired ObjectARX entry point. First,
by using the ObjectARX documentation, we must identify the method name we wish to
access, as well as the DLL that contains the method.
For this example, we are going to add access to the Scale() and SetScale()
methods for the MLeader entity. These methods are missing from the
managed API. By looking at the ObjectARX documentation, we find the method
signatures for the methods:
Acad::ErrorStatus setScale (double scale);
double scale() const;
Now we only need to figure out which DLL contains the access point. For the
most part, the default AutoCAD entities (at least in the 2009 products) are located
in the acdb17.dll file located in the AutoCAD install directory. So
let's take a look at the acdb17.dll file, and see what we find.
By typing simply "dumpbin" at the command line, we can see the various
options for the command. In order to see the various entry points exposed
by the API, we must use the "/exports" flag. Also, if we try doing
this on acdb17.dll, we'll get an extremely long list that will probably overrun
the screen buffer. So we will also use the "/out" flag to send the
output to a text file we can examine in a text editor. In order to do this,
we type the following command in the command line:
dumpbin /exports acdb17.dll /out:acdb17dump.txt
And here is a screenshot of the process. Note that this image was captured
using Civil-3D 2009, but the same basic process applies to any AutoCAD product.
Simply browse to the correct installation directory for the AutoCAD product you
are using, and enter the dumpbin command as shown:
This creates a text file named "acdb17dump.txt" that we can open in any
text editor. If we do so, we'll see an extremely long list of exposed
functions. Scroll down the list until you find the various Scale()
methods. As we can see from the signature, one of these exposed methods is
on the MLeader entity - that's the one we want.
Similarly, we can find the mangled name for the SetScale() method a little
further down the list:
Creating the C#.NET Wrappers around the ObjectARX code
Now that we've identified the mangled names for our access points, we will create
C-Sharp methods that invoke our target methods in acdb17.dll.
using System;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
namespace DatabaseExtensions
{
public class DatabaseExtensions
{
private static class AcDbMLeader
{
// this line
improves performance by relaxing stack traces
// during the
call to the unmanaged code; only need to
// include this
line once for each class
[System.Security.SuppressUnmanagedCodeSecurity]
// method sig
from ObjectARX:
// double scale()
const;
[DllImport("acdb17.dll",
CallingConvention = CallingConvention.ThisCall, CharSet = CharSet.Unicode, EntryPoint
= "?scale@AcDbMLeader@@QBENXZ")]
public static
extern double scale(IntPtr mleader);
// method sig
from ObjectARX:
// Acad::ErrorStatus
setScale (double scale);
[DllImport("acdb17.dll",
CallingConvention = CallingConvention.ThisCall, CharSet = CharSet.Unicode, EntryPoint
= "?setScale@AcDbMLeader@@QAE?AW4ErrorStatus@Acad@@N@Z")]
public static
extern ErrorStatus setScale(IntPtr mleader, double scale);
}
public static double GetMLeaderScale(MLeader
mldr)
{ return AcDbMLeader.scale(mldr.UnmanagedObject);
}
public static void SetMLeaderScale(MLeader
mldr, double scale)
{ AcDbMLeader.setScale(mldr.UnmanagedObject,
scale); }
}
}
In the code above, the AcDbMLeader subclass encapsulates our calls to the unmanaged
code in acdb17.dll. Note how the mangled names we located with dumpbin
are used as the "EntryPoint" in the [DllImport] attribute. As is
typical when using a standard function call to invoke a method on a target object,
a pointer to the target object (in this case, the MLeader) is passed as the first
argument in the function call. The succeeding arguments should match the method
signature we found in the ObjectARX documentation.
Finally, we define the public static methods GetMLeaderScale() and SetMLeaderScale(),
which are the methods we use in our code.
Using the ObjectARX Wrappers
Our new wrapper is now ready to be used in our code just like any other method. For example, the
snippet of code below will create an MLeader, and if it is not using an
Annotative MLeader style, it sets the MLeader scale based on the current
CANNOSCALE:
MLeader mleader = new MLeader();
mleader.SetDatabaseDefaults();
double scale = 1.0 /
(double)Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("CANNOSCALEVALUE");
if (mleader.Annotative != AnnotativeStates.True)
DatabaseExtensions.SetMLeaderScale(mleader, scale);
This method may be used to access any method in any DLL on your system, as long
as that method has been made available to external calls. It is not the
only way to interact with the unmanaged world from the managed world, but it is
a relatively simple and effective way to get around those holes in Autodesk's
managed API.