System.InvalidCastException on all objects.

System.InvalidCastException on all objects.

Eric.Mathews120
Contributor Contributor
4,792 Views
7 Replies
Message 1 of 8

System.InvalidCastException on all objects.

Eric.Mathews120
Contributor
Contributor

I'm trying to work with a range of AutoCAD versions spanning from 2016 to 2019. I'm referencing the 2019 interop dll's. In the code snippet, I'm able to set a reference to a new AutoCAD instance as a dynamic data type. The issue arises when I try to cast that object into an IAcadApplication object. The issue extends when opening a document as well. I can validate the type name from the code snippet. I know it's the correct type. So, why am I receiving these errors?

 

Application Error: 'Autodesk.AutoCAD.Interop.IAcadApplication'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{6323190E-C6ED-434C-A368-E9314A8D7597}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).'

 

Document Error: Unable to cast COM object of type 'System.__ComObject' to interface type 'Autodesk.AutoCAD.Interop.IAcadDocument'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{7F63D215-73CE-4BC0-80EA-83EBC7CFF905}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).. 

    public string GetTypeName(object comObject)
{
if (!(comObject is IDispatch)) return null;

var _dispatch = (IDispatch)comObject;

var pTypeInfo = _dispatch.GetTypeInfo(0, 1033);

pTypeInfo.GetDocumentation(
-1,
out var pBstrName,
out string pBstrDocString,
out int pdwHelpContext,
out var pBstrHelpFile);

string str = pBstrName;
if (str[0] == 95)
{
// remove leading '_'
str = str.Substring(1);
}

return str;
}

 

0 Likes
Accepted solutions (1)
4,793 Views
7 Replies
Replies (7)
Message 2 of 8

FRFR1426
Collaborator
Collaborator
Accepted solution

If you want your code to work with different version of AutoCAD, you have to use late binding (dynamic keyword) everywhere and not reference any Interop assembly.

Maxence DELANNOY
Manager
Add-ins development for Autodesk software products
http://wiip.fr
0 Likes
Message 3 of 8

Eric.Mathews120
Contributor
Contributor

That is extremely aggravating. The Inventor interop is backward compatible and works wonderfully. Is Autodesk trying to kill off AutoCAD? I expect better support from a company as large as Autodesk. It sounds like they are making any .Net solution as difficult as possible. 

0 Likes
Message 4 of 8

norman.yuan
Mentor
Mentor

AutoCAD is also quite good at backward compatible, if not better than Inventor, at least as good.

 

So, if you use early binding (adding DLL references to your code project) to target multiple version of AutoCAD, and hoping the code working based on BACKWARD COMPATIBILITY, you need to use the referenced DLL from the EARLIEST version, not the LATEST version. That is, in your case, if you target 2016 to 2019, you DO NOT set reference to Acad2019's interop DLL, instead, you set reference to Acad2016's interop DLLs. It would most likely work with all Acad versions newer than Acad 2016 (but not guaranteed).

 

But I agree with @FRFR1426, using dynamic would be the way to go. Again, even with dynamic, you cannot expect AutoCAD feature that only available in alter version that works in earlier Acad version, of course.

 

If I worked on project heavily using dynamic types, I'd set reference DLLs to to earliest target version; work out my code, and then remove the references, modify the code to replace related types with "dynamic". This way, I would be sure the dynamic type' properties/methods are coded correctly.\

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 8

FRFR1426
Collaborator
Collaborator

There is 2 strategies with COM: keep the backward compatibility (like in Inventor) but you can not change/remove a function from the API  or produce a different API for each version (like in AutoCAD), which allow you to change the API to reflect changes in the program.

 

First approach allow your addin to work with new version of the host, but can lead to hideous API like in Solidworks where there are several types/members suffixed with a number (IBody2, IMate2, ActivateDoc3, OpenDoc6(), ...). In Inventor, there is also the same problem: BOMView.Sort2(), AngleConstraint.ConvertToInsertConstraint2(), but Autodesk developpers manage it very well.

 

Second approach let the developpers of the host keep the API clean.

 

@norman.yuan I'm pretty sure that your approach to reference the DLL from the earliest version does not work. If I use the following code with a reference to the 2016 Autodesk.AutoCAD.Interop:

 

using System;
using Autodesk.AutoCAD.Interop;

namespace COM
{
    class Program
    {
        public static void Main(string[] args)
        {
            var acadApplication = (AcadApplication) Activator.CreateInstance(Type.GetTypeFromProgID("AutoCAD.Application.23"));
            Console.Write(acadApplication.Version);
            acadApplication.Quit();
        }
    }
}

It starts the 2019 version (AutoCAD.Application.23), but I've an InvalidCastException : ... QueryInterface ... '{1C9CCE52-F48B-42CC-877B-F74905EC7DBC}'

 

