.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Launching multiple versions of AutoCAD from C# using "Activator.CreateInstance"

18 REPLIES 18
Reply
Message 1 of 19
Anonymous
5009 Views, 18 Replies

Launching multiple versions of AutoCAD from C# using "Activator.CreateInstance"

We have a .NET tool that uses AutoCAD to automatically process graphics.  This tool uses a method identical to this article from Kean Walmsley’s blog:  http://through-the-interface.typepad.com/through_the_interface/2009/05/interfacing-an-external-com-a...

 

I have been asked to make this tool work with a variety of different AutoCAD versions from AutoCAD 2010 to 2015, both 32 and 64 bit variants.

The ProgID's of the AutoCAD versions I need to CreateInstance for are:

 

"AutoCAD.Application.18" "AutoCAD.Application.18.1" "AutoCAD.Application.18.2"

"AutoCAD.Application.19" "AutoCAD.Application.19.1"

"AutoCAD.Application.20"

 

I am experiencing two problems:

  

First there are different versions of the Autodesk.AutoCAD.Interop.dll in ObjectARX SDK, corresponding to the different versions of AutoCAD.  I cannot include references to all the different versions in the same Visual Studio project.  Most importantly the interface ID’s for the IAcadApplication interface is different in all of them.

 

ObjectARX 2010   32-bit: 84F323FC-C179-4704-87E7-E3D576C2599E

                            64-bit: 2959C1CC-8577-4EDB-ADDC-6EBBAB147926

ObjectARX 2013   32-bit: 7558007D-8677-4FF1-BD48-B66281BD3DD7

                            64-bit: 070AA05D-DFC1-4E64-8379-432269B48B07

ObjectARX 2015   32-bit: 9FD28580-7461-4A57-AF4F-695AF5FDDA35

                            64-bit: 1C9CCE52-F48B-42CC-877B-F74905EC7DBC

 

Is there some method using Reflection that might enable me to create an instance of AutoCAD, and then reflect on the AutoCAD object to obtain the IAcadApplication interface at runtime?

 

The second problem I’m having is that when multiple versions of AutoCAD are installed side-by-side there is some confusion with the CreateInstance call.  For Example, if AutoCAD 2013 and AutoCAD 2014 are both installed and I try to create an AutoCAD.Application.19 instance, I always get an AutoCAD.Application.19.1 instance.  Is there a way I can prevent this, getting the AutoCAD.Application.19 instance I asked for.

 

Any information or strategy would be most helpful.

Thanks,

 

Eric Matthews

 

 

18 REPLIES 18
Message 2 of 19
_gile
in reply to: Anonymous

Hi,

 

First, to avoid COM versions dependency, you can use the dynamic type (requires framework 4.0 or later) or late binding with Reflection, these way you won't have to reference any AutoCAD COM library.

The main inconvenient is that you won't have no more help from Visual Studio intellisense, but you can build your program referencing the COM libraries and the convert it to dynamic or late binding.

 

Here're three examples of the same little Console Application which creates a new instance of AutoCAD, make it visible and change the cursor size to 100.

 

Using COM libraries reference and strong typing, the progId have to be same version à the referenced libraries.

        static void Main()
        {
            AcadApplication acadApp;
            try
            {
                acadApp = (AcadApplication)Activator.CreateInstance(System.Type.GetTypeFromProgID("AutoCAD.Application.18.1"));
            }
            catch (Exception)
            {
                MessageBox.Show("Unable to launch AutoCAD");
                return;
            }
            WaitForAcadIsQuiescent(acadApp);
            acadApp.Visible = true;
            WaitForAcadIsQuiescent(acadApp);
            acadApp.Preferences.Display.CursorSize = 100;
        }

        static void WaitForAcadIsQuiescent(AcadApplication acadApp)
        {
            while (true)
            {
                try
                {
                    AcadState state = acadApp.GetAcadState();
                    if (state.IsQuiescent)
                        break;
                }
                catch { }
            }
        }

 

Converting to dynamic is the easiest way: remove references to the COM references and replace all AutoCAD COM strong types in the code with the 'dynamic' keyword.

 

Using dynamic typing, requires the Framework 4

        static void Main()
        {
            dynamic acadApp;
            try
            {
                acadApp = Activator.CreateInstance(System.Type.GetTypeFromProgID("AutoCAD.Application.18"));
            }
            catch (Exception)
            {
                MessageBox.Show("Unable to launch AutoCAD");
                return;
            }
            WaitForAcadIsQuiescent(acadApp);
            acadApp.Visible = true;
            WaitForAcadIsQuiescent(acadApp);
            acadApp.Preferences.Display.CursorSize = 100;
        }

        static void WaitForAcadIsQuiescent(dynamic acadApp)
        {
            while (true)
            {
                try
                {
                    dynamic state = acadApp.GetAcadState();
                    if (state.IsQuiescent)
                        break;
                }
                catch { }
            }
        }

 

 

