Accessing COM components using the AcadApplication.GetInterfaceObject method

Accessing COM components using the AcadApplication.GetInterfaceObject method

aknigin
Enthusiast Enthusiast
1,435 Views
12 Replies
Message 1 of 13

Accessing COM components using the AcadApplication.GetInterfaceObject method

aknigin
Enthusiast
Enthusiast

We develop our .NET modules for data transfer into AutoCAD. To access these modules from our software we used the AcadApplication.GetInterfaceObject method. It looks like starting from Civil 3D 2025 which uses .NET Core 8 this approach does not work anymore: calling this method throws the InvalidCastException. Below is the code example (works fine if COM module is created uning .NET Framework):

try
{
AcadApplication acad = (AcadApplication)Marshal2.GetActiveObject("AutoCAD.Application.25");
object? obj = acad.GetInterfaceObject("MyCompany.MyNetCoreApplication");
Type? object_type = obj?.GetType();
MessageBox.Show(object_type?.ToString());
}
catch (System.Exception ex)
{
MessageBox.Show(ex.ToString());
}

0 Likes
1,436 Views
12 Replies
Replies (12)
Message 2 of 13

norman.yuan
Mentor
Mentor

You did not say, but I assume the component "MyCompany.MyNetCoreApplication" is developed with .NET framework, exposed as COM (i.e. both the component DLL and the interop DLL are .NET framework DLLs). It looks like the code you showed runs from an external app that interact/automate AutoCAD (with AutoCAD has the component loaded prior to the external app contacting AutoCAD).

 

Firstly, a .NET Framework AutoCAD plugin DLL may or may not work with AutoCAD 2025, depending on what the code inside does. So, have you tried to load/run the component/plugin in AutoCAD 2025 to confirm it indeed works? Obviously, you can only run it, not debug it, because the code project is .NET framework based. It only make sense to ask this question when you confirm the component works inside AutoCAD 2025.

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 13

aknigin
Enthusiast
Enthusiast

Hi, Norman.

The component "MyCompany.MyNetCoreApplication" is developed with .NET Core actually, and calling the GetInterfaceObject method in this case results in InvalidCastException. If I try to access the same way a component created with .NET Framework - the object is created.

0 Likes
Message 4 of 13

norman.yuan
Mentor
Mentor

OK, have you tried to run the component and its COM interop inside AutoCAD 2025?

 

I am sure you have run .NET 8 component either as a plugin or part of other plugin in AutoCAD 2025 already. But have you tried to use the component's COM exposed interop in another plugin, or in AutoCAD 2025 VBA to verify the COM interop works inside AutoCAD?

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 13

aknigin
Enthusiast
Enthusiast

No, I didn't. I think I will try that, just to check that approach, but, anyway our task is to transfer data into AutoCAD/Civil 3D from our software which is C++ written and uses COM for AutoCAD application access.

0 Likes
Message 6 of 13

norman.yuan
Mentor
Mentor

You did not say how do you expose the said .NET 8 component to COM. It is not the same way as one does with .NET Framework component, which is as easy as check the "Register for COM interop" checkbox in VS's build option. With .NET Core, it is quite different and complicated. Have you done it correctly? I'd consider that in most cases it is technically not worth doing it, especially if the effort is to get external app/process to gain access to/control a running desktop AutoCAD session.

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 7 of 13

aknigin
Enthusiast
Enthusiast

Well, for us it does worth, because we use this technique for transferring data into AutoCAD/Civil 3D drawing since 2012 I believe... And switching to a different approach would mean to us to develop the transfer module from the scratch and to use an external temporary file for that, for example...

Anyway, that's what I did to expose my interfaces to COM:

I have created a simple interface/class pair like this:

 

using System.Runtime.InteropServices;

namespace AutoCADApplicationNETCore
{
    [ComVisible(true)]
    [Guid("76920C6F-93FD-45B9-A7E6-91036B467E7C")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IMyCoreApplication
    {
        void DoSomething();
    }

    [ComVisible(true)]
    [Guid("E1CA5B63-D9DA-4AA4-95AF-F016A7E35809")]
    [ProgId("MyCompany.MyNetCoreApplication")]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    public class MyCoreApplication : IMyCoreApplication
    {
        public void DoSomething()
        {
        }
    }
}

Then in my .csproj project file I set the following value to true:

<EnableComHosting>true</EnableComHosting>

 

That creates an intermediate .dll file AutoCADApplicationNETCore.comhost.dll; I register it using regsvr32, and here we are: my components are registered and I can create such objects, like that, for example:

                Type? t = Type.GetTypeFromProgID("MyCompany.MyNetCoreApplication");
                dynamic? obj = t == null ? null : Activator.CreateInstance(t);
                if (obj != null)
                {
                    obj.DoSomething();
                }

It works, and the DoSomething() method is called.

 

 

0 Likes
Message 8 of 13

sm2GVFR
Participant
Participant

Hello @aknigin,

 

I have the exact same issue, AcadAppObject.GetInterfaceObject("ProgID_OF_ADDIN_LOADED_IN_ACAD") is causing exception with .NET Core dll. The same code was working with .NET 4.8 with regasm.

 

Could you figure out a solution to the issue? 

This is what I have tried so far :

1) Create the .NET Core 8.0 dll sample

sm2GVFR_0-1730192490087.png

 

