Kean Walmsley

July 2009

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Twitter Updates

    follow me on Twitter



    « Creating an AutoCAD Wipeout using .NET | Main | Getting AutoCAD's current directory from a .NET application »

    December 14, 2007

    Launching AutoCAD from a .NET application

    This topic has been raised a few times, and Adam Nagy, from our DevTech EMEA team, sent a technical response recently with code that I decided to use as the basis for this post.

    Developers typically want to either integrate functionality into AutoCAD (using its plug-in architecture to add commands, user-interface elements, objects, etc.), or to drive it, automating common tasks. Clearly the line can get blurred between these two areas, but today I’m going to focus on the second category.

    To help with later explanations, I’d like to introduce two types of application interaction:

    Out-of-process

    In this situation we have two separate executables trying to communicate. Imagine you have an EXE and you want to drive AutoCAD. You need to find some way to launch AutoCAD and communicate with it – typically using COM or, before that, DDE. The communication is, by definition, done via Inter-Process Communication (IPC), which is a very inefficient way to send large chunks of data. This is why old ADS and external VB applications ran (or run) so slowly.

    In-process

    When your code is packaged inside a DLL (whether an ActiveX DLL defined in VB6, an ObjectARX module or a .NET assembly), the communication with the host process (AutoCAD) is much more efficient – data structures can be passed by pointer or reference rather than squirting the information through the inefficient marshalling of IPC.

    Most of AutoCAD’s current APIs are designed to be used “in-process” – this includes LISP, ObjectARX and AutoCAD’s .NET API. Given the availability of .NET Remoting, people often hope or expect AutoCAD to be drivable via .NET “out-of-process”, but this is not how the managed API was designed: it is really a wrapper on top of ObjectARX, whose performance is based on direct access to objects via pointers, and this simply does not extend to work across process boundaries. One of the great features of COM Automation is that it was designed to work either out-of-process (from an external EXE) or in-process (from VBA or by calling GetInterfaceObject() to load a VB6 ActiveX DLL). This is still the best way to drive AutoCAD from an external executable.

    I generally recommend not trying to pass too much information across the process boundary: if you need to drive AutoCAD from an external executable, simply launch it (or connect to a running instance, if that’s an option) and then load an in-process module to do the heavy lifting from within AutoCAD’s process space.

    The following code shows how to do just that, using C#. It tries to connect to a running instance of AutoCAD (this is optional – you can easily adjust the code only to launch AutoCAD), and failing that launches it. Once there’s a running instance we make it visible and run a custom command. My recommendation is to set up demand-loading for your application – either to load it on AutoCAD startup or when a command is invoked – and then to run a command defined in your module.

    You will need to add a reference to the “AutoCAD Type Library” from your application, as well as including these assembly references:

    using System;

    using System.Runtime.InteropServices;

    using Autodesk.AutoCAD.Interop;

    This C# code can be added to an “on-click” event handler for a button (for example) or another function that makes sense in the context of your application.

      // "AutoCAD.Application.17" uses 2007 or 2008,

      //  whichever was most recently run


      // "AutoCAD.Application.17.1" uses 2008, specifically


      const string progID = "AutoCAD.Application.17.1";


      AcadApplication acApp = null;

      try

      {

        acApp =

          (AcadApplication)Marshal.GetActiveObject(progID);

      }

      catch

      {

        try

        {

          Type acType =

            Type.GetTypeFromProgID(progID);

          acApp =

            (AcadApplication)Activator.CreateInstance(

              acType,

              true

            );

        }

        catch

        {

          MessageBox.Show(

            "Cannot create object of type \"" +

            progID + "\""

          );

        }

      }

      if (acApp != null)

      {

        // By the time this is reached AutoCAD is fully

        // functional and can be interacted with through code

        acApp.Visible = true;

        acApp.ActiveDocument.SendCommand("_MYCOMMAND ");

      }

    TrackBack

    TrackBack URL for this entry:
    http://www.typepad.com/services/trackback/6a00d83452464869e200e54fb759508834

    Listed below are links to weblogs that reference Launching AutoCAD from a .NET application:

    Comments

    Hi Kean,

    After AutoCAD has launched, I often have a need to determine the "Start In" directory specified in the shortcut (Right-click AutoCAD shortcut, Properties, Start in). Is there a way to determine what that directory is? I'm thinking I can use the path of the active drawing (either Drawing1 or the drawing they double-clicked on). Is that a fool-proof way?

    Thanks in advance for your help,
    -Danny

    Hi Kean
    Your notes at the top of the code make reference to the version of Autocad to run, but is there any way of passing through the profile to activate?. Many people have 3rd party apps that rely on profiles to ensure the correct menus etc are loaded. Some automation tasks will need the correct profiles loaded (in order to activate any custom object handlers)

    Danny -

    Please see this post.

    Greg -

    COM doesn't allow launching of a process with command-line arguments, as far as I'm aware, but you can use another approach to the launch AutoCAD to meet this need. I'll make this the topic of my next post.

    Thank you both for your comments!

    Regards,

    Kean

    Hi Kean, Happy Holidays.

    Regarding:

    Type acType = Type.GetTypeFromProgID(progID);
    acApp = (AcadApplication)Activator.CreateInstance(acType,true);

    I was just wondering if you may have come across any problems using the standard means of creating a new instance of AutoCAD and getting its AcadApplication object, e.g.,

    AcadApplication acApp = new AcadApplication();

    The AcadApplication's new() constructor internally calls Activator.CreateInstance() for you, and calling that usually starts up a new instance of AutoCAD.

    No specific reasons - that's just the code I inherited to do this.

    Happy Holidays to you, too, Tony.

    Kean

    Greg -

    Here's the post that looks at launching AutoCAD with a specific profile.

    Cheers,

    Kean

    Hi Kean,

    i will launch AutoCAD from an existing ProcessID in C#. The existing ProcessID was Written in the Registry from another C++ ObjectARX Application.
    Now i must linked at this Process, how can i make this.

    Hi Dirk,

    Do you mean ProcessID or ProgID?

    I'd recommend submitting this question via the ADN website, if you're a member.

    Regards,

    Kean

    Hi Kean,

    Is there a way of either passing parameters with SendCommand or directly calling methods in the NETLOAD'ed dll?

    Under VB6 when using GetInterfaceObject, the class is returned from the call allowing its methods to be called.

    Specifically, I want to rewrite this VB6 app in .NET. The app needs to pass a string of XML into my dll in AutoCAD to be processed.

    Regards,

    Brian

    Hi Brian,

    A couple of choices...

    To pass everything by the command-line, you can use Editor.GetString() from within your command to pick up strings that are passed in after the command-name using SendStringToExecute().

    Otherwise you can expose public methods from public classes that can be imported into other modules (a standard .NET technique for publishing libraries or modularizing your code in different assemblies).

    Regards,

    Kean

    Hi Kean,

    In trying this sample, I ran into some trouble. I set references to acdbmgd, acmgd, AutoCAD 2008 Type Library, and AutoCAD/ObjectDBX Common 17.0 Type Library. The first 3 references I can do fine, but in setting the last reference, Visual Studio comes back to the references tab with 'The system cannot find the reference specified'. I am using Visual Basic 2008, AutoCAD Mechanical 2008 SP2, and Vista. Any ideas why this is happening?

    Regards,

    Brian

    Hi Brian,

    Sorry - I have no idea what the problem is. Re-installing is likely to help, if nothing else.

    Regards,

    Kean

    Hi Kean,

    Oh well, I was hoping you would have some insight!

    Back to your first reply on "exposing public methods from public classes". Are you saying exposing them from the NETLOAD'ed dll? If so, I always get an "unable to find entry point" exception in my .exe.

    Just to clarify, I will have an out-of-process .exe written in VB.net, the NETLOAD'ed dll written in VB.net, and I need a way to call the methods in the dll.

    Any advice you can provide, or pointers to other blog entries, is greatly appreciated.

    Thanks,

    Brian

    Hi Brian,

    Oh, you want to call into the DLL from an EXE? In that case you really should use the command-line, whether using Editor.GetString() (etc.) or using the LispFunction attribute to also expose your commands via LISP functions.

    Either approach can then be called by SendCommand() (you should really use COM for this, as AutoCAD's .NET interface is not supported across process boundaries).

    Regards,

    Kean

    Verify your Comment

    Previewing your Comment

    This is only a preview. Your comment has not yet been posted.

    Working...
    Your comment could not be posted. Error type:
    Your comment has been posted. Post another comment

    The letters and numbers you entered did not match the image. Please try again.

    As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

    Having trouble reading this image? View an alternate.

    Working...

    Post a comment

    Feed & Share

    Search