Using late binding is a little more tricky, but to avoid all the object.GetType().InvokeMember( ...) stuff, you can use the below extension methods which allow to write code in a frienly way (LISP vlax-* syntax like).

 

Using late bindig with Reflection, requires the LateBindingHelper extension methods

        static void Main(string[] args)
        {
            object acadApp;
            try
            {
                acadApp = LateBindingHelper.CreateInstance("AutoCAD.Application");
            }
            catch (Exception)
            {
                MessageBox.Show("Unable to launch AutoCAD");
                return;
            }
            WaitForAcadIsQuiescent(acadApp);
            acadApp.Set("Visible", true);
            WaitForAcadIsQuiescent(acadApp);
            acadApp.Get("Preferences").Get("Display").Set("CursorSize", 100);
        }

        static void WaitForAcadIsQuiescent(object acadApp)
        {
            while (true)
            {
                try
                {
                    object state = acadApp.Invoke("GetAcadState");
                    if ((bool)state.Get("IsQuiescent"))
                        break;
                }
                catch { }
            }
        }

 

Late binding helper extension methods, requires reference to System.Reflection and System.runtime.InteropServices.

    public static class LateBindingHelper
    {
        public static object GetInstance(string appName)
        {
            return Marshal.GetActiveObject(appName);
        }

        public static object CreateInstance(string appName)
        {
            return Activator.CreateInstance(System.Type.GetTypeFromProgID(appName));
        }

        public static object GetOrCreateInstance(string appName)
        {
            try { return GetInstance(appName); }
            catch { return CreateInstance(appName); }
        }

        public static void ReleaseInstance(this object obj)
        {
            Marshal.ReleaseComObject(obj);
        }

        public static object Get(this object obj, string propName)
        {
            return obj.GetType().InvokeMember(propName, BindingFlags.GetProperty, null, obj, new object[0]);
        }

        public static void Set(this object obj, string propName, object value)
        {
            obj.GetType().InvokeMember(propName, BindingFlags.SetProperty, null, obj, new object[1] { value });
        }

        public static object Invoke(this object obj, string methName, params object[] parameters)
        {
            return obj.GetType().InvokeMember(methName, BindingFlags.InvokeMethod, null, obj, parameters);
        }
    }

 

For the second problem, as far as I know, using the ProgId without specifying version ("AutoCAD.Application") launches the latest opened version. Using only the major version ("AutoCAD.Application.19") launches the latest opened AutoCAD for this major version.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 19
tvmanalan
in reply to: _gile

thanks Gilles Chanteau
vallimanalan.T
Message 4 of 19
tvmanalan
in reply to: _gile

Hi,

 

Greetings..Thanks for your post.

 

Could you please guide me, how we operate Acaddocuments commands in LateBindingHelper method.

 

Thanks,

vallimanalan.T

public void CREATE_polyline(double[] Point_list)
       {
           AcadPolyline polyline = ACAD_Document.ModelSpace.AddPolyline(Point_list);
           ACAD_Application.ZoomExtents();
       }
vallimanalan.T
Message 5 of 19
_gile
in reply to: tvmanalan

Not tested:

 

       public void CREATE_polyline(double[] Point_list)
       {
           AcadPolyline polyline = ACAD_Document.Get("ModelSpace").Invoke("AddPolyline", Point_list);
           ACAD_Application.Invoke("ZoomExtents");
       }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 19
tvmanalan
in reply to: _gile

Hi Gilles Chanteau,

 

Greetings. Am struggling do handle lot of functions through LateBindingHelper (like this approach). Could you please guide me, how i can use this same function thorough LateBindingHelper.

 

If possible, could you please give any tutorial for better understanding of LateBindingHelper

 

Wish you a happy new year in advance

 

Thank you

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Reflection;
using System.IO;
[assembly:CommandClass(typeof(loading_netload_automatically.Class1))]

namespace loading_netload_automatically
{
    public class Class1
    {
        [CommandMethod("autoloading")]
        public static void autoloading()
        {
            Assembly.LoadFrom(@"C:\Users\Naveen-Pc\Documents\Visual Studio 2017\Projects\cadd customization\autocadd should be closed everytime\multicircle\multicircle\bin\Debug\multicircle.dll");
           
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
           
            Application.SetSystemVariable("cmdecho", 0);
            ed.Command("mc",206);
            Application.SetSystemVariable("cmdecho", 1);

            String[] blocks = new string[3] { "b1", "b2", "b3" };

            string ip = "0,0,0";
            foreach (string block in blocks)
            {

                ed.Command("-insert", block, ip, "", "", "");
            }

        }
        }
    }
vallimanalan.T
Message 7 of 19
_gile
in reply to: tvmanalan

This helper just provides extension methods to make late binding less noisy.