2) regsrv the dll

3) Start ACAD.exe and NETLOAD the custom dll

3) From a seperate console APP, try to get the interface

sm2GVFR_1-1730192663695.png

 

 

0 Likes
Message 9 of 13

aknigin
Enthusiast
Enthusiast

@sm2GVFR ,

What actually did help:

1) In your C# code, declare the IDispatch interface explicitely:

 

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
    public interface IDispatch
    {
        [PreserveSig]
        int GetTypeInfoCount(out int Count);

        [PreserveSig]
        int GetTypeInfo
        (
          [MarshalAs(UnmanagedType.U4)] int iTInfo,
          [MarshalAs(UnmanagedType.U4)] int lcid,
          out System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo
        );

        [PreserveSig]
        int GetIDsOfNames
        (
          ref Guid riid,
          [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
                string[] rgsNames,
          int cNames,
          int lcid,
          [MarshalAs(UnmanagedType.LPArray)] int[] rgDispId
        );

        [PreserveSig]
        int Invoke
        (
          int dispIdMember,
          ref Guid riid,
          uint lcid,
          ushort wFlags,
          ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
          out object pVarResult,
          ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo,
          out UInt32 pArgErr
        );
    }

 

2) Derive your class, which you try to get from outside application, from IDispatch:

 

    [ComVisible(true)]
    [Guid("08BBAD0D-51AD-450D-9CF9-50B26F656CBD")]
    [ProgId("MY_PROG_ID")]
    public class MyClass : IDispatch, IExtensionApplication

 

3) You don't need to implement IDispatch methods actually:

 

int IDispatch.GetTypeInfoCount(out int Count)
{
    throw new NotImplementedException(); // never called actually, added just for compatibility of .NET Core assemblies with IDispatch calls from native code
}

int IDispatch.GetTypeInfo(int iTInfo, int lcid, out ITypeInfo typeInfo)
{
    throw new NotImplementedException(); // never called actually, added just for compatibility of .NET Core assemblies with IDispatch calls from native code
}

int IDispatch.GetIDsOfNames(ref Guid riid, string[] rgsNames, int cNames, int lcid, int[] rgDispId)
{
    throw new NotImplementedException(); // never called actually, added just for compatibility of .NET Core assemblies with IDispatch calls from native code
}

int IDispatch.Invoke(int dispIdMember, ref Guid riid, uint lcid, ushort wFlags, ref DISPPARAMS pDispParams, out object pVarResult, ref EXCEPINFO pExcepInfo, out uint pArgErr)
{
    throw new NotImplementedException(); // never called actually, added just for compatibility of .NET Core assemblies with IDispatch calls from native code
}

 

4) That's it. I don't have 100% explanation why it works, I just found (during debugging) that on some stage your class is checked on the IDispatch interface presense in base interfaces, and if this interface is not found, the InvalidCastException or something is thrown. Hope this helps.

 

 

Message 10 of 13

sm2GVFR
Participant
Participant

@aknigin 

Thank you for your valuable comment on the topic. It provided helpful direction for further investigation, and I finally managed to resolve the issue after struggling for over a month.

 

Below is my understanding of the problem, which I hope will help others facing a similar challenge:

 

What worked for me was using the GUID of IID_IDispatch as the GUID for the implemented interface. From what I’ve learned, in .NET Framework (up to version 4.8), COM interfaces should not derive directly from IDispatch. Instead, they should use the [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] attribute to ensure proper usage. Directly inheriting from IDispatch is not considered valid in this context.

However, in .NET Core, this direct inheritance doesn’t seem to work (though I’m not entirely sure why). To resolve the issue, I used the IID of IDispatch directly as the GUID of the implemented interface, and that worked successfully.

 

 

0 Likes
Message 11 of 13

sm2GVFR
Participant
Participant

[ComImport] // ComVisible(true) works as well

[Guid("00020400-0000-0000-C000-000000000046")] //IID_IDispatch

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] // InterfaceIsDual works as well

[TypeLibType(TypeLibTypeFlags.FDispatchable)]

public interface IInstance

{

    [MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]

    [DispId(1)]

    public int AddTwoNumbers(int n1, int n2);

}

0 Likes
Message 12 of 13

aknigin
Enthusiast
Enthusiast

@sm2GVFR,

Yes, inheriting a class from IDispatch makes things working, however these implemented IDispatch methods are never called. 

Also I would add a little bit of information for those who is going to move their plugins to AutoCAD 2025 and so to .NET Core: the System.EnterpriseServices.ServicedComponent does not exist in .NET Core. That means that you have to pass external calls of your methods to the main thread manually, if you need. And if you accessing the AutoCAD database - you definitely need to do that in the main thread, otherwise you would face unexpected crashes, at least from my experience.

Message 13 of 13

cadtown
Advocate
Advocate

Does any of your methods create the TLB file with DLL ?...

 

Up until Autocad 2024 versions, we used to be able to create TLB & DLL to connect/call net dll functions from COM executable using old 4.8 Framework by checking "Register for COM Interop" checkbox under the Compile section  in Visual Studio.  

 

But it looks like that option is no longer available after migrating from .NET 4.8 to 8.0 .NET.

Is there another way to call/connect a NET dll from a COM exe application with something like "acadApp.GetInterfaceObject("getNetFunctions.ComClass1")" ?..

0 Likes