Lesson 7: Writing .Net Plug-ins

3ds Max uses its integrated .NET assembly loaders at run time to load any valid .Net assembly located in [3ds Max installation folder]\bin\assemblies that implements a 3ds Max plug-in. There are different DLL libraries providing the necessary APIs for your .NET plug-ins to use the 3ds Max SDK. The important libraries for this purpose are :

To write .Net plug-ins, you create a new C# class library project in Visual Studio. Once the new project is created, you can select Add Reference from the Project menu and browse for UiViewModel.dll, and / or Autodesk.Max.dll in the 3ds Max installation folder, depending on what functionality you want to implement.

Writing .Net Action plug-ins using UiViewModel.dll

To create .NET Action plug-ins using UiViewModel.dll, your plug-in class must implement CuiActionCommandAdapter. You can automatically implement an abstract class in Visual Studio (if you are using C#) by right clicking on the class name and selecting "Implement Abstract Class". Doing so for CuiActionCommandAdapter will create stubs for four properties and one function for you. The properties provide basic information about your CUI action plug-in. The function is named Execute() and implements the actual functionality of your plug-in.

Any plug-in class extended from CuiActionCommandAdapter can be independently compiled and loaded in 3ds Max. We will see later that it is not the case for plug-ins implementing the interfaces from Autodesk.Max.dll. The code samples on this page provide an example of a simple .Net Action plug-in that prompts a message in a pop up window.

Writing .Net plug-ins using Autodesk.max.dll

Plug-in developers can use the interfaces defined in Autodesk.max.dll to implement any type of plug-in they need. This is similar to extending from a 3ds Max base class for a C++ plug-in. There is almost a one to one mapping between 3ds Max C++ SDK classes and the interfaces available in Autodesk.Max.dll. We will create a utility plug-in as a basic example. At the beginning, your code will look like this:

using System;
using Autodesk.Max;

namespace firstDotNet
{
    public class utilityDotNet : Autodesk.Max.Plugins.UtilityObj
    {        
    }
}

Using Visual Studio's "Implement Abstract Class" option for a class extending Autodesk.Max.Plugins.UtilityObj will create two function stubs for you to implement, BeginEditParams() and EndEditParams(). These two functions are explained in lesson 1 of the 3ds Max SDK learning path. Similar to the example there, we get this utility plug-in to send some text to the prompt area of 3ds Max. The following code shows what your code looks like after this step:

using System;
using Autodesk.Max;

namespace firstDotNet
{
    public class utilityDotNet : Autodesk.Max.Plugins.UtilityObj
    {
        public override void BeginEditParams(IInterface ip, IIUtil iu)
        {
            ip.PushPrompt("Hello Dot Net!");
        }
        public override void EndEditParams(IInterface ip, IIUtil iu)
        {
            ip.PopPrompt();
        }
    }
}

.Net Plug-ins also need class descriptors, similar to unmanaged (C++) plug-ins. Class descriptors are explained in Getting Started Writing Plug-ins. You can define your own class descriptor that extends from Autodesk.Max.Plugins.ClassDesc2. The descriptor class returns an object of your plug-in class in its Create() function. You can right click on the class descriptor declaration name and select Implement Abstract Class to create stubs for the functions you have to implement.

You do not need to explicitly define a constructor for your plug-in class since your call in Descriptor::Create() does not take any arguments. Your plug-in project can be compiled at this point, but 3ds Max still cannot recognize it even after placing the .dll file in [3ds max installation folder]\bin\assemblies and restarting 3ds Max. As explained in .NET Assembly Loader, you will need to register your plug-in with 3ds Max by exposing a public static function named AssemblyMain(). You can implement this function in any public class in your plug-in's namespace, including the main plug-in class and the class descriptor. We create a new public class to implement this and other assembly functions for more clarity. Similar to AssemblyMain(), the public static function AssemblyShutdown() is used to perform the required tasks (if any) when your plug-in is being terminated. Adding the following public class to the namespace will make your plug-in be loaded by the .NET Assembly Loader when 3ds Max is starting up:

public static class AssemblyFunctions
    {
        public static void AssemblyMain()
        {
            var g = Autodesk.Max.GlobalInterface.Instance;
            var i = g.COREInterface13;
            i.AddClass(new Descriptor(g));
        }

        public static void AssemblyShutdown()
        {
        }
    }

The .NET project Lesson7.zip provides different examples of using the Autodesk.Max.dll assembly along with CUI action items to create .NET plug-ins.

Interfaces instead of Classes, Properties instead of Functions

We can see from the above code that 3ds Max includes a handle to its global interface in its call to the BeginEditParams(). We use that interface handle to call the PushPrompt() function. We can also obtain the 3ds Max global interface by defining our own IGlobal Interface as follows:

IGlobal global = Autodesk.Max.GlobalInterface.Instance;
Interface13 Interface = global.COREInterface13;
global.PushPrompt("...");
The equivalent code in 3ds Max C++ SDK will be:
Interface* ip = GetCOREInterface();
ip->PushPrompt("...");

By comparing the two pieces of code above, we can see two differences that can be generalized as two rules of thumb:

  • COREInterface13 is a 'property' of the Global interface, while its equivalent in 3ds Max SDK, GetCOREInterface() is a global 'function'. In general, all the accessor functions in the 3ds Max SDK with names that start with 'Get' and have no argument have equivalent properties in Autodesk.Max.dll.
  • The type of the parameter ip in the .Net version is an 'interface' named IInterface, as opposed to the equivalent parameter in the 3ds Max C++ SDK where the type is a pointer to a 'class' named Interface. In Autodesk.Max.dll almost every class has a corresponding interface which is used instead where the class might be expected as an argument type or return type. Remember that "if a method requires an interface as an argument, then any object that implements that interface can be used in the argument".