Question

I am attempting to launch AutoCAD 2013 from an existing C# desktop application, use database values to draw a diagram, and then save the drawing to the database as a variable bit array (varbyte). I've been having good success with all except getting coordinates into an existing AutoCAD editor window.

I've tried NETLOAD but I can't put a list of 55+ AutoCAD Database Circle objects through the command line. Attempting to pass objects through COM Interop has been met with failure and confusion over the 32-bit versus 64-bit status of AutoCAD and my application. While this site has been helpful, http://through-the-interface.typepad.com/ , I haven't been able to get what I want done.

Any suggestions would be greatly appreciated. Thanks.

EDIT:

I took Locke's advice and I added functions to the .NET DLL to draw single items based on simple arguments (like X, Y coordinates, radius, label, etc.). Here is what the method signature looks like:

[CommandMethod("DrawSmallCircle", CommandFlags.Session)]
public static void DrawSmallCircle(double x, double y, double aRadius, string aGuage, string aLabel, string aTitle)

After netloading the .dll which hosts the above method (and it's containing class), I use SendCommand on the instantiated interop AcadApplication like so:

acApp.ActiveDocument.SendCommand("DrawSmallCircle " +
circ.circle.Center.X.ToString() + ", " +
circ.circle.Center.Y.ToString() + ", " +
circ.circle.Radius.ToString() + ", " +
circ.guage + ", " +
circ.label + ", " +
circ.title + " "
);

Unfortunately the error I get in response is simply "Common Language Runtime detected an invalid program."

I'm not sure if registering a command in my .dll will do any good because, allegedly, "Command methods can't take arguments, but lispFunctions can."

http://forums.autodesk.com/t5/NET/CommandMethod-with-custom-params/td-p/2572973

Was it helpful?

Solution

Okay, so you've run into the Interop/In-Process automation issue. Traditionally, AutoCAD doesn't want to let out of process modules talk to in-process modules, at least in the context of sending parameters back and forth. The formal approach which involves registering the in-process COM interface is challenging to get behaving correctly, especially in the context of x64 bit. I've still yet to see it behave consistently across multiple computers, therefore I tend to default to the following approach.

You are correct in that methods flagged with the [CommandMethod] flag cannot take arguments, so they'll obviously need to be voids. The trick to sending it parameter context at runtime is to include parameter prompts in the defined method itself. Think of it as you're developing the command to be invoked by the user inside AutoCAD, and they are prompted for each piece of data before the command can proceed. Much like other native AutoCAD commands, the parameter data can be sent alongside the call to the command in a single string.

Example:

(Command "._CIRCLE" "0,0" "5") <-- Draws a circle at 0,0 with a radius of 5.

So your command call could end up looking something like this:

(Command "DRAWDBCIRCLE" "2.3,56.12", "7" "Gauge" "Label" "Title")

In-Process Code

[CommandMethod("DRAWDBCIRCLE")]
public void DrawDbCircle()
{
    var acDb = HostApplicationServices.WorkingDatabase;
    var acEd = Application.DocumentManager.MdiActiveDocument.Editor;

    using (var acTrans = acDb.TransactionManager.StartOpenCloseTransaction())
    {
        var bt = (BlockTable)acTrans.GetObject(acDb.BlockTableId, OpenMode.ForWrite);
        var btr = (BlockTableRecord)acTrans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

        Point3d centerPoint;
        double radius;
        string gauge, label, title;

        // Prompt for the centerpoint
        var pointResult = acEd.GetPoint("ENTER CIRCLE ORIGIN: \n");
        centerPoint = pointResult.Value;

        // Prompt for the radius
        var doubleResult = acEd.GetDouble("ENTER CIRCLE RADIUS: \n");
        radius = doubleResult.Value;

        // Prompt for the strings
        var stringResult = acEd.GetString("ENTER CIRCLE GAUGE: \n");
        gauge = stringResult.StringResult;
        stringResult = acEd.GetString("ENTER CIRCLE LABEL: \n");
        label = stringResult.StringResult;
        stringResult = acEd.GetString("ENTER CIRCLE TITLE: \n");
        title = stringResult.StringResult;

        // Create the circle
        var circ = new Circle(centerPoint, Vector3d.ZAxis, radius);
        // <-- Add code for dealing with strings -->
        btr.AppendEntity(circ);
        acTrans.AddNewlyCreatedDBObject(circ, true);

        acTrans.Commit();
    }
}

Interop Code

private AcadApplication acApp;
private AcadDocument acDoc;

private void btnRun_Click(object sender, EventArgs e)
{
    if (acApp == null) return;
    acDoc = acApp.ActiveDocument;

    foreach (DataRow row in circleTable.Rows)
        DrawDatabaseCircle(row);
}

private void DrawDatabaseCircle(DataRow circRow)
{
    var cmdFormat = string.Format("\"{0},{1}\" \"{2}\" \"{3}\" \"{4}\" \"{5}\"", circRow.ItemArray);
    acDoc.SendCommand(string.Format("(Command \"DRAWDBCIRCLE\" {0})\n", cmdFormat));
}

Obviously this is more or less pseudo-code. I assume a number of things here, like the AcadApplication and AcadDocument fields being set correctly, that the dll containing the defined command method has been properly netloaded, and that the database circles are coming out of a DataTable. Error-handling would be needed in the commandmethod for checking the parameters, and it would make sense to enclose the SendCommand method in a try/catch.

This technique really only works when you have data types that can be represented by strings, so it won't cover every situation. It's definitely worth trying to get the COM registered interface working long-term for more robust communication between in/out processes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top