1C9CCE52-F48B-42CC-877B-F74905EC7DBC is the GUID of Autodesk.AutoCAD.Interop.AcadApplication in the 2016 Autodesk.AutoCAD.Interop.dll.

 

CreateInstance returns an Autodesk.AutoCAD.Interop.AcadApplication object with the GUID 6323190E-C6ED-434C-A368-E9314A8D7597 (AutoCAD 2019) and this object can not be cast to an Autodesk.AutoCAD.Interop.AcadApplication object with the GUID 1C9CCE52-F48B-42CC-877B-F74905EC7DBC (AutoCAD 2016).

Maxence DELANNOY
Manager
Add-ins development for Autodesk software products
http://wiip.fr
0 Likes
Message 6 of 8

norman.yuan
Mentor
Mentor

@FRFR1426 wrote:

 

...

 @norman.yuan I'm pretty sure that your approach to reference the DLL from the earliest version does not work. If I use the following code with a reference to the 2016 Autodesk.AutoCAD.Interop:

 

using System;
using Autodesk.AutoCAD.Interop;

namespace COM
{
    class Program
    {
        public static void Main(string[] args)
        {
            var acadApplication = (AcadApplication) Activator.CreateInstance(Type.GetTypeFromProgID("AutoCAD.Application.23"));
            Console.Write(acadApplication.Version);
            acadApplication.Quit();
        }
    }
}

It starts the 2019 version (AutoCAD.Application.23), but I've an InvalidCastException : ... QueryInterface ... '{1C9CCE52-F48B-42CC-877B-F74905EC7DBC}'

 

1C9CCE52-F48B-42CC-877B-F74905EC7DBC is the GUID of Autodesk.AutoCAD.Interop.AcadApplication in the 2016 Autodesk.AutoCAD.Interop.dll.

 

CreateInstance returns an Autodesk.AutoCAD.Interop.AcadApplication object with the GUID 6323190E-C6ED-434C-A368-E9314A8D7597 (AutoCAD 2019) and this object can not be cast to an Autodesk.AutoCAD.Interop.AcadApplication object with the GUID 1C9CCE52-F48B-42CC-877B-F74905EC7DBC (AutoCAD 2016).


@FRFR1426, the reason of your sample code does not work, is because of using VERSION-specific program ID ("AutoCAD.Application.23"), which one should not do when trying to write code for multiple version. If you use generic program ID "AutoCAD.Application", the Activator would create an AutoCAD Application instance of whatever version installed, or an instance of AutoCAD version of running last, if multiple versions of AutoCAD are installed.

 

As matter of fact, since AutoCAD interop DLL is referenced, there is no need to use Activator and program ID to start AutoCAD, one can simply:

 

var cadApp=new AcadApplication(); which would create an instance of whatever version of AutoCAD.

 

Of course if you want the code can be modified easily later to use dynamic (C#) or object (VB.NET late binding), the you still want to use Activator.CreateInstance() with Program ID "AutoCAD.Application" (or CreateObject("AutoCAD.Application") in VB.NET). Just make sure you do not include the version-specific suffix in the program ID for multiple AutoCAD targeting purpose.

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 7 of 8

FRFR1426
Collaborator
Collaborator

You're right for using new when you're using early binding. If you reference a 2018 Interop, it will start the 2018 version of AutoCAD, like if you were using the versioned ProgID: "Application.AutoCAD.22" with Activator.CreateInstance(). The code is cleaner and there can be no mismatch between the referenced version and the ProgID.

 

But if you use the unversioned ProgID : "AutoCAD.Application", it will start the latest version that has been launched and if it does not match the referenced version, it will throw a System.InvalidCastException. There is no backward compatibility with early binding. I've only put the version in the ProgID to make it clear, but removing it does not make my sample work. Try it and let me know.

Maxence DELANNOY
Manager
Add-ins development for Autodesk software products
http://wiip.fr
0 Likes
Message 8 of 8

Eric.Mathews120
Contributor
Contributor

This is my experience. When I try referencing the 2016 dll, it only works with 2016. Even if I specify the program id with out the version number. The invalid cast exception is thrown on every other version.

 

It sounds like AutoCAD's API is still in flux (which is amazing considering its age) because I'm referencing Inventor 2019's dll and it works back to 2016. The point about a newer dll not being fully functional for older versions of the software is valid, however; some objects like the AcadDocument object should be well established by now. Inventor's AssemblyDocument can be set in any version back to 2016 ( and probably further). 

 

If I had to wager a conclusion here, it would come down to two options:

  1. There are two different development teams at Autodesk and the Inventor team is clearly superior. 
  2. Autodesk is pulling some shenanigans here to keep people like solid works at bay and making their lives as difficult as possible. 
0 Likes