This is useless since .NET Framework 4.0 brings the dynamic type.

 

From the code you're showing (using Editor.Command), it looks like you're targerting AutoCAD 2015 or later. That means you have at least the NET Framework 4.5 installed, so you absolutely do not need to use this helper and should use dynamic type instead.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 8 of 19
tvmanalan
in reply to: _gile

Hi,

 

Thanks. If we use dynamic type then its a dependency of particular version like "AutoCAD.Application.18".So we cant achieve multiple version type.

Am right?

 

thanks,

vallimanalan.T

vallimanalan.T
Message 9 of 19
_gile
in reply to: tvmanalan

Sorry, I cannot understand what you're trying to achieve.

The code you are showing does not use the COM interface, so there is no reason to use late binding or the dynamic type to get rid of dependencies on AutoCAD versions. However, you use the Editor.Command () method, which only exists for versions of AutoCAD 2015 and later.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 10 of 19
tvmanalan
in reply to: _gile

Hi Gilles Chanteau,

 

Greetings.

 

In my project Scope, i need to prepare window application with the following features

 

1.through "COM" method , i need open different AutoCAD (invisible mode) and load dll  file (list of custom commands design through "CommandMethod") 

2.Execute the custom command with SQlite data. [Example: for creating polyline, sqlite data will provide points]

 

Could you please guide me

1.How i need to achieve "loading dll file in my application" through latebinderhelp. i unable to understand the dynamic type.if am using dynamic method, am unable to create autocad instance in program.

 

2.How to handle custom commands in latebinderhelp. the reason is am using prompt for user input to transfer sqlite data to command prompt.

like this method for creating layer am passing layer name

 

[CommandMethod("CreateLayer_DWG")]
        public static void CreateLayer()
        {
            // Get the current document and database
            Document acadDoc = Application.DocumentManager.MdiActiveDocument;
           
            Database acadCurDb = acadDoc.Database;

            // Start a transaction
            using (Transaction acadTrans = acadCurDb.TransactionManager.StartTransaction())
            {
                // Open the Layer table for read
                LayerTable acadLyrTbl;
                
                acadLyrTbl = acadTrans.GetObject(acadCurDb.LayerTableId, OpenMode.ForRead) as LayerTable;

                // prompt receive the Layer name
                PromptStringOptions pLyrName = new PromptStringOptions("\nEnter Layer name: ");
                pLyrName.AllowSpaces = true;
                
                PromptResult pLyrRes = acadDoc.Editor.GetString(pLyrName);

                string sLayerName = pLyrRes.StringResult;

                // prompt receive the Layer color
                PromptIntegerOptions pLyrColor = new PromptIntegerOptions("\nEnter Color Index: ");
                pLyrColor.AllowNone = false;
                pLyrColor.AllowZero = false;

                PromptIntegerResult pLyrCol = acadDoc.Editor.GetInteger(pLyrColor);


                if ((pLyrRes.Status != PromptStatus.Cancel) && (pLyrCol.Status == PromptStatus.OK))
                {
                    if (acadLyrTbl.Has(sLayerName) == false)
                    {
                        LayerTableRecord acadLyrTblRec = new LayerTableRecord();
                                               
                            short colorIndex = Convert.ToInt16(pLyrCol.Value);
                            // Assign the layer the ACI color 
                            acadLyrTblRec.Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex);
                        
                            acadLyrTblRec.Name = sLayerName;

                            // Upgrade the Layer table for write
                            acadLyrTbl.UpgradeOpen();

                            // Append the new layer to the Layer table and the transaction
                            acadLyrTbl.Add(acadLyrTblRec);
                            acadTrans.AddNewlyCreatedDBObject(acadLyrTblRec, true);
                        
                    }
                }

                // Save the changes and dispose of the transaction
                acadTrans.Commit();
            }
        }

2.Is it possible to bind the dll with my window application (because we are not providing any setup file to users)

 

 

Thank you,

vallimanalan.T

 

 

vallimanalan.T
Message 11 of 19
_gile
in reply to: tvmanalan

@tvmanalan

 

I don't clearly understand your request.

The process to get or create an AutoCAD instance and call custom .NET commands is described in this topic.

 

To load a DLL just use SendCommand with a string figuring the (command "NETLOAD" ...) LISP expression as shown in the upper link.

 

 

acadDoc.SendCommand("(command \"NETLOAD\" \"C:\\NET commands\\CreateLayer.dll\") ");

 

To call a command with inputs, just build a string with the command name and the inputs as you'd do at AutoCAD Command line and pass it to SendCommand (example to call your command to create a layer named "Layer_1" with color index = 7):

 

 

acadDoc.SendCommand("CreateLayer_DWG Layer_1 7 ");

 

 

 

And please, create a new post. You'd get more help.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 12 of 19
_gile
in reply to: tvmanalan


