The plug-in loader has been created specifically for .NET plug-ins which makes it safer for 3ds Max to load and unload .NET and mixed-mode 3ds Max plug-ins. Assemblies (DLLs) which contain .NET or C++/CLI plug-ins should now be placed in the bin/assemblies subfolder of the 3ds Max installation, instead of the plugins folder. Note: Placing managed mixed-mode assemblies in stdplugs and not in bin/assemblies, is risky and can crash the application.
Starting with release 2013, the plugin loader can now load managed maxscript plugin extensions (*.dlx). This is used, for instance, with the maxsdk sample project mxsdotnet.dlx which contains managed code. These Maxscript assemblies require not one but two entry methods. The extra method it needs to implement is: public static void AssemblyLoad(). This method is called first by the managed loader, even before AssemblyMain() described below.
The assembly loader loads plug-ins from the bin/assemblies folder in the 3ds Max program folder. Each plug-in is given a chance to execute some initialization code. The assembly loading process happens in four phases (Or five for maxscript plugins):
This second form is passed the current executing loader object. This form is useful if an assembly needs to examine types from other loaded classes during the next loading phase. For managed maxscript assemblies, this functions should execute normal maxscript initialization methods like any other native maxscript plugin.
When the application shuts down, the loader runs over all loaded assemblies and calls a shutdown method. For the Loader to recognize the method, the method must exist in a public type, and it must match one of the following two signatures:
These shutdown methods can be used to release any native resources and to delete any remaining native objects. Each hook indicated above is entirely optional. The plug-in designer may choose to use none, any, or all of these hooks: the AssemblyMain method, TypeLoaded event handlers, the AssemblyInitializationCleanup method, or the AssemblyShutdown method.
It is important that .NET assemblies avoid native global objects: objects allocated during DLL load, and released only during DLL shutdown. This includes every type other than basic primitives or arrays of primitives. Any actual object (class or struct) would have a constructor or destructor, even if it is generated by the compiler, and executing the constructor at load time or the destructor during application termination may cause the execution of managed code outside the CLR's lifetime. This can cause the application to crash, or to enter a deadlock. Instead of global objects, plug-ins should use dynamically allocated objects explicitly constructed during AssemblyMain(), and explicitly destructed during AssemblyShutdown().
For those who want to convert a native Global Utility Plugin (*.gup) to a managed assembly, please note: The managed plugin loader is agnostic about what code is executed in it's AssemblyMain() methods. A GUP plugin type that inherits from class GUP, and compiled into a managed mixed-mode assembly will not get initlialized. In fact the managed loader will have no idea it even exists and will simply ignore it. Therefore it makes no sense to compile classes that inherit from class GUP into a managed assembly. The plugin loader also is agnostic about any other type of plugin class that inherit from standard plugin classes in the maxsdk. Meaning it will ignore those as well.