11

I've been browsing for a good hour and have yet to find something that would help with this. I'm working on opening AutoCAD from the .NET API in VS2013 using C#, but for some reason, I can never get AutoCAD to actually launch. I'm using the following code:

using System;
using System.Runtime.InteropServices;

using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

namespace IOAutoCADHandler
{
    public static class ACADDocumentManagement
    {
        [CommandMethod("ConnectToAcad")]
        public static void ConnectToAcad()
        {

            AcadApplication acAppComObj = null;
            // no version number so it will run with any version
            const string strProgId = "AutoCAD.Application";

            // Get a running instance of AutoCAD
            try
            {
                acAppComObj = (AcadApplication)Marshal.GetActiveObject(strProgId);
            }
            catch // An error occurs if no instance is running
            {
                try
                {
                    // Create a new instance of AutoCAD
                    acAppComObj = (AcadApplication)Activator.CreateInstance(Type.GetTypeFromProgID(strProgId), true);
                }
                catch   //// STOPS HERE
                {
                    // If an instance of AutoCAD is not created then message and exit
                    // NOTE: always shows this box and never opens AutoCAD
                    System.Windows.Forms.MessageBox.Show("Instance of 'AutoCAD.Application'" +
                                                         " could not be created.");

                    return;
                }
            }

            // Display the application and return the name and version
            acAppComObj.Visible = true;
            System.Windows.Forms.MessageBox.Show("Now running " + acAppComObj.Name +
                                                 " version " + acAppComObj.Version);

            // Get the active document
            AcadDocument acDocComObj;
            acDocComObj = acAppComObj.ActiveDocument;

            // Optionally, load your assembly and start your command or if your assembly
            // is demandloaded, simply start the command of your in-process assembly.
            acDocComObj.SendCommand("(command " + (char)34 + "NETLOAD" + (char)34 + " " +
                                    (char)34 + @"C:\Users\Administrator\Documents\All Code\main-libraries\IOAutoCADHandler\bin\Debug\IOAutoCADHandler.dll" + (char)34 + ") ");

            acDocComObj.SendCommand("DRAWCOMPONENT");
        }
    }

Unfortunately, it always stops at the nested catch statement and always displays the popup box without opening AutoCAD. Any suggestions on how to at least make AutoCAD open for me?

EDIT: Error message
][1

Akrem
  • 5,033
  • 8
  • 37
  • 64
Archer
  • 492
  • 1
  • 4
  • 12
  • You're not being very precise about what the exact problem is. Anyway, a couple of suggestions: change the "catch" statements to "catch (Exception e1)" and "catch (Exception e2)". Run the program under the Visual Studio debugger and put breakpoints on the first statement in each catch clause. When the exception is caught you should be able to see the exact type of the exception, and that should (hopefully) give you some clues. – RenniePet Jun 24 '14 at 15:16
  • @RenniePet Yeah, realized that and included the error output. – Archer Jun 24 '14 at 15:17
  • Is that on the first or second catch? – RenniePet Jun 24 '14 at 15:20
  • Dumb question - must be the second catch. Sorry, I don't know anything about AutoCad. Can it be a 32-bit vs. 64-bit problem? – RenniePet Jun 24 '14 at 15:22
  • @Archer are you sure about progid? Have a look http://through-the-interface.typepad.com/through_the_interface/2007/02/using_the_com_i.html. don't you need to specify AutoCAD version? – Evgeny Timoshenko Jun 24 '14 at 15:22
  • @Archer I guess you can `acAppComObj = new AcadApplication` instead of using `Activator.CreateInstance`. Could you give it a try? – Evgeny Timoshenko Jun 24 '14 at 15:46
  • @johny Just tried that and it spat out essentially the same thing. No such interface supported. – Archer Jun 24 '14 at 16:52
  • 6
    Are you running this as a NETLOADed DLL? If yes, then the Application object is `Autodesk.AutoCAD.ApplicationServices.Application` if not then the `[CommandMethod("ConnectToAcad")]` attribute is confusing us. – CAD bloke Jun 25 '14 at 02:29
  • It might be worth a try to implement IMessageFilter. – Parrish Husband Jun 25 '14 at 12:11
  • 4
    I'm with CAD bloke on this one, why are you mixing in-process with interop? You can have both in the same assembly if you want, but I'd keep them out of the same classes and namespace. – Parrish Husband Jun 25 '14 at 12:15
  • I mean, that's what the AutoCAD documentation (that I very conveniently misplaced) code had; I just copied and pasted. The problem it looks like is the casting the `Marshal.GetActiveObject(strProgId)` to `(AcadApplication)`. http://adndevblog.typepad.com/infrastructure/2012/04/com-references-on-civil-3d-2013.html mentions something about 64 and 32 bit, so I changed all of my projects to be x86 and .NET frameworks to 4.5, but nothing changes. – Archer Jun 25 '14 at 13:40
  • Which interop assemblies have you referenced? They come in both x64 and x86 flavors. – Parrish Husband Jun 25 '14 at 14:12
  • @Locke I'm pretty sure they're all the x86 variety; I got them from the ObjectARX inc/ folder. – Archer Jun 25 '14 at 14:15
  • 4
    Check for this common rookie error: all AutoCAD reference need "Copy Local" set to false. We've all done it. – CAD bloke Jun 26 '14 at 00:57
  • Have you taken a look at this? http://stackoverflow.com/a/1257539/753125 – Whitey Jun 29 '14 at 07:32
  • Try this. Go to references. Click on the Autocad dll and put Enable Interop Types to False – ssanga Jun 30 '14 at 16:27
  • Archer, were you able to figure out the problem here? I need the same thing, I want to open an and also 'create' an AutoCAD file through code. – Bilal Saeed Oct 03 '14 at 10:54
  • @BilalSaeed No, I never was able to figure this out. I have my "Copy Local" as false, and everything else that was supposed to happen, but AutoCAD simply would not load up. – Archer Oct 07 '14 at 18:08

5 Answers5

2

The issue is you're coding (correctly) to the AutoCAD interop interface. I recommend against that (due to potential version changes).

The other issue is that the documentation for AutoCAD plugins using the newer .net api is for plugins when AutoCAD is already running.

Final issue could be that the program Id of AutCAD is a mystery. I have resorted to making that a configurable setting, but default to "AutoCAD.Application", which will take the currently registered AutoCAD.Application on the production machine. If there are multiple versions installed on the machine and you want to be specific, then you could append the version number (which you'll need to research) to the ProgID like: "AutoCAD.Application.19", or "AutoCAD.Application.20" for 2015.

For the first issue, one technique is to use dynamics for the autoCad objects, particularly for creating instances. I have used the ObjectARX api for creating my application in a dummy project, and then switching to dynamics when I'm happy with the properties and method names.

In a standalone .Net application that starts AutoCAD you could use something like:

// I comment these out in production
//using Autodesk.AutoCAD.Interop;
//using Autodesk.AutoCAD.Interop.Common;
//...
//private static AcadApplication _application;
private static dynamic _application;
static string _autocadClassId = "AutoCAD.Application";

private static void GetAutoCAD()
{
    _application = Marshal.GetActiveObject(_autocadClassId);
}

private static void StartAutoCad()
{
    var t = Type.GetTypeFromProgID(_autocadClassId, true);
    // Create a new instance Autocad.
    var obj = Activator.CreateInstance(t, true);
    // No need for casting with dynamics
    _application = obj;
}

public static void EnsureAutoCadIsRunning(string classId)
{
    if (!string.IsNullOrEmpty(classId) && classId != _autocadClassId)
        _autocadClassId = classId;
    Log.Activity("Loading Autocad: {0}", _autocadClassId);
    if (_application == null)
    {
        try
        {
            GetAutoCAD();
        }
        catch (COMException ex)
        {
            try
            {
                StartAutoCad();
            }
            catch (Exception e2x)
            {
                Log.Error(e2x);
                ThrowComException(ex);
            }
        }
        catch (Exception ex)
        {
            ThrowComException(ex);
        }
    }
}
reckface
  • 5,678
  • 4
  • 36
  • 62
2

When there are several versions of AutoCAD installed on a computer, creating an instance with the ProgID "AutoCAD.Application" will run the latest version started on this computer by the current user. If the version of the Interop assemblies used does not match the version that is starting, you'll get a System.InvalidCastException with an HRESULT 0x80004002 (E_NOINTERFACE).

In your specific case, the {070AA05D-DFC1-4E64-8379-432269B48B07} IID in your error message is the GUID for the AcadApplicationinterface in R19 64-bit (AutoCAD 2013 & 2014). So there is an AutoCAD 2013 or 2014 that is starting, and you cannot cast this COM object to a 2015 type because 2015 is R20 (not binary compatible).

To avoid that, you can add a specific version to your ProgID (like "AutoCAD.Application.20" for AutoCAD 2015 (R20.0) to 2016 (R20.1)) to start the version matching your Interop assemblies or you can use late binding (eg. remove your references to Autodesk.AutoCAD.Interop* and use the dynamic keyword instead of the AutoCAD types).

In the last case, you will lost autocompletion, but your program will work with all the versions of AutoCAD.

Check also 32-bit vs 64-bit because TypeLib/Interop assemblies are not the same.

Maxence
  • 12,868
  • 5
  • 57
  • 69
0

I open the application in a much straight-forward way. First, be sure to reference the correct type library. The one I am using is AutoCAD 2014 Type Library, located at: c:\program files\common files\autodesk shared\acax19enu.tlb

To initialize the application:

using AutoCAD;

namespace test
{
class Program
{
    static void Main(string[] args)
    {
        AutoCAD.AcadApplication app;

        app = new AcadApplication();

        app.Visible = true;

        Console.Read();
    }
}
}
0

Try this:


"sourcefile" is the original file
"newfile" is the new file

[CommandMethod("ModifyAndSaveas", CommandFlags.Redraw | CommandFlags.Session)]
    public void ModifyAndSaveAs()
    {
        Document acDoc = Application.DocumentManager.Open(sourcefile);
        Database acDB = acDoc.Database;

        Transaction AcTran = acDoc.Database.TransactionManager.StartTransaction();
        using (DocumentLock acLckDoc = acDoc.LockDocument())
        {
            using (AcTran)
            {
                BlockTable acBLT = (BlockTable)AcTran.GetObject(acDB.BlockTableId, OpenMode.ForRead);
                BlockTableRecord acBLTR = (BlockTableRecord)AcTran.GetObject(acBLT[BlockTableRecord.ModelSpace], OpenMode.ForRead);
                var editor = acDoc.Editor;                  

                var SelectionSet = editor.SelectAll().Value;
                foreach (ObjectId id in SelectionSet.GetObjectIds())
                {
                    Entity ent = AcTran.GetObject(id, OpenMode.ForRead) as Entity;

                    //modify entities 

                }

                AcTran.Commit();

            }
        }
        acDB.SaveAs(newfile, DwgVersion.AC1021);
    }
-3
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Tekkit
{
    class Program
    {
        static void Main(string[] args)
        {

                //make sure to add last 2 using statements
                ProcessStartInfo start = new ProcessStartInfo("calc.exe");                              
                Process.Start(start);//starts the process

        }
    }
}
  • 4
    Yeah... but getting the automation object from the running instance afterwards is non-trivial (requires iterating through the global object table repeatedly until what you're looking for shows up). You're also missing the code that gets the path to the process by looking up its LocalServer32 key in the registry. And the `-Embedding` flag that the DCOM remoting server passes to the application when it's started the normal way via COM. – Cameron Jul 09 '14 at 14:23
  • Worse than iterating through the table, the ROT only registers the first instance started. Therefore if you have multiple sessions open, you'll only be able to work with the first and by that point who knows which one that is. I actually use this in one of my products but if a session is already open or there are more than one session open, I prompt the user and go from there. I don't play games with it. – bjhuffine Aug 19 '15 at 11:13