@tvmanalan wrote:

Hi,

 

Thanks. If we use dynamic type then its a dependency of particular version like "AutoCAD.Application.18".So we cant achieve multiple version type.

Am right?

 

thanks,

vallimanalan.T


The only requirement for using the dynamic type is the Framework 4.0 or upper is installed.

You can use "AutoCAD.Application" as progId to launch the last opened version of AutoCAD.

 

acadApp = Activator.CreateInstance(System.Type.GetTypeFromProgID("AutoCAD.Application"));


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 13 of 19
tvmanalan
in reply to: _gile

thank you so much. i will work on this topics and come back with you
vallimanalan.T
Message 14 of 19
tvmanalan
in reply to: tvmanalan

Hi,

 

only i have confusion on how i need to write the below commands in LateBinderHelp with parameters

 

acadDoc.SendCommand("CreateLayer_DWG Layer_1 7 ")

 

In the above example, Layer & color index are parameters

 

 

acadDoc.SendCommand("(command \"NETLOAD\" \"C:\\NET commands\\CreateLayer.dll\") ");

 

similarly i have more commands in "CommandMethod".could you please correct me.

 

Thanks

 

 

 

 

vallimanalan.T
Message 15 of 19
tvmanalan
in reply to: tvmanalan

Basically I want to access the autocad command line through your latebindhelper.

 

acDoc.SendStringToExecute("._circle 2,2,0 4 ", true, false, false);

acDoc.SendStringToExecute("._zoom _all ", true, false, false);

 

if you can send the equivalent code for the above two line it will be very useful for me. 

thanks a lot.

vallimanalan.T
Message 16 of 19
_gile
in reply to: tvmanalan


@tvmanalan wrote:

Hi,

 

only i have confusion on how i need to write the below commands in LateBinderHelp with parameters

 

acadDoc.SendCommand("CreateLayer_DWG Layer_1 7 ")

 

In the above example, Layer & color index are parameters



You're confusing method parameters and command inputs.

 

There's only one parameter for the SendCommand method which is a string containing the command inputs.

 

Using the dynamic type (you can safely use this for AutoCAD 2012 and later versions):

 

            try
            {
                dynamic acadApp;
                try
                {
                    acadApp = LateBindingHelper.CreateInstance("AutoCAD.Application");
                }
                catch (Exception)
                {
                    Console.WriteLine("Unable to launch AutoCAD");
                    return;
                }
                WaitForAcadIsQuiescent(acadApp);
                acadApp.Visible = true;
                WaitForAcadIsQuiescent(acadApp);

                dynamic acadDoc = acadApp.ActiveDocument;
                acadDoc.SendCommand(@"(command ""NETLOAD"" ""C:\\Temp\\AcadStandaloneLateBinding\\CreateLayerSample.dll"") ");
                acadDoc.SendCommand("CreateLayer_DWG Layer_1\n7 ");
            }
            catch (System.Exception e)
            {
                Console.WriteLine(e.Message);
            }

 

Using late binding (only required if the .NET Framework is lower than 4.0):

 

            try
            {
                object acadApp;
                try
                {
                    acadApp = LateBindingHelper.CreateInstance("AutoCAD.Application");
                }
                catch (Exception)
                {
                    Console.WriteLine("Unable to launch AutoCAD");
                    return;
                }
                WaitForAcadIsQuiescent(acadApp);
                acadApp.Set("Visible", true);
                WaitForAcadIsQuiescent(acadApp);

                object acadDoc = acadApp.Get("ActiveDocument");
                acadDoc.Invoke("SendCommand", @"(command ""NETLOAD"" ""C:\\NET Commands\\AcadStandaloneLateBinding\\CreateLayerSample.dll"") ");
                acadDoc.Invoke("SendCommand", "CreateLayer_DWG Layer_1\n7 ");
            }
            catch(System.Exception e)
            {
                Console.WriteLine(e.Message);
            }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 17 of 19
_gile
in reply to: tvmanalan


@tvmanalan wrote:

Basically I want to access the autocad command line through your latebindhelper.

 

acDoc.SendStringToExecute("._circle 2,2,0 4 ", true, false, false);

acDoc.SendStringToExecute("._zoom _all ", true, false, false);

 

if you can send the equivalent code for the above two line it will be very useful for me. 

thanks a lot.


Using dynamic:

acadDoc.SendCommand("._circle 2,2,0 4 ");
acadDoc.SendCommand("._zoom _all ");


Using late binding helper

acadDoc.Invoke("SendCommand", "._circle 2,2,0 4 ");
acadDoc.Invoke("SendCommand", "._zoom _all ");


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 18 of 19
tvmanalan
in reply to: _gile

Thank you so much for the clarification
vallimanalan.T
Message 19 of 19
tvmanalan
in reply to: _gile

thank you
vallimanalan.T

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost