.NET plugin not loading using NETLOAD

.NET plugin not loading using NETLOAD

nshupeFMPE3
Advocate Advocate
763 Views
19 Replies
Message 1 of 20

.NET plugin not loading using NETLOAD

nshupeFMPE3
Advocate
Advocate

I have a user that is currently unable to use my plugin. I have had similar problems before related to AutoLoading. But in this case even NetLoad does not load the plugin. This is from a fresh install of AutoCAD 2026 on Windows 11. 

The bundle is located at
C:\Users\username\AppData\Roaming\Autodesk\ApplicationPlugins\MyPlugin.bundle

I tried deleting my plugin's .bundle folder, recreating the .bundle folder. The bundle has a DLL folder which has three .dlls. One is the main plugin .dll while the other two are dependencies. I tried NETLOAD'ing the two dependencies before loading the main plugin, no luck. 

So I decided to step back and just try to have the user NETLOAD a .dll with this plugin after uninstalling/reinstalling AutoCAD 2026

using System.IO;
using Autodesk.AutoCAD.Runtime;

namespace AutoCAD_2025_Test_Plugin
{
    public class PluginExtension : IExtensionApplication
    {
        public void Initialize()
        {
            // Add your initialization code here

           
        }

        public void Terminate()
        {
            // Add your termination code here
        }
    }
}

/////////////////////seperate files

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(AutoCAD_2025_Test_Plugin.MyCommands))]
[assembly: ExtensionApplication(typeof(AutoCAD_2025_Test_Plugin.PluginExtension))]

namespace AutoCAD_2025_Test_Plugin
{
    public class MyCommands
    {
        // The CommandMethod attribute can be applied to any public  member 
        // function of any public class.
        // The function should take no arguments and return nothing.
        // If the method is an intance member then the enclosing class is 
        // intantiated for each document. If the member is a static member then
        // the enclosing class is NOT intantiated.
        //
        // NOTE: CommandMethod has overloads where you can provide helpid and
        // context menu.

        // Modal Command with localized name
        [CommandMethod("MyGroup", "MyCommand", "MyCommandLocal", CommandFlags.Modal)]
        public void MyCommand() // This method can have any name
        {
            // Put your command code here
            Document doc = AcadApp.DocumentManager.MdiActiveDocument;
            Autodesk.AutoCAD.EditorInput.Editor ed;
            if (doc != null)
            {
                ed = doc.Editor;
                ed.WriteMessage("Hello, this is your first command.");

            }
        }

        // Modal Command with pickfirst selection
        [CommandMethod("MyGroup", "MyPickFirst", "MyPickFirstLocal", CommandFlags.Modal | CommandFlags.UsePickSet)]
        public void MyPickFirst() // This method can have any name
        {
            PromptSelectionResult result = AcadApp.DocumentManager.MdiActiveDocument.Editor.GetSelection();
            if (result.Status == PromptStatus.OK)
            {
                // There are selected entities
                // Put your command using pickfirst set code here
            }
            else
            {
                // There are no selected entities
                // Put your command code here
            }
        }

        // Application Session Command with localized name
        [CommandMethod("MyGroup", "MySessionCmd", "MySessionCmdLocal", CommandFlags.Modal | CommandFlags.Session)]
        public void MySessionCmd() // This method can have any name
        {
            // Put your command code here
        }

        // LispFunction is similar to CommandMethod but it creates a lisp 
        // callable function. Many return types are supported not just string
        // or integer.
        [LispFunction("MyLispFunction", "MyLispFunctionLocal")]
        public int MyLispFunction(ResultBuffer args) // This method can have any name
        {
            // Put your command code here

            // Return a value to the AutoCAD Lisp Interpreter
            return 1;
        }
    }
}


This should be as simple a "plugin" as possible. No other dependencies besides the AutoCAD dependencies and the system. I am able to NETLOAD the .dll for this plugin and run "MyCommand". 

When I have the user try to NetLoad the .dll that is in their downloads folder they do not get any error that it did not load. But if they run "MyCommand" they get a command not found error.

Any idea's that I should look into for how to solve this? Thank you in advance

0 Likes
Accepted solutions (1)
764 Views
19 Replies
Replies (19)
Message 2 of 20

ActivistInvestor
Mentor
Mentor

Perhaps it is a trusted folder issue? You can confirm that by having the user set the SECURELOAD sysvar to 0 and then trying to NETLOAD the dll. 

0 Likes
Message 3 of 20

nshupeFMPE3
Advocate
Advocate

Thank you for the suggestion. I had the user check this and they said SECURELOAD was set to 1. They set it to 0 and then tried to NETLOAD the "test plugin" .dll again but had the same result. 

0 Likes
Message 4 of 20

_gile
Consultant
Consultant

@nshupeFMPE3  a écrit :

When I have the user try to NetLoad the .dll that is in their downloads folder they do not get any error that it did not load. But if they run "MyCommand" they get a command not found error.


Did you "unblock" the DLL?



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 20

nshupeFMPE3
Advocate
Advocate

I just tried that on their computer with no luck. Since I had access to their computer I also tried adding 

C:\Users\username\AppData\Roaming\Autodesk\ApplicationPlugins

to their AutoCAD's trusted locations and moved the .dll there then tried NETLOADing. Still no luck. I also verified that SECURELOAD was set to 0

As I said in the original post they started from a fresh install of AutoCAD 2026. They also uninstalled and reinstalled Friday.

0 Likes
Message 6 of 20

ricaun
Advisor
Advisor

Why you need to use NETLOAD if you are using AppBundle.

 

How your PackageContents.xml looks like?

 

This is what I'm using in my template and works fine for AutoCAD Addins: https://github.com/ricaun-io/ricaun.Revit.Templates

<?xml version="1.0" encoding="utf-8"?>
<ApplicationPackage SchemaVersion="1.0" AutodeskProduct="AutoCAD" Name="AutoCADAddin" AppVersion="0.0.0" ProductType="Application" >
  <CompanyDetails Name="ricaun" />
  <Components Description="AutoCAD 2019-2020">
    <RuntimeRequirements OS="Win64" Platform="AutoCAD*" SeriesMin="R23.0" SeriesMax="R23.9"/>
    <ComponentEntry AppName="AutoCADAddin" ModuleName="./2019/AutoCADAddin.dll" />
  </Components>
  <Components Description="AutoCAD 2021-2024">
    <RuntimeRequirements OS="Win64" Platform="AutoCAD*" SeriesMin="R24.0" SeriesMax="R24.9"/>
    <ComponentEntry AppName="AutoCADAddin" ModuleName="./2021/AutoCADAddin.dll" />
  </Components>
  <Components Description="AutoCAD 2025+">
    <RuntimeRequirements OS="Win64" Platform="AutoCAD*" SeriesMin="R25.0" />
    <ComponentEntry AppName="AutoCADAddin" ModuleName="./2025/AutoCADAddin.dll" />
  </Components>
</ApplicationPackage>

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 7 of 20

_gile
Consultant
Consultant

@ricaun  a écrit :

Why you need to use NETLOAD if you are using AppBundle.


Did you read the original post?



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 8 of 20

_gile
Consultant
Consultant

@nshupeFMPE3 

Try with the following code in the PluginExtension class and check the text screen (F2) content after you try to netload the plugin

public void Initialize()
{
    Application.Idle += OnIdle;
}

private void OnIdle(object? sender, EventArgs e)
{
    var doc = Application.DocumentManager.MdiActiveDocument;
    if (doc != null)
    {
        Application.Idle -= OnIdle;
        try
        {
            doc.Editor.WriteMessage("\nAutoCAD_2025_Test_Plugin loaded.\n");
        }
        catch (System.Exception ex)
        {
            doc.Editor.WriteMessage($"\nError: {ex.Message}\n");
        }
    }
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 9 of 20

gleeuwdrent
Advocate
Advocate

I have had similar issue before, in my case it was due to wrong or conflicting version(s) of third party libraries which were used in my code. 

0 Likes
Message 10 of 20

nshupeFMPE3
Advocate
Advocate

Finally we have some progress. I added the Application.Idle code as suggested and the test plugin did load. It printed to the command line during load and then MYCOMMAND worked. 

I just connected to the users computer and tried to load my plugin but had no luck. I think I'm going to make a branch of my plugin and remove different things from the plugin startup process just to try and narrow down what could be the problem. 

0 Likes
Message 11 of 20

ricaun
Advisor
Advisor

What dependencies are you using? 

Could have a plugin in your user machine that uses the same dependency and a conflict could explain why is not loading your addin.

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 12 of 20

nshupeFMPE3
Advocate
Advocate

Ok I think I know what is causing the problems. Its OPMNet. 

Its a dependency that exposes the ability to add custom Properties to .NET. We use this for multiple pieces of data specific to our workflow. 

The difficulty is I'm not as familiar with C++, I was very fortunate that  a project already existed which I could fork. 

Thanks to this version I was able to get it working in .NET 8. The .dll for OPMNet is in the same folder as the main .dll for my plugin. 

Its going to be tough to figure out how to fix this for me as the problem is on another users computer, and I cant seem to recreate it on the three computers I have available to me. 

I checked in the users ApplicationPlugins folder and it doesn't appear that the only other plugin they seem to have uses anything like OPMNet. 

What is the best practice for dependencies for your plugin? My current plugin has a .dll for "itself", a library class that I wrote and create the .dll for, and the .dll for OPMNet which I also created myself from the fork of the original project. All of these .dll's are in the same folder inside of the .bundle folder of my project. And I've heard that on loading my main plugins .dll it the run time should be able to find the depencecies in the same folder? But I think I'm wrong about how that works. 

Should I be using something like Assembly.Load() in my initialization for my plugin to load the other dependencies it needs? Or is this something that should be handled in the PackageContents of the .bundle?

0 Likes
Message 13 of 20

ActivistInvestor
Mentor
Mentor

Did you forget about what was discussed in this thread?

0 Likes
Message 14 of 20

nshupeFMPE3
Advocate
Advocate

I had been experimenting with the notes from that thread and even on my computer where things were working they were not working out after adding AssemblyMappings etc. And apparently the ComponentEntries are parsed and loaded from the bottom up? So when I added the asdkOPMNet to PackageContents at the bottom of the list it worked on my computer. Jury is still out if this will work on the other users computer. 

<?xml version="1.0" encoding="utf-8"?>

<ApplicationPackage 
    SchemaVersion="1.0"
    ProductType="Application"
    Name="Company Core"
    AppVersion="40.0.00"
    Description="Company Tools"
    Author="Me"
	Icon="renamesubs16.bmp"
	ProductCode="GUID">

  <CompanyDetails 
	  Name="Company, Inc."
	  Phone=""
	  Url="http://www.Company.com/"
	  Email="" />

	<Components Description="AutoCAD 2025">

		<RuntimeRequirements SupportPath="./" Platform="AutoCAD*" SeriesMin="R25.0" ToolPalettePath="./Palettes/" />

		

		<ComponentEntry
			AppName="Company CUI" 
			ModuleName="./CUI/Company.cuix"
			AppType="CuiX">			
			<RuntimeRequirements OS="Win64"
			Platform="AutoCAD"
			SeriesMin="R25.0"/>			
		</ComponentEntry>

		</ComponentEntry>

		<ComponentEntry
			AppName="CompanyCore"
			ModuleName="./DLL/CompanyCore.dll"
			LoadOnAutoCADStartup="True"
			AppType=".Net">
			<RuntimeRequirements OS="Win64"
			                     Platform="AutoCAD"
			                     SeriesMin="R25.0"/>
								 <AssemblyMappings>
									<AssemblyMappingFolder Path="./DLL"/>
								</AssemblyMappings>
		</ComponentEntry>

		<ComponentEntry
			AppName="asdkOPMNet"
			ModuleName="./DLL/asdkOPMNetExt8.0.dll"
			LoadOnAutoCADStartup="True"
			AppType=".Net">
			<RuntimeRequirements OS="Win64"
			                     Platform="AutoCAD"
			                     SeriesMin="R25.0"/>
								 <AssemblyMappings>
									<AssemblyMappingFolder Path="./DLL"/>
								</AssemblyMappings>
		</ComponentEntry>


	</Components>

</ApplicationPackage>
0 Likes
Message 15 of 20

ActivistInvestor
Mentor
Mentor

AssemblyMappings tells the runtime where to look for dependent assemblies.

 

asdkOPMNet is not a component, it is an assembly that a component depends on.

 

There shouldn't be a ComponentEntry for it at all, if you are using AssemblyMappings

0 Likes
Message 16 of 20

nshupeFMPE3
Advocate
Advocate

Sorry the C++ gets a little confusing for me, but this is part of OPMNetExt 

#include "StdAfx.h"
#include "resource.h"

#define szRDS _RXST("asdk")

//----- ObjectARX EntryPoint
class COPMNetExtApp : public AcRxArxApp {

public:
	COPMNetExtApp() : AcRxArxApp() {}

	virtual AcRx::AppRetCode On_kInitAppMsg(void *pkt)
  {
		// You *must* call On_kInitAppMsg here

		AcRx::AppRetCode retCode = AcRxArxApp::On_kInitAppMsg(pkt);
		
		return (retCode);
	}

	virtual AcRx::AppRetCode On_kUnloadAppMsg(void *pkt)
  {
		// You *must* call On_kUnloadAppMsg here

    AcRx::AppRetCode retCode = AcRxArxApp::On_kUnloadAppMsg(pkt);

		return retCode;
	}

	virtual void RegisterServerComponents()
  {}
};

IMPLEMENT_ARX_ENTRYPOINT(COPMNetExtApp)

Does this mean that it is a plugin that needs to be loaded that then is used by other plugins? 

Also I used the above PackageContents just this morning while testing and now its not loading my plugin? I was able to NETLOAD OPMNet and then my Plugins .dll

In the Example Plugin for how to use OPMNetExt this is used in the Initialization method for that example plugin

Assembly.LoadFrom(@"C:\Users\username\source\repos\OPMNetExt\x64\Debug\asdkOPMNetExt.dll");
0 Likes
Message 17 of 20

ActivistInvestor
Mentor
Mentor

The example is wrong. The managed runtime takes care of loading dependent assemblies for you, and you do not need to do that manually. This extension DLL is just another managed assembly that your code references and uses, and it shouldn't be treated any differently than any other managed assembly that your code is dependent on. 

0 Likes
Message 18 of 20

nshupeFMPE3
Advocate
Advocate

Ok in order to narrow down possible variables but for me to properly understand what is going on this is what I've done. 

In the simple plugin from the Original Post (plus the Application.Idle changes recommended by _gile) I added OPMNetExt as a dependency and created a simple property

public class PluginExtension : IExtensionApplication
{
    public void Initialize()
    {
        // Add your initialization code here

        Autodesk.AutoCAD.ApplicationServices.Application.Idle += OnIdle;
    }

    private void OnIdle(object? sender, EventArgs e)
    {
        var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager?.MdiActiveDocument;

        if (doc != null)
        {
            Autodesk.AutoCAD.ApplicationServices.Application.Idle -= OnIdle;

            try
            {
                LoadProperties();
                doc.Editor.WriteMessage($"\nAutoCAD_2025_Test_Plugin loaded.\n");
            }
            catch(Exception ex)
            {
                doc.Editor.WriteMessage($"\nError: {ex.Message}");
            }
        }
    }

    public void Terminate()
    {
        // Add your termination code here
        UnloadProperties();
    }

    private ZoneNumberProperty zoneNumProp = null;

    private void LoadProperties()
    {
        //https://github.com/WB-nshupe/OPMNetExt
        // Add the Dynamic Property
        Dictionary classDict = SystemObjects.ClassDictionary;
        RXClass lineDesc = (RXClass)classDict.At("AcDbPolyline");
        IPropertyManager2 pPropMan = (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);

        zoneNumProp = new();
        pPropMan.AddProperty((object)zoneNumProp);

    }

    private void UnloadProperties()
    {
        // Remove the Dynamic Property
        Dictionary classDict = SystemObjects.ClassDictionary;
        RXClass lineDesc = (RXClass)classDict.At("AcDbPolyline");
        IPropertyManager2 pPropMan = (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);

        pPropMan.RemoveProperty((object)zoneNumProp);
        zoneNumProp = null;
    }
}

#######################

#region COMStuff

[
    
    Guid("33C21CD0-D6F3-41D7-AA40-755B1ADDE261"),
    ProgId("TestTools.ZoneProperty.3"),

    // No class interface is generated for this class and
    // no interface is marked as the default.
    // Users are expected to expose functionality through
    // interfaces that will be explicitly exposed by the object
    // This means the object can only expose interfaces we define

    ClassInterface(ClassInterfaceType.None),
    // Set the default COM interface that will be used for
    // Automation. Languages like: C#, C++ and VB allow to 
    //query for interface's we're interested in but Automation 
    // only aware languages like javascript do not allow to 
    // query interface(s) and create only the default one

    ComDefaultInterface(typeof(IDynamicProperty2)),
    ComVisible(true)
]

#endregion

public class ZoneNumberProperty : IDynamicProperty2, ICategorizeProperties
{
    private IDynamicPropertyNotify2 m_pSink = null;

    public void GetGUID([UnscopedRef] out Guid propGUID)
    {
        propGUID = new Guid("33C21CD0-D6F3-41D7-AA40-755B1ADDE261");//needs to be the same as in the COMStuff
    }

    public void GetDisplayName(ref string name)
    {
        name = "Zone Number";
    }

    public void IsPropertyEnabled(object pUnk, [UnscopedRef] out int bEnabled)
    {
        bEnabled = 0;

        if (pUnk is null) return;

        bEnabled = 1;
    }

    public void IsPropertyReadOnly([UnscopedRef] out int bReadonly)
    {
        bReadonly = 0;
    }

    public void GetDescription(ref string description)
    {
        description = "This is the zone number for Testing";
    }

    public void GetCurrentValueName(ref string name)
    {
        name = string.Empty;
    }

    public void GetCurrentValueType([UnscopedRef] out ushort pVarType)
    {
        // The Property Inspector supports the following data
        // types for dynamic properties:
        // VT_I2, VT_I4, VT_R4, VT_R8,VT_BSTR, VT_BOOL
        // and VT_USERDEFINED. 
        /*
         * VT_I2 => 2
         * VT_I4 => 3
         * VT_R4 => 4
         * VT_R8 => 5
         * VT_BSTR => 8
         * VT_BOOL => 11
         * VT_USERDEFINED => 29
         */

        pVarType = 8;
    }

    public void GetCurrentValueData(object pUnk, ref object varData)
    {
        string zoneNum = "1";

        
        varData = (string)zoneNum;
    }

    public void SetCurrentValueData(object pUnk, object varData)
    {
        // Because we said the value type was a 32b int (VT_I4)
        string myVal = (string)varData;

    }

    public void Connect(object pSink)
    {
        m_pSink = (IDynamicPropertyNotify2)pSink;
    }

    public void Disconnect()
    {
        m_pSink = null;
    }

    public void MapPropertyToCategory(int dispid, ref int ppropcat)
    {
        ppropcat = 1;
    }

    public void GetCategoryName(int propcat, uint lcid, ref string pbstrName)
    {
        if (propcat != 1) pbstrName = string.Empty;
        pbstrName = "Zone Properties";
    }
}


And this is my file structure for this "plugin" test

nshupeFMPE3_0-1754514859299.png


This is not a .bundle and does not have PackageContents

If I open AutoCAD 2026 and run NETLOAD and select "AutoCAD 2025 Test Plugin.dll" it does not load and gives no error in the command line. 
If I first run NETLOAD with "asdkOPMNetExt8.0.dll" then NETLOAD "AutoCAD 2025 Test Plugin.dll" everything loads correctly. 




0 Likes
Message 19 of 20

ricaun
Advisor
Advisor
Accepted solution

Did you rename the file to asdkOPMNetExt8.0? With the 8.0 in the end?

 

Usually the resolver tries to find the dll using the assembly name, if you change the name of the file your AutoCAD 2025 Test Plugin.dll is not gonna find the assembly asdkOPMNetExt.

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 20 of 20

Izhar_Azati
Advocate
Advocate

I have encountered several times that running NETLOAD fails and I don't get any error either.
If a CommandMethod with the same name is defined in the project - AutoCAD does not load the DLL.

Even if the DLL is used in a recent version and a DLL exists in one of the ACAD.EXE libraries, the operation fails without a message.

0